Compare commits
4 Commits
main
...
charshift/
Author | SHA1 | Date |
---|---|---|
Gergely Nagy | 0cc060e4a4 | 3 years ago |
Gergely Nagy | ee36ed6929 | 3 years ago |
Gergely Nagy | 7d6ce8bf45 | 3 years ago |
Gergely Nagy | f916c2c0d4 | 3 years ago |
@ -1,47 +0,0 @@
|
||||
# -*- mode: yaml -*-
|
||||
---
|
||||
BasedOnStyle: Google
|
||||
---
|
||||
Language: Cpp
|
||||
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
## clang-format-15
|
||||
# AlignConsecutiveAssignments:
|
||||
# Enabled: true
|
||||
# AlignCompound: true
|
||||
# PadOperators: true
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignConsecutiveMacros: AcrossEmptyLines
|
||||
AlignEscapedNewlines: Right
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AttributeMacros:
|
||||
- __attribute__((weak))
|
||||
- __attribute__((always_inline))
|
||||
- __attribute__((noinline))
|
||||
- __attribute__((packed))
|
||||
- __attribute__((optimize(3)))
|
||||
- __attribute__((unused))
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
# BraceWrapping:
|
||||
# SplitEmptyFunction: false
|
||||
# SplitEmptyRecord: true
|
||||
# SplitEmptyNamespace: true
|
||||
# BreakBeforeBraces: Custom
|
||||
ColumnLimit: 0
|
||||
ConstructorInitializerIndentWidth: 2
|
||||
ContinuationIndentWidth: 2
|
||||
DerivePointerAlignment: false
|
||||
FixNamespaceComments: true
|
||||
IndentCaseLabels: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
# PackConstructorInitializers: CurrentLine
|
||||
PointerAlignment: Right
|
||||
# ReferenceAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SpaceAfterTemplateKeyword: false
|
@ -1,9 +0,0 @@
|
||||
set noparent
|
||||
|
||||
extensions=cpp,h,ino
|
||||
|
||||
filter=-build/include
|
||||
filter=-legal/copyright
|
||||
filter=-readability/namespace
|
||||
filter=-runtime/references
|
||||
filter=-whitespace
|
@ -1,9 +0,0 @@
|
||||
set noparent
|
||||
|
||||
extensions=cpp,h,ino
|
||||
|
||||
filter=-build/include
|
||||
filter=-legal/copyright
|
||||
filter=-readability/namespace
|
||||
filter=-runtime/references
|
||||
filter=-whitespace/line_length
|
@ -1,14 +0,0 @@
|
||||
examples
|
||||
src/Kaleidoscope.h
|
||||
src/Kaleidoscope-LEDControl.h
|
||||
src/kaleidoscope/HIDTables.h
|
||||
src/kaleidoscope/driver/bootloader/gd32/Base.h
|
||||
src/kaleidoscope/driver/led/Base.h
|
||||
src/kaleidoscope/driver/led/WS2812.h
|
||||
src/kaleidoscope/driver/led/ws2812/config.h
|
||||
src/kaleidoscope/driver/mcu/GD32.h
|
||||
src/kaleidoscope/driver/storage/GD32Flash.h
|
||||
plugins/Kaleidoscope-FirmwareDump/**
|
||||
plugins/Kaleidoscope-HostOS/src/kaleidoscope/plugin/HostOS.h
|
||||
plugins/Kaleidoscope-Hardware-EZ-ErgoDox/src/kaleidoscope/device/ez/ErgoDox/i2cmaster.h
|
||||
testing/googletest
|
@ -0,0 +1,3 @@
|
||||
# -*- mode: sh -*-
|
||||
|
||||
DEFAULT_SKETCH=Kaleidoscope
|
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
: "${KALEIDOSCOPE_DIR:=$(pwd)}"
|
||||
cd "${KALEIDOSCOPE_DIR}" || exit 1
|
||||
|
||||
ERROR_COUNT=0
|
||||
|
||||
while read -r SCRIPT; do
|
||||
shellcheck "${SCRIPT}"
|
||||
(( ERROR_COUNT += $? ))
|
||||
done < <(grep -E -n -r -l '(env (ba)?sh)|(/bin/(ba)?sh)' "${KALEIDOSCOPE_DIR}/bin")
|
||||
|
||||
exit $ERROR_COUNT
|
@ -1,110 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
|
||||
|
||||
# This is free and unencumbered software released into the public domain.
|
||||
|
||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
# distribute this software, either in source code form or as a compiled
|
||||
# binary, for any purpose, commercial or non-commercial, and by any
|
||||
# means.
|
||||
|
||||
# In jurisdictions that recognize copyright laws, the author or authors
|
||||
# of this software dedicate any and all copyright interest in the
|
||||
# software to the public domain. We make this dedication for the benefit
|
||||
# of the public at large and to the detriment of our heirs and
|
||||
# successors. We intend this dedication to be an overt act of
|
||||
# relinquishment in perpetuity of all present and future rights to this
|
||||
# software under copyright law.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# For more information, please refer to <http://unlicense.org/>
|
||||
# ------------------------------------------------------------------------------
|
||||
"""Utilities shared by Kaleidoscope code maintenance tools."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
@contextmanager
|
||||
def cwd(temp_wd):
|
||||
"""Execute code in a different working directory
|
||||
|
||||
Parameters:
|
||||
`temp_wd` (`str`): The name of a directory to (temporarily) change to
|
||||
|
||||
Change the current working directory, then automatically restore the previous working
|
||||
directory when done. Invoke `cwd()` like this:
|
||||
```py
|
||||
with cwd(temp_wd):
|
||||
...
|
||||
```
|
||||
"""
|
||||
old_wd = os.getcwd()
|
||||
os.chdir(temp_wd)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def split_on_newlines(string):
|
||||
"""Split the string using newlines as the separator."""
|
||||
return string.splitlines()
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def split_on_nulls(string):
|
||||
"""Split the input string using NULL characters as the separator."""
|
||||
targets = [_ for _ in string.split('\0') if _ != '']
|
||||
return targets or []
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def setup_logging(loglevel):
|
||||
"""Set up basic logging."""
|
||||
logformat = "%(message)s"
|
||||
logging.basicConfig(
|
||||
level=loglevel,
|
||||
stream=sys.stdout,
|
||||
format=logformat,
|
||||
datefmt="",
|
||||
)
|
||||
return logging.getLogger()
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def check_git_diff():
|
||||
"""Check for unstaged changes with `git diff`
|
||||
|
||||
Returns: a list of the names of files with unstaged changes
|
||||
|
||||
This check isn't perfect, because it can give false positives (if there are unrelated
|
||||
unstaged changes).
|
||||
"""
|
||||
git_diff_cmd = ['git', 'diff', '-z', '--exit-code', '--name-only']
|
||||
|
||||
changed_files = []
|
||||
proc = subprocess.run(git_diff_cmd, capture_output=True)
|
||||
if proc.returncode != 0:
|
||||
changed_files = split_on_nulls(proc.stdout.decode('utf-8'))
|
||||
return changed_files
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
if __name__ == "__main__":
|
||||
sys.exit(1)
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
#
|
||||
# Scan all USB devices to find the Model 01's modem device number.
|
||||
#
|
||||
my @output = qx(/usr/sbin/usbconfig show_ifdrv);
|
||||
my $serial_port_number;
|
||||
|
||||
foreach my $line (@output) {
|
||||
chomp $line;
|
||||
|
||||
next unless $line =~ m/umodem(\d+):.*Keyboardio Model 01/;
|
||||
$serial_port_number = $1;
|
||||
}
|
||||
|
||||
die "Can't find Model 01" unless defined($serial_port_number);
|
||||
|
||||
my $serial_port_name = "/dev/cuaU$serial_port_number";
|
||||
die "Missing serial port at $serial_port_name" unless -e $serial_port_name;
|
||||
print "$serial_port_name\n";
|
||||
exit 0;
|
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env perl
|
||||
# find-device-port-linux-udev - Kaleidoscope helper tool
|
||||
# Copyright (C) 2017-2018 Keyboard.io, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use FindBin qw{$Bin};
|
||||
|
||||
die "Usage: $0 VID PID [-v]\n" unless @ARGV >= 2;
|
||||
|
||||
my $vid = shift @ARGV;
|
||||
my $pid = shift @ARGV;
|
||||
my $verbose = shift @ARGV if (@ARGV);
|
||||
my $prefix = '/dev/serial/by-id/';
|
||||
my @paths = `ls $prefix`;
|
||||
my %devices;
|
||||
my @log;
|
||||
|
||||
sub debug {
|
||||
if ($verbose) {
|
||||
print STDERR @_;
|
||||
} else {
|
||||
push @log, @_;
|
||||
}
|
||||
}
|
||||
|
||||
sub print_warning {
|
||||
print STDERR @_;
|
||||
}
|
||||
|
||||
|
||||
debug "Looking for USB device with vid=$vid and pid=$pid\n";
|
||||
|
||||
for my $path (@paths) {
|
||||
chomp($path);
|
||||
debug "Examining $path\n";
|
||||
debug " not symlink\n" unless -l $prefix . $path;
|
||||
next unless -l $prefix . $path;
|
||||
my @data = `udevadm info -q property --name=${prefix}${path}`;
|
||||
for my $line (@data) {
|
||||
chomp($line);
|
||||
my ( $key, $val ) = split( /=/, $line, 2 );
|
||||
$devices{$path}{$key} = $val;
|
||||
}
|
||||
if ( hex $devices{$path}{'ID_VENDOR_ID'} != hex $vid ) {
|
||||
debug " ID_VENDOR_ID $devices{$path}{'ID_VENDOR_ID'} != $vid\n";
|
||||
next;
|
||||
}
|
||||
if ( hex $devices{$path}{'ID_MODEL_ID'} != hex $pid ) {
|
||||
debug " ID_MODEL_ID $devices{$path}{'ID_MODEL_ID'} != $pid\n";
|
||||
next;
|
||||
}
|
||||
|
||||
debug " Found keyboard!\n";
|
||||
|
||||
if ( $devices{$path}{'ID_MM_DEVICE_IGNORE'} ) {
|
||||
debug " ID_MM_DEVICE_IGNORE is set - good!\n";
|
||||
}
|
||||
|
||||
if ( $devices{$path}{'ID_MM_CANDIDATE'}) {
|
||||
my $rules = "$Bin/../etc/60-kaleidoscope.rules";
|
||||
print_warning <<EOWARN
|
||||
|
||||
WARNING: your udev rules are currently configured to suggest
|
||||
that your keyboard is suitable for use by ModemManager. This
|
||||
means that there is a risk of ModemManager interfering. To avoid
|
||||
this, copy
|
||||
|
||||
$rules
|
||||
|
||||
to /etc/udev/rules.d
|
||||
EOWARN
|
||||
}
|
||||
|
||||
print $devices{$path}{DEVNAME};
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#debug("ERROR: I couldn't find a USB device matching the keyboard's USB Vendor and Device IDs\n");
|
||||
#print_warning(join("\n",@log));
|
@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# Based on listArduinos.pl from https://github.com/todbot/usbSearch (License: MIT)
|
||||
# Original (C) 2012, Tod E. Kurt, http://todbot.com/blog/
|
||||
# This version by Michael Richters <gedankenexperimenter@gmail.com> and Jesse Vincent <jesse@keyboard.io>
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
my $vid = shift @ARGV;
|
||||
my $pid = shift @ARGV;
|
||||
|
||||
if (!defined $vid || !defined $pid) {
|
||||
die "$0 has two required parameters, VID and PID";
|
||||
}
|
||||
|
||||
# ioreg might be more machine-readable than system_profiler, but I haven't been able to
|
||||
# get it to produce useful output
|
||||
my @output = qx(/usr/sbin/system_profiler SPUSBDataType 2> /dev/null);
|
||||
|
||||
my $serial = "";
|
||||
my $location = "";
|
||||
|
||||
my $output = join('', @output);
|
||||
|
||||
my @stanzas = split(/\n\n/, $output);
|
||||
foreach my $stanza (@stanzas) {
|
||||
if ($stanza =~ /Product ID: ${pid}/ && $stanza =~ /Vendor ID: ${vid}/) {
|
||||
if ($stanza =~ /Serial Number: (.*?)$/m) {
|
||||
$serial = $1;
|
||||
}
|
||||
if ($stanza =~ /Location ID: (.*?)$/m) {
|
||||
$location = $1;
|
||||
}
|
||||
|
||||
if ($serial) {
|
||||
try_for_raw_serialnum($serial);
|
||||
}
|
||||
if ($location) {
|
||||
try_for_location_id($location);
|
||||
}
|
||||
if ($serial) {
|
||||
try_for_sn_prefix($serial);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
sub try_for_raw_serialnum {
|
||||
my $sn = shift;
|
||||
|
||||
my $serial_port_name = "/dev/cu.usbmodem" . $sn;
|
||||
exit_with_port_if_exists($serial_port_name);
|
||||
|
||||
# High Sierra sometimes has a mismatch between the serial number and the device
|
||||
# filename. I'm not sure why, but system_profiler has a serial number ending in "E",
|
||||
# whereas the device filename ends in "1". In fact, when I change HID.getShortName()
|
||||
# to return "kbio02", the final character is replaced with a "1".
|
||||
|
||||
if ($serial_port_name =~ /\d$/) {
|
||||
chop $serial_port_name;
|
||||
exit_with_port_if_exists($serial_port_name . "1");
|
||||
} else {
|
||||
# If the serial port name doesn't end with a digit, try -appending- rather than replacing
|
||||
# the last character of the port name
|
||||
exit_with_port_if_exists($serial_port_name . "1");
|
||||
|
||||
# and if that didn't work, try replacing the last character with a "1" anyway.
|
||||
# Jason Koh reports that he saw this behavior as required on Catalina in May 2020.
|
||||
|
||||
chop $serial_port_name;
|
||||
exit_with_port_if_exists($serial_port_name . "1");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
sub try_for_location_id {
|
||||
my $location_id = shift;
|
||||
|
||||
# macOS truncates the string of "0"s from the right of the location id.
|
||||
# Here, also, the final character is an appended "1", so if macOS ever stops doing that,
|
||||
# this will need an update, as well.
|
||||
if ($location_id =~ /0x(\d+?)0*\b/) {
|
||||
my $loc = $1;
|
||||
exit_with_port_if_exists("/dev/cu.usbmodem" . $loc . "1");
|
||||
}
|
||||
}
|
||||
|
||||
sub try_for_sn_prefix {
|
||||
my $sn = shift;
|
||||
# If macOS has appended 'E', take it off to maximise our chances of a match.
|
||||
$sn =~ s/E$//;
|
||||
|
||||
# If none of the above tests succeeds, just list the directory and see if there are any
|
||||
# files that have the device shortname that we expect:
|
||||
foreach my $line (qx(ls /dev/cu.usbmodem*)) {
|
||||
if ($line =~ /${sn}/) {
|
||||
chomp $line;
|
||||
print $line;
|
||||
exit 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub exit_with_port_if_exists {
|
||||
my $serial_port_name = shift;
|
||||
|
||||
if (-e $serial_port_name) {
|
||||
print $serial_port_name;
|
||||
exit 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
# Usage:
|
||||
#
|
||||
# > find-device-port-cygwin.ps1 '1209' '2300' -Format COM
|
||||
# COM7
|
||||
# > find-device-port-cygwin.ps1 '1209' '2300' -Format WSL
|
||||
# /dev/ttyS7
|
||||
# > find-device-port-cygwin.ps1 '1209' '2300' -Format Cygwin
|
||||
# /dev/ttyS6
|
||||
Param(
|
||||
[string]$VendorID,
|
||||
[string]$ProductID, # Careful; $PID is a different builtin
|
||||
[ValidateSet('COM','Cygwin','WSL')][string]$Format
|
||||
)
|
||||
|
||||
$DeviceParametersRegKey = @(Get-ChildItem -ErrorAction SilentlyContinue -Recurse 'HKLM:\SYSTEM\CurrentControlSet\Enum' |
|
||||
Where-Object Name -match "VID_$VendorID&PID_$ProductID" |
|
||||
Where-Object Property -eq PortName)
|
||||
|
||||
if ($DeviceParametersRegKey.Count -eq 0) {
|
||||
throw "Could not find any devices matching VID $VendorID and PID $ProductID which were mapped to a COM port"
|
||||
}
|
||||
|
||||
if ($DeviceParametersRegKey.Count -ge 2) {
|
||||
throw "More than one devices matching VID $VendorID and PID $ProductID were found mapped to a COM port"
|
||||
}
|
||||
|
||||
# This will be of form 'COM6'
|
||||
$COMPortName = ($DeviceParametersRegKey | Get-ItemProperty).PortName
|
||||
$COMPortNumber = [int]$COMPortName.Substring(3)
|
||||
|
||||
if ($Format -eq 'COM') {
|
||||
$Output = $COMPortName
|
||||
} elseif ($Format -eq 'WSL') {
|
||||
$Output = "/dev/ttyS$COMPortNumber"
|
||||
} elseif ($Format -eq 'Cygwin') {
|
||||
$CygwinPortNumber = $COMPortNumber - 1
|
||||
$Output = "/dev/ttyS$CygwinPortNumber"
|
||||
}
|
||||
|
||||
# "-NoNewline" below is important to prevent bash from seeing an extra trailing '\r'
|
||||
Write-Host -NoNewline $Output
|
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# find-filename-conflicts - Finds cpp files with conflicting filenames
|
||||
# Copyright (C) 2020 Keyboard.io, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## When building Kaleidoscope, the compiled object files are linked together
|
||||
## into a static archive. This static archive has a very simple structure, and
|
||||
## only stores filenames, not paths, not even relative ones. As such, we can't
|
||||
## have files with the same name, because they will conflict, and one will
|
||||
## override the other.
|
||||
##
|
||||
## To avoid this situation, this script will find all cpp source files (we don't
|
||||
## need to care about header-only things, those do not result in an object
|
||||
## file), and will comb through them to find conflicting filenames.
|
||||
##
|
||||
## If a conflict is found, it will print all files that share the name, and will
|
||||
## exit with an error at the end. It does not exit at the first duplicate, but
|
||||
## will find and print all of them.
|
||||
##
|
||||
## If no conflict is found, the script just prints its status message and exits
|
||||
## with zero.
|
||||
|
||||
set -e
|
||||
|
||||
FILE_LIST="$(find src -name '*.cpp' | sed -e 's,\(\(.*\)/\([^/]*\)\),\3 \1,')"
|
||||
|
||||
exit_code=0
|
||||
|
||||
echo -n "Looking for conflicting filenames... "
|
||||
|
||||
for f in $(echo "${FILE_LIST}" | cut -f1 -d" "); do
|
||||
count=$(echo "${FILE_LIST}" | grep -c "^${f}")
|
||||
if [ "$count" -gt 1 ]; then
|
||||
echo >&2
|
||||
echo " Conflict found for ${f}: " >&2
|
||||
echo "${FILE_LIST}" | grep "${f}" | cut -d" " -f2 | sed -e 's,^, ,' >&2
|
||||
exit_code=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${exit_code}" -eq 0 ]; then
|
||||
echo "done."
|
||||
fi
|
||||
|
||||
exit ${exit_code}
|
@ -1,96 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
|
||||
|
||||
# This is free and unencumbered software released into the public domain.
|
||||
|
||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
# distribute this software, either in source code form or as a compiled
|
||||
# binary, for any purpose, commercial or non-commercial, and by any
|
||||
# means.
|
||||
|
||||
# In jurisdictions that recognize copyright laws, the author or authors
|
||||
# of this software dedicate any and all copyright interest in the
|
||||
# software to the public domain. We make this dedication for the benefit
|
||||
# of the public at large and to the detriment of our heirs and
|
||||
# successors. We intend this dedication to be an overt act of
|
||||
# relinquishment in perpetuity of all present and future rights to this
|
||||
# software under copyright law.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# For more information, please refer to <http://unlicense.org/>
|
||||
# ------------------------------------------------------------------------------
|
||||
"""When building Kaleidoscope, the compiled object files are linked together
|
||||
into a static archive. This static archive has a very simple structure, and only
|
||||
stores filenames, not paths, not even relative ones. As such, we can't have
|
||||
files with the same name, because they will conflict, and one will override the
|
||||
other.
|
||||
|
||||
To avoid this situation, this script will find all cpp source files (we don't
|
||||
need to care about header-only things, those do not result in an object file),
|
||||
and will comb through them to find conflicting filenames.
|
||||
|
||||
If a conflict is found, it will print all files that share the name, and will
|
||||
exit with an error at the end. It does not exit at the first duplicate, but will
|
||||
find and print all of them.
|
||||
|
||||
If no conflict is found, the script just prints its status message and exits
|
||||
with zero."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
cpp_regex = re.compile('.*\.cpp')
|
||||
|
||||
|
||||
def find_duplicates(root):
|
||||
"""Search for files with the same basename, but in different directories in
|
||||
the tree under <root>. Prints a message for each conflict found, and
|
||||
returns a count of the number of non-unique basenames."""
|
||||
|
||||
# Search the specified tree for matching basenames:
|
||||
basenames = {}
|
||||
for dir_path, dirs, files in os.walk(root):
|
||||
for file_name in files:
|
||||
if cpp_regex.match(file_name):
|
||||
if file_name not in basenames:
|
||||
basenames[file_name] = []
|
||||
basenames[file_name].append(dir_path)
|
||||
|
||||
conflict_count = 0
|
||||
for file_name, dirs in basenames.items():
|
||||
# Prune unique basenames from the dict:
|
||||
if len(dirs) <= 1:
|
||||
continue
|
||||
|
||||
conflict_count += 1
|
||||
# Print info about basenames with conflicts:
|
||||
print(f"Conflict found for file name '{file_name}':")
|
||||
for root in dirs:
|
||||
path = os.path.join(root, file_name)
|
||||
print(f' -> {path}')
|
||||
|
||||
return conflict_count
|
||||
|
||||
|
||||
def main(args):
|
||||
print('Searching for conflicting filenames...')
|
||||
exit_code = 0
|
||||
for path in args:
|
||||
exit_code += find_duplicates(path)
|
||||
if exit_code != 0:
|
||||
sys.exit(exit_code)
|
||||
print('No filename conflicts found.')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# In case it hasn't been set, assume we're being run from the root of the local
|
||||
# Kaleidoscope repository.
|
||||
: "${KALEIDOSCOPE_DIR:=$(pwd)}"
|
||||
|
||||
# This variable is a git ref that should point to the current master branch of
|
||||
# the primary Kaleidoscope repository. In most cases, this would be
|
||||
# `origin/master`.
|
||||
: "${KALEIDOSCOPE_MERGE_BASE:=origin/master}"
|
||||
|
||||
# Don't do anything if the working tree has unstaged changes, to avoid
|
||||
# unintentional combining of contentful changes with formatting changes.
|
||||
if ! git diff -z --exit-code --quiet; then
|
||||
echo "Working tree has unstaged changes; aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run git-diff so we only run IWYU on files that differ from `master`. This
|
||||
# isn't necessarily what we want, but if the current branch has been rebased, it
|
||||
# shouldn't touch any extra files.
|
||||
git diff -z --name-only "${KALEIDOSCOPE_MERGE_BASE}" -- src plugins \
|
||||
| "${KALEIDOSCOPE_DIR}/bin/iwyu.py" -z -v
|
||||
|
||||
# After running it on Kaleidoscope source files, run it on the test simulator,
|
||||
# which requires some additional include dirs.
|
||||
git diff -z --name-only "${KALEIDOSCOPE_MERGE_BASE}" -- testing \
|
||||
| "${KALEIDOSCOPE_DIR}/bin/iwyu.py" \
|
||||
-z -v \
|
||||
-I="${KALEIDOSCOPE_DIR}" \
|
||||
-I="${KALEIDOSCOPE_DIR}/testing/googletest/googlemock/include" \
|
||||
-I="${KALEIDOSCOPE_DIR}/testing/googletest/googletest/include"
|
||||
|
||||
# Always run clang-format after IWYU, because they have different indentation
|
||||
# rules for comments added by IWYU.
|
||||
git diff -z --name-only "${KALEIDOSCOPE_MERGE_BASE}" -- src plugins testing \
|
||||
| "${KALEIDOSCOPE_DIR}/bin/format-code.py" \
|
||||
-z -v \
|
||||
--exclude-dir='testing/googletest' \
|
||||
--exclude-file='generated-testcase.cpp' \
|
||||
--force \
|
||||
--check \
|
||||
--verbose
|
@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# focus-send - Trivial Focus testing tool
|
||||
# Copyright (C) 2018-2022 Keyboard.io, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set -e
|
||||
|
||||
OS=$(uname -s)
|
||||
|
||||
# igncr absorbs CR from Focus CRLF line endings
|
||||
# -echo is needed because raw doesn't turn it off on Linux
|
||||
STTY_ARGS="9600 raw igncr -echo"
|
||||
|
||||
case ${OS} in
|
||||
Linux)
|
||||
DEVICE="${DEVICE:-/dev/ttyACM0}"
|
||||
;;
|
||||
Darwin)
|
||||
# bash on macOS has a bug that randomly drops serial input
|
||||
if [ -n "$BASH_VERSION" ] && [ -x /bin/dash ]; then
|
||||
# Prevent loop in case someone exported it
|
||||
export -n BASH_VERSION
|
||||
exec /bin/dash "$0" "$@"
|
||||
fi
|
||||
DEVICE="${DEVICE:-/dev/cu.usbmodemCkbio01E}"
|
||||
;;
|
||||
*)
|
||||
echo "Error Unknown OS : ${OS}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Redirect prior to running stty, because macOS sometimes resets termios
|
||||
# state upon last close of a terminal device.
|
||||
exec < "${DEVICE}"
|
||||
# shellcheck disable=SC2086 # intentional word splitting
|
||||
stty $STTY_ARGS
|
||||
|
||||
read_reply () {
|
||||
while read -r line; do
|
||||
if [ "${line}" = "." ]; then
|
||||
break
|
||||
fi
|
||||
echo "${line}"
|
||||
done
|
||||
}
|
||||
|
||||
# Flush any invalid commands out of input buffer.
|
||||
# This could happen after a failed upload.
|
||||
echo ' ' > "${DEVICE}"
|
||||
read_reply > /dev/null
|
||||
echo "$@" >"${DEVICE}"
|
||||
read_reply
|
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# focus-test - Trivial Focus testing tool
|
||||
# Copyright (C) 2018 Keyboard.io, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set -e
|
||||
|
||||
OS=$(uname -s)
|
||||
|
||||
case ${OS} in
|
||||
Linux)
|
||||
DEVICE="${DEVICE:-/dev/ttyACM0}"
|
||||
stty -F "${DEVICE}" 9600 raw -echo
|
||||
;;
|
||||
Darwin)
|
||||
DEVICE="${DEVICE:-/dev/cu.usbmodemCkbio01E}"
|
||||
stty -f "${DEVICE}" 9600 raw -echo
|
||||
;;
|
||||
*)
|
||||
echo "Error Unknown OS : ${OS}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
exec 3<"${DEVICE}"
|
||||
echo "$@" >"${DEVICE}"
|
||||
|
||||
while read -r line <&3; do
|
||||
line="$(echo -n "${line}" | tr -d '\r')"
|
||||
if [ "${line}" == "." ]; then
|
||||
break
|
||||
fi
|
||||
echo "${line}"
|
||||
done
|
@ -1,275 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
|
||||
|
||||
# This is free and unencumbered software released into the public domain.
|
||||
|
||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
# distribute this software, either in source code form or as a compiled
|
||||
# binary, for any purpose, commercial or non-commercial, and by any
|
||||
# means.
|
||||
|
||||
# In jurisdictions that recognize copyright laws, the author or authors
|
||||
# of this software dedicate any and all copyright interest in the
|
||||
# software to the public domain. We make this dedication for the benefit
|
||||
# of the public at large and to the detriment of our heirs and
|
||||
# successors. We intend this dedication to be an overt act of
|
||||
# relinquishment in perpetuity of all present and future rights to this
|
||||
# software under copyright law.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# For more information, please refer to <http://unlicense.org/>
|
||||
# ------------------------------------------------------------------------------
|
||||
"""This script runs clang-format on a Kaleidoscope repository."""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common import check_git_diff, setup_logging, split_on_newlines, split_on_nulls
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def parse_args(args):
|
||||
"""Parse command line parameters
|
||||
|
||||
Args:
|
||||
args (list[str]): command line parameters as list of strings
|
||||
(for example ``["--help"]``).
|
||||
|
||||
Returns:
|
||||
:obj:`argparse.Namespace`: command line parameters namespace
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="""
|
||||
Recursively search specified directories and format source files with
|
||||
clang-format. By default, it operates on Arduino C++ source files with
|
||||
extensions: *.{cpp,h,hpp,inc,ino}.""")
|
||||
parser.add_argument(
|
||||
'-q',
|
||||
'--quiet',
|
||||
dest='loglevel',
|
||||
action='store_const',
|
||||
const=logging.ERROR,
|
||||
default=logging.WARNING,
|
||||
help="""
|
||||
Suppress output except warnings and errors.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v',
|
||||
'--verbose',
|
||||
action='store_const',
|
||||
dest='loglevel',
|
||||
const=logging.INFO,
|
||||
help="""
|
||||
Output verbose debugging information.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
'--debug',
|
||||
action='store_const',
|
||||
dest='loglevel',
|
||||
const=logging.DEBUG,
|
||||
help="""
|
||||
Save output from `include-what-you-use` for processed files beside the originals, with
|
||||
a '.iwyu' suffix, for debugging purposes.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-X',
|
||||
'--exclude-dir',
|
||||
action='append',
|
||||
dest='exclude_dirs',
|
||||
default=[],
|
||||
metavar="<path>",
|
||||
help="""
|
||||
Exclude dir from search (path relative to the pwd)""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-x',
|
||||
'--exclude-file',
|
||||
action='append',
|
||||
dest='exclude_files',
|
||||
default=[],
|
||||
metavar="<file>",
|
||||
help="""
|
||||
Exclude <file> (base name only, not a full path) from formatting""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-r',
|
||||
'--regex',
|
||||
dest='regex',
|
||||
default=r'\.(cpp|h|hpp|inc|ino)$',
|
||||
metavar="<regex>",
|
||||
help="""
|
||||
Regular expression for matching source file names""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-z',
|
||||
'-0',
|
||||
action='store_const',
|
||||
dest='input_splitter',
|
||||
const=split_on_nulls,
|
||||
default=split_on_newlines,
|
||||
help="""
|
||||
When reading target filenames from standard input, break on NULL characters instead
|
||||
of newlines.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-f',
|
||||
'--force',
|
||||
action='store_true',
|
||||
help="""
|
||||
Format code even if there are unstaged changes""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--check',
|
||||
action='store_true',
|
||||
help="""
|
||||
Check for changes after formatting by running `git diff --exit-code`. If there are any
|
||||
changes after formatting, a non-zero exit code is returned.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'targets',
|
||||
metavar="<search_dir>",
|
||||
nargs='*',
|
||||
help="""
|
||||
A list of files and/or directories to search for source files to format.""",
|
||||
)
|
||||
return parser.parse_args(args)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def main():
|
||||
"""Parse command-line arguments and format source files.
|
||||
"""
|
||||
# Parse command-line argumets:
|
||||
opts = parse_args(sys.argv[1:])
|
||||
# Set up logging system:
|
||||
setup_logging(opts.loglevel)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Unless we've been given the `--force` option, check for unstaged changes to avoid
|
||||
# clobbering any work in progress:
|
||||
exit_code = 0
|
||||
if not opts.force:
|
||||
changed_files = check_git_diff()
|
||||
if len(changed_files) > 0:
|
||||
logging.error("Working tree has unstaged changes; aborting")
|
||||
return 1
|
||||
|
||||
# Locate `clang-format` executable:
|
||||
clang_format_exe = os.getenv('KALEIDOSCOPE_CODE_FORMATTER')
|
||||
if clang_format_exe is None:
|
||||
clang_format_exe = shutil.which('clang-format')
|
||||
logging.debug("Found `clang-format` executable: %s", clang_format_exe)
|
||||
clang_format_cmd = [clang_format_exe, '-i']
|
||||
if opts.loglevel <= logging.INFO:
|
||||
clang_format_cmd.append('--verbose')
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Read targets from command line:
|
||||
targets = opts.targets
|
||||
logging.debug("CLI target parameters: %s", targets)
|
||||
|
||||
# If stdin is a pipe, read target filenames from it:
|
||||
if not sys.stdin.isatty():
|
||||
targets += opts.input_splitter(sys.stdin.read())
|
||||
logging.debug("All targets: %s", targets)
|
||||
|
||||
# Prepare exclusion lists. The file excludes are basenames only, and the dirs get
|
||||
# converted to absolute path names.
|
||||
exclude_files = set(opts.exclude_files)
|
||||
exclude_dirs = set(os.path.abspath(_) for _ in opts.exclude_dirs)
|
||||
|
||||
# Convert target paths to absolute, and remove any that are excluded:
|
||||
target_paths = set(os.path.abspath(_) for _ in targets if _ not in exclude_dirs)
|
||||
logging.debug("Target paths: %s", target_paths)
|
||||
|
||||
# Build separate sets of target files and dirs. Later, we'll search target dirs and add
|
||||
# matching target files to the files set.
|
||||
target_files = set()
|
||||
target_dirs = set()
|
||||
for t in target_paths:
|
||||
if os.path.isfile(t):
|
||||
target_files.add(os.path.abspath(t))
|
||||
elif os.path.isdir(t):
|
||||
target_dirs.add(os.path.abspath(t))
|
||||
logging.debug("Target files after separating: %s", target_files)
|
||||
logging.debug("Target dirs after separating: %s", target_dirs)
|
||||
|
||||
# Remove excluded filenames:
|
||||
target_files -= set(_ for _ in target_files if os.path.basename(_) in exclude_files)
|
||||
|
||||
# Remove files and dirs in excluded dirs:
|
||||
target_files -= set(_ for _ in target_files for x in exclude_dirs if _.startswith(x))
|
||||
target_dirs -= set(_ for _ in target_dirs for x in exclude_dirs if _.startswith(x))
|
||||
|
||||
# Compile regex for matching files to be formatted:
|
||||
target_matcher = re.compile(opts.regex)
|
||||
|
||||
# Remove target files that don't match the regex:
|
||||
logging.debug("Target files before matching regex: %s", target_files)
|
||||
target_files = set(_ for _ in target_files if target_matcher.search(_))
|
||||
logging.debug("Target files after matching regex: %s", target_files)
|
||||
|
||||
# Search target dirs for non-excluded files, and add them to `target_files`:
|
||||
logging.debug("Searching target dirs: %s", target_dirs)
|
||||
for path in target_dirs:
|
||||
for root, dirs, files in os.walk(path):
|
||||
# Prune excluded dirs
|
||||
for x in exclude_dirs:
|
||||
if x in (os.path.join(root, _) for _ in dirs):
|
||||
dirs.remove(os.path.basename(x))
|
||||
# Add non-excluded files
|
||||
for f in files:
|
||||
if target_matcher.search(f) and f not in exclude_files:
|
||||
target_files.add(os.path.join(root, f))
|
||||
|
||||
if len(target_files) == 0:
|
||||
logging.error("No target files found; exiting.")
|
||||
return 1
|
||||
|
||||
# Run clang-format on target files:
|
||||
proc = subprocess.run(clang_format_cmd + sorted(target_files))
|
||||
if proc.returncode != 0:
|
||||
logging.error("Error: clang-format returned non-zero status: %s", proc.returncode)
|
||||
return proc.returncode
|
||||
else:
|
||||
logging.info("Finished formatting target files.")
|
||||
|
||||
# If we've been asked to check for changes made by the formatter:
|
||||
if opts.check:
|
||||
logging.warning('Checking for changes made by the formatter...')
|
||||
changed_files = check_git_diff()
|
||||
if len(changed_files) == 0:
|
||||
logging.warning("No files changed. Congratulations!")
|
||||
else:
|
||||
logging.warning("Found files with changes after formatting:")
|
||||
exit_code = 1
|
||||
for f in changed_files:
|
||||
logging.warning(" %s", f)
|
||||
|
||||
return exit_code
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except KeyboardInterrupt:
|
||||
logging.info("Aborting")
|
||||
sys.exit(1)
|
@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
: "${KALEIDOSCOPE_DIR:=$(pwd)}"
|
||||
cd "${KALEIDOSCOPE_DIR}" || exit 1
|
||||
|
||||
: "${VERBOSE:=}"
|
||||
|
||||
"${KALEIDOSCOPE_DIR}/bin/format-code.py" \
|
||||
--exclude-dir 'testing/googletest' \
|
||||
--exclude-file 'generated-testcase.cpp' \
|
||||
--verbose \
|
||||
src plugins testing
|
@ -1,508 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
|
||||
|
||||
# This is free and unencumbered software released into the public domain.
|
||||
|
||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
# distribute this software, either in source code form or as a compiled
|
||||
# binary, for any purpose, commercial or non-commercial, and by any
|
||||
# means.
|
||||
|
||||
# In jurisdictions that recognize copyright laws, the author or authors
|
||||
# of this software dedicate any and all copyright interest in the
|
||||
# software to the public domain. We make this dedication for the benefit
|
||||
# of the public at large and to the detriment of our heirs and
|
||||
# successors. We intend this dedication to be an overt act of
|
||||
# relinquishment in perpetuity of all present and future rights to this
|
||||
# software under copyright law.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# For more information, please refer to <http://unlicense.org/>
|
||||
# ------------------------------------------------------------------------------
|
||||
"""Run `include-what-you-use` on Kaleidoscope sources
|
||||
|
||||
This is a script for maintenance of the headers included in Kaleidoscope source
|
||||
files. It is not currently possible to run this automatically on all
|
||||
Kaleidoscope source files, because of the peculiarities therein. It uses
|
||||
llvm/clang to determine which headers should be included in a given file, but
|
||||
there's no avr-clang, so we're limited to using the virtual hardware device.
|
||||
|
||||
It takes any number of source files as its input, examines them and updates the
|
||||
list of header `#include` directives.
|
||||
|
||||
It is safe to run on most Kaleidoscope source files, and a good idea to run it
|
||||
on new ones (after staging them, so you can easily see what changes have been
|
||||
made).
|
||||
"""
|
||||
|
||||
# Example invocation:
|
||||
# $ git ls-files -m | grep '\.\(h\|cpp\)' | bin/iwyu.py
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common import cwd, setup_logging, split_on_newlines, split_on_nulls
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def parse_args(args):
|
||||
"""Parse command line parameters
|
||||
|
||||
Args:
|
||||
args (list[str]): command line parameters as list of strings
|
||||
(for example ``["--help"]``).
|
||||
|
||||
Returns:
|
||||
:obj:`argparse.Namespace`: command line parameters namespace
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="""
|
||||
Run `include-what-you-use` on source files given as command-line arguments and/or read
|
||||
from standard input. When reading target filenames from standard input, they should be
|
||||
either absolute or relative to the current directory, and each line of input (minus the
|
||||
line-ending character(s) is treated as a filename.""")
|
||||
parser.add_argument(
|
||||
'-q',
|
||||
'--quiet',
|
||||
dest='loglevel',
|
||||
action='store_const',
|
||||
const=logging.ERROR,
|
||||
default=logging.WARNING,
|
||||
help="""
|
||||
Suppress output except warnings and errors.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v',
|
||||
'--verbose',
|
||||
action='store_const',
|
||||
dest='loglevel',
|
||||
const=logging.INFO,
|
||||
help="""
|
||||
Output verbose debugging information.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
'--debug',
|
||||
action='store_const',
|
||||
dest='loglevel',
|
||||
const=logging.DEBUG,
|
||||
help="""
|
||||
Save output from `include-what-you-use` for processed files beside the originals, with
|
||||
a '.iwyu' suffix, for debugging purposes.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-r',
|
||||
'--regex',
|
||||
action='store',
|
||||
dest='regex',
|
||||
default=r'\.(h|cpp)$',
|
||||
help="""
|
||||
A regular expression for matching filenames Only the basename of the file is
|
||||
matched, and the regex is only used when searching a directory for files to process,
|
||||
not on target filenames specified in arguments or read from standard input.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-i',
|
||||
'--ignores_file',
|
||||
action='store',
|
||||
dest='ignores_file',
|
||||
default='.iwyu_ignore',
|
||||
metavar='<ignores_file>',
|
||||
help="""
|
||||
The name of a file (relative to KALEIDOSCOPE_DIR) that contains a list of glob patterns
|
||||
that will be ignored when a target directory is searched for filenames that match
|
||||
<regex>.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-I',
|
||||
'--include',
|
||||
action='append',
|
||||
dest='include_dirs',
|
||||
metavar='<dir>',
|
||||
help="""
|
||||
Add <dir> to the list of directories that will be searched for header files.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-z',
|
||||
'-0',
|
||||
action='store_const',
|
||||
dest='input_splitter',
|
||||
const=split_on_nulls,
|
||||
default=split_on_newlines,
|
||||
help="""
|
||||
When reading target filenames from standard input, break on NULL characters instead
|
||||
of newlines.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--update_comments',
|
||||
action='store_true',
|
||||
help="""
|
||||
Call `include-what-you-use` with `--update_comments` to always rewrite its 'for'
|
||||
comments regarding which symbols are used.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'targets',
|
||||
nargs='*',
|
||||
metavar="<target>",
|
||||
help="""
|
||||
A list of target files and/or directories to search for source files to format. Any
|
||||
target file will be processed, regardless of the filename. Any target directory will
|
||||
be recursively searched for files matching the regular expression given by --regex.
|
||||
Filenames and directories beginning with a '.' will always be excluded from the search,
|
||||
but can still be processed if specified as a command-line target.""",
|
||||
)
|
||||
return parser.parse_args(args)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def main():
|
||||
"""Main entry point function."""
|
||||
# Parse command-line arguments:
|
||||
opts = parse_args(sys.argv[1:])
|
||||
# Set up logging system:
|
||||
setup_logging(opts.loglevel)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Find include-what-you-use:
|
||||
iwyu = shutil.which('include-what-you-use')
|
||||
logging.debug("Found `include-what-you-use` executable: %s", iwyu)
|
||||
iwyu_opts = [
|
||||
'--no_fwd_decls', # No forward declarations
|
||||
'--max_line_length=100',
|
||||
]
|
||||
if opts.update_comments:
|
||||
iwyu_opts.append('--update_comments')
|
||||
# Prepend '-Xiwyu' to each `include-what-you-use` option:
|
||||
iwyu_opts = [_ for opt in iwyu_opts for _ in ('-Xiwyu', opt)]
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Find fix_includes:
|
||||
fix_includes = shutil.which('fix_includes.py')
|
||||
logging.debug("Found `fix_includes` executable: %s", fix_includes)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Find clang (first checking environment variable):
|
||||
clang = os.getenv('CLANG_COMPILER')
|
||||
if clang is None:
|
||||
clang = shutil.which('clang')
|
||||
logging.debug("Found `clang` executable: %s", clang)
|
||||
|
||||
# Get system include dir from `clang`:
|
||||
clang_cmd = [clang, '-print-resource-dir']
|
||||
logging.debug("Running command: `%s`", shlex.join(clang_cmd))
|
||||
result = subprocess.run(clang_cmd, capture_output=True, check=True)
|
||||
clang_resource_dir = result.stdout.decode('utf-8').rstrip()
|
||||
system_include_dir = os.path.join(clang_resource_dir, 'include')
|
||||
logging.debug("Using system include dir: %s", system_include_dir)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Get $KALEIDOSCOPE_DIR from enironment, falling back on `pwd`:
|
||||
kaleidoscope_dir = os.getenv('KALEIDOSCOPE_DIR')
|
||||
if kaleidoscope_dir is None:
|
||||
kaleidoscope_dir = os.getcwd()
|
||||
logging.debug("Using Kaleidoscope dir: %s", kaleidoscope_dir)
|
||||
kaleidoscope_src_dir = os.path.join(kaleidoscope_dir, 'src')
|
||||
|
||||
# Define locations of other dirs to find Arduino libraries:
|
||||
virtual_hardware_dir = os.path.join(
|
||||
kaleidoscope_dir, '.arduino', 'user', 'hardware', 'keyboardio', 'virtual')
|
||||
logging.debug("Using virtual hardware dir: %s", virtual_hardware_dir)
|
||||
|
||||
virtual_arduino_core_dir = os.path.join(virtual_hardware_dir, 'cores', 'arduino')
|
||||
logging.debug("Using virtual arduino core: %s", virtual_arduino_core_dir)
|
||||
|
||||
virtual_model01_dir = os.path.join(virtual_hardware_dir, 'variants', 'model01')
|
||||
logging.debug("Using virtual Model01 dir: %s", virtual_model01_dir)
|
||||
|
||||
virtual_keyboardiohid_dir = os.path.join(
|
||||
virtual_hardware_dir, 'libraries', 'KeyboardioHID', 'src')
|
||||
logging.debug("Using virtual KeyboardioHID dir: %s", virtual_keyboardiohid_dir)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Create the long list of options passed to `clang` via `include-what-you-use`.
|
||||
# First, we tell it we're using C++:
|
||||
clang_opts = ['-x', 'c++']
|
||||
# General compiler options:
|
||||
clang_opts += [
|
||||
'-c',
|
||||
'-g',
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-std=gnu++14', # Not `c++14`, because we're using clang, not gcc
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
'-fno-threadsafe-statics',
|
||||
'-MMD',
|
||||
'-Woverloaded-virtual',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-unused-variable',
|
||||
'-Wno-ignored-qualifiers',
|
||||
'-Wno-type-limits',
|
||||
'-Wno-pragma-once-outside-header',
|
||||
]
|
||||
|
||||
# Variables we define to do a Kaleidoscope build:
|
||||
defines = [
|
||||
'KALEIDOSCOPE_VIRTUAL_BUILD=1',
|
||||
'KEYBOARDIOHID_BUILD_WITHOUT_HID=1',
|
||||
'USBCON=dummy',
|
||||
'ARDUINO_ARCH_AVR=1',
|
||||
'ARDUINO=10607',
|
||||
'ARDUINO_AVR_MODEL01',
|
||||
'ARDUINO_ARCH_VIRTUAL',
|
||||
'USB_VID=0x1209',
|
||||
'USB_PID=0x2301',
|
||||
'USB_MANUFACTURER="Keyboardio"',
|
||||
'USB_PRODUCT="Model 01"',
|
||||
'KALEIDOSCOPE_HARDWARE_H="Kaleidoscope-Hardware-Keyboardio-Model01.h"',
|
||||
'TWI_BUFFER_LENGTH=32',
|
||||
]
|
||||
clang_opts += ['-D' + _ for _ in defines]
|
||||
|
||||
# Directories to search for libraries to include:
|
||||
includes = [
|
||||
system_include_dir,
|
||||
kaleidoscope_src_dir,
|
||||
virtual_arduino_core_dir,
|
||||
virtual_model01_dir,
|
||||
virtual_keyboardiohid_dir,
|
||||
]
|
||||
# Include plugin source dirs for plugins that depend on other plugins:
|
||||
includes += glob.glob(os.path.join(kaleidoscope_dir, 'plugins', '*', 'src'))
|
||||
# Include dirs specified on the command line, if any:
|
||||
if opts.include_dirs:
|
||||
includes += [os.path.abspath(_) for _ in opts.include_dirs]
|
||||
clang_opts += ['-I' + _ for _ in includes]
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Define the `include-what-you-use` command (sans target files)
|
||||
iwyu_cmd = [iwyu] + iwyu_opts + clang_opts
|
||||
logging.debug("Using IWYU command: %s", ' \\\n\t'.join(iwyu_cmd))
|
||||
|
||||
fix_includes_cmd = [
|
||||
fix_includes,
|
||||
'--nosafe_headers',
|
||||
'--reorder',
|
||||
'--separate_project_includes=' + kaleidoscope_src_dir, # Does this help?
|
||||
]
|
||||
if opts.update_comments:
|
||||
fix_includes_cmd.append('--update_comments')
|
||||
logging.debug("Using `fix_includes` command: %s", ' \\\n\t'.join(fix_includes_cmd))
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
targets = opts.targets
|
||||
# If stdin is a pipe, read pathname targets, one per line. This allows us to connect the
|
||||
# output of `find` to our input conveniently:
|
||||
if not sys.stdin.isatty():
|
||||
logging.debug("Reading targets from STDIN...")
|
||||
targets += opts.input_splitter(sys.stdin.read())
|
||||
for t in targets:
|
||||
logging.debug(" Target path: %s", t)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
iwyu_ignores_file = os.path.join(kaleidoscope_dir, opts.ignores_file)
|
||||
ignores = build_ignores_list(iwyu_ignores_file)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
regex = re.compile(opts.regex)
|
||||
# Process source files first, then header files, because a source file might have been
|
||||
# relying on a header included by its associated header, but which that header does not
|
||||
# need on its own. In this case, if we process the header first, IWYU won't be able to
|
||||
# parse the source file, and we'll get an error, but if we do them in the other order,
|
||||
# the source file will get all the includes it needs before the header removes any of them.
|
||||
source_files = []
|
||||
header_files = []
|
||||
for target_file in (_ for t in targets for _ in build_target_list(t, regex, ignores)):
|
||||
if target_file.endswith('.cpp') or target_file.endswith('.ino'):
|
||||
source_files.append(target_file)
|
||||
else:
|
||||
header_files.append(target_file)
|
||||
|
||||
# If there's an error processing any file, return an error code.
|
||||
exit_code = 0
|
||||
for target_file in source_files + header_files:
|
||||
# Run IWYU and fix_headers:
|
||||
if not run_iwyu(os.path.relpath(target_file), iwyu_cmd, fix_includes_cmd):
|
||||
exit_code = 1
|
||||
|
||||
return exit_code
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def build_target_list(target, regex, ignores):
|
||||
"""Build the list of target files, starting from a file or directory.
|
||||
|
||||
Parameters:
|
||||
target (str): The name of the file or directory to search
|
||||
regex (Pattern): A compiled regular expression to match target file names
|
||||
ignores (list): A list of (absolute) file names to be excluded from the target list
|
||||
Returns:
|
||||
targets (list): A list of (absolute) file names to be processed
|
||||
|
||||
Given a filename or directory (`path`), returns a list of target filenames to run IWYU
|
||||
on. Target filenames will all be absolute, but `path` can be relative to the current
|
||||
directory. All target filenames will match `regex`, and any items in `ignores` will be
|
||||
excluded. If an element of the `ignores` list is a directory, all files and directories
|
||||
beneath it will be excluded from the search.
|
||||
"""
|
||||
logging.debug("Searching target: %s", target)
|
||||
|
||||
# Convert all paths to absolute.
|
||||
root = os.path.abspath(target)
|
||||
|
||||
# If `path` is a file, and it matches `regex`, add it to the target list.
|
||||
if os.path.isfile(target) and regex.search(target) and root not in ignores:
|
||||
return [root]
|
||||
|
||||
# If the specified path is not valid, just return an empty list.
|
||||
if not os.path.isdir(target):
|
||||
logging.error("Error: File not found: %s", target)
|
||||
return []
|
||||
|
||||
# Start with an empty list.
|
||||
targets = []
|
||||
|
||||
# The specified path is a directory, so we search recursively for files
|
||||
# contained therein that match the specified regular expression.
|
||||
for path, dirs, files in os.walk(root):
|
||||
logging.debug("Searching dir: %s", os.path.relpath(path))
|
||||
# First, ignore all dotfiles (and directories).
|
||||
for x in (os.path.basename(_)
|
||||
for _ in ignores + glob.glob(os.path.join(path, '.*'))
|
||||
if os.path.dirname(_) == path):
|
||||
if x in dirs:
|
||||
logging.info("Skipping ignored dir: %s", os.path.join(path, x))
|
||||
dirs.remove(x)
|
||||
if x in files:
|
||||
logging.info("Skipping ignored file: %s", os.path.join(path, x))
|
||||
files.remove(x)
|
||||
|
||||
logging.debug("Files found: %s", ', '.join(files))
|
||||
# Add all matching files to the list of source files to be formatted.
|
||||
for f in (_ for _ in files if regex.search(_)):
|
||||
t = os.path.join(path, f)
|
||||
logging.debug("Source file found: %s", t)
|
||||
targets.append(t)
|
||||
|
||||
return targets
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def build_ignores_list(ignores_file_path):
|
||||
"""Build a list of files and dirs to exclude from processing by IWYU
|
||||
|
||||
Parameters:
|
||||
ignores_file_path (str): The name of the file to read ignores globs from
|
||||
Returns:
|
||||
ignores_list (list): A list of (absolute) file names to exclude from processing
|
||||
|
||||
Reads `ignores_file` and expands each line as a glob, returning a list of all the target
|
||||
file names to be excluded from processing. Each path in the file is treated as a relative
|
||||
pathname (unless it is already absolute), relative to the directory in which `ignores_file`
|
||||
is located.
|
||||
"""
|
||||
logging.debug("Searching for ignores file: %s", ignores_file_path)
|
||||
# If the ignores file doesn't exist, return an empty list:
|
||||
if not os.path.isfile(ignores_file_path):
|
||||
logging.debug("Ignores file not found")
|
||||
return []
|
||||
|
||||
ignores_list = []
|
||||
with open(ignores_file_path) as f:
|
||||
for line in f.read().splitlines():
|
||||
# Lines starting with `#` are treated as comments.
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
logging.debug("Ignoring files like: %s", line)
|
||||
ignores_list += glob.glob(line, recursive=True)
|
||||
|
||||
# Get the dir of the ignores file so we can construct absolute pathnames.
|
||||
ignores_file_dir = os.path.dirname(ignores_file_path)
|
||||
with cwd(ignores_file_dir):
|
||||
ignores_list[:] = [os.path.abspath(_) for _ in ignores_list]
|
||||
|
||||
logging.debug("Ignores list:\n\t%s", "\n\t".join(ignores_list))
|
||||
return ignores_list
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def run_iwyu(source_file, iwyu_cmd, fix_includes_cmd):
|
||||
"""Run IWYU and fix_includes on a single source file
|
||||
|
||||
Parameters:
|
||||
source_file (str): The name of a file to run IWYU on
|
||||
iwyu_cmd (list): The command name and options list for `include-what-you-use`
|
||||
fix_includes_cmd (list): The command name and options list for `fix_headers.py`
|
||||
Returns:
|
||||
True on success, False if either IWYU or fix_headers returns an error code
|
||||
|
||||
Run `include-what-you-use` on <source_file>, an update that file's header includes by
|
||||
sending the output to `fix_includes.py`. If either command returns an error code, return
|
||||
`False`, otherwise return `True`.
|
||||
"""
|
||||
logging.info("Fixing headers in file: %s", source_file)
|
||||
|
||||
# Run IWYU on <source_file>
|
||||
iwyu_proc = subprocess.run(iwyu_cmd + [source_file], capture_output=True, check=False)
|
||||
|
||||
# If IWYU returns an error, report on it:
|
||||
if iwyu_proc.returncode != 0:
|
||||
logging.error("Error: failed to parse file: %s", source_file)
|
||||
logging.debug("IWYU returned: %s", iwyu_proc.returncode)
|
||||
logging.debug("STDOUT:\n%s", iwyu_proc.stdout.decode('utf-8'))
|
||||
logging.debug("STDERR:\n%s", iwyu_proc.stderr.decode('utf-8'))
|
||||
# In addition to reporting the error, save the output for analysis:
|
||||
with open(source_file + '.iwyu', 'wb') as f:
|
||||
f.write(iwyu_proc.stderr)
|
||||
# Don't run fix_includes if there was an error (or if we've got an old version of IWYU
|
||||
# that returns bogus exit codes):
|
||||
return False
|
||||
|
||||
# IWYU reports on the associated header of *.cpp files, but if we want to skip processing
|
||||
# that header, we need to use only the part of the output for the *.cpp file. Fortunately,
|
||||
# the header is listed first, so we only need to search for the start of the target file's
|
||||
# section of the output.
|
||||
n = iwyu_proc.stderr.find(f"\n{source_file} should".encode('utf-8'))
|
||||
iwyu_stderr = iwyu_proc.stderr[n:]
|
||||
|
||||
# Run fix_includes.py, using the output (stderr) of IWYU:
|
||||
fix_includes_proc = subprocess.run(
|
||||
fix_includes_cmd, input=iwyu_stderr, capture_output=True, check=False)
|
||||
|
||||
# Report any errors returned by fix_includes.py:
|
||||
if fix_includes_proc.returncode != 0:
|
||||
logging.error("Error: failed to fix includes for file: %s", source_file)
|
||||
logging.debug("fix_includes.py returned: %s", fix_includes_proc.returncode)
|
||||
logging.debug("STDOUT:\n%s", fix_includes_proc.stdout.decode('utf-8'))
|
||||
logging.debug("STDERR:\n%s", fix_includes_proc.stderr.decode('utf-8'))
|
||||
return False
|
||||
|
||||
# Return true on success, false otherwise:
|
||||
return True
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except KeyboardInterrupt:
|
||||
logging.info("Aborting")
|
||||
sys.exit(1)
|
@ -0,0 +1,738 @@
|
||||
#!/usr/bin/env bash
|
||||
# kaleidoscope-builder - Kaleidoscope helper tool
|
||||
# Copyright (C) 2017-2018 Keyboard.io, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set -e
|
||||
|
||||
######
|
||||
###### Build and output configuration
|
||||
######
|
||||
|
||||
absolute_filename() {
|
||||
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
|
||||
}
|
||||
|
||||
|
||||
|
||||
build_version () {
|
||||
: "${LIB_PROPERTIES_PATH:="../.."}"
|
||||
GIT_VERSION="$(cd "${SKETCH_DIR}"; if [ -d .git ]; then echo -n '-g' && git describe --abbrev=4 --dirty --always; fi)"
|
||||
LIB_VERSION="$(cd "${SKETCH_DIR}"; (grep version= "${LIB_PROPERTIES_PATH}/library.properties" 2>/dev/null || echo version=0.0.0) | cut -d= -f2)${GIT_VERSION}"
|
||||
}
|
||||
|
||||
build_paths() {
|
||||
# We need that echo because we\re piping to cksum
|
||||
# shellcheck disable=SC2005
|
||||
SKETCH_IDENTIFIER="$(echo "${SKETCH_FILE_PATH}" | cksum | cut -d ' ' -f 1)-${SKETCH_FILE_NAME}"
|
||||
: "${KALEIDOSCOPE_TEMP_PATH:=${TMPDIR:-/tmp}/kaleidoscope-${USER}}"
|
||||
|
||||
: "${KALEIDOSCOPE_BUILD_PATH:=${KALEIDOSCOPE_TEMP_PATH}/sketch}"
|
||||
: "${KALEIDOSCOPE_OUTPUT_PATH:=${KALEIDOSCOPE_TEMP_PATH}/sketch}"
|
||||
|
||||
: "${SKETCH_OUTPUT_DIR:=${SKETCH_IDENTIFIER}/output}"
|
||||
: "${SKETCH_BUILD_DIR:=${SKETCH_IDENTIFIER}/build}"
|
||||
|
||||
: "${BUILD_PATH:=${KALEIDOSCOPE_BUILD_PATH}/${SKETCH_BUILD_DIR}}"
|
||||
: "${OUTPUT_PATH:=${KALEIDOSCOPE_OUTPUT_PATH}/${SKETCH_OUTPUT_DIR}}"
|
||||
|
||||
: "${CCACHE_WRAPPER_PATH:=${KALEIDOSCOPE_TEMP_PATH}/ccache/bin}"
|
||||
: "${CORE_CACHE_PATH:=${KALEIDOSCOPE_TEMP_PATH}/arduino-cores}"
|
||||
|
||||
mkdir -p "$CORE_CACHE_PATH"
|
||||
mkdir -p "$BUILD_PATH"
|
||||
}
|
||||
|
||||
build_filenames () {
|
||||
: "${OUTPUT_FILE_PREFIX:=${SKETCH_BASE_NAME}-${LIB_VERSION}}"
|
||||
: "${HEX_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.hex}"
|
||||
: "${HEX_FILE_WITH_BOOTLOADER_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}-with-bootloader.hex}"
|
||||
: "${ELF_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.elf}"
|
||||
: "${LIB_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.a}"
|
||||
}
|
||||
|
||||
|
||||
enable_ccache () {
|
||||
if [ -z "${CCACHE_NOT_SUPPORTED}" ] && [ "$(command -v ccache)" ]; then
|
||||
if ! [ -d "$CCACHE_WRAPPER_PATH" ]; then
|
||||
mkdir -p "$CCACHE_WRAPPER_PATH"
|
||||
|
||||
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}" ]; then
|
||||
ln -s "$(command -v ccache)" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}"
|
||||
fi
|
||||
|
||||
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}" ]; then
|
||||
ln -s "$(command -v ccache)" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}"
|
||||
fi
|
||||
|
||||
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}nm" ]; then
|
||||
ln -s "${AVR_NM}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}nm"
|
||||
fi
|
||||
|
||||
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}objcopy" ]; then
|
||||
ln -s "${AVR_OBJCOPY}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}objcopy"
|
||||
fi
|
||||
|
||||
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}ar" ]; then
|
||||
ln -s "${AVR_AR}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}ar"
|
||||
fi
|
||||
|
||||
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}size" ]; then
|
||||
ln -s "${AVR_SIZE}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}size"
|
||||
fi
|
||||
fi
|
||||
|
||||
export CCACHE_PATH=${COMPILER_PATH}/
|
||||
CCACHE_ENABLE="-prefs compiler.path=${CCACHE_WRAPPER_PATH}/"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
firmware_size () {
|
||||
if [ "${ARCH}" = "virtual" ]; then
|
||||
echo "[Size not computed for virtual build]"
|
||||
return
|
||||
fi
|
||||
|
||||
: "${MAX_PROG_SIZE:=$(get_arduino_pref 'upload.maximum_size')}"
|
||||
|
||||
## This is a terrible hack, please don't hurt me. - algernon
|
||||
|
||||
set +e
|
||||
raw_output=$("$@" 2> /dev/null)
|
||||
rc=$?
|
||||
set -e
|
||||
|
||||
if [ $rc -eq 0 ]; then
|
||||
output="$(echo "${raw_output}"| grep "\\(Program\\|Data\\):" | sed -e 's,^, - ,' && echo)"
|
||||
|
||||
PROGSIZE="$(echo "${output}" | grep "Program:" | cut -d: -f2 | awk '{print $1}')"
|
||||
|
||||
PERCENT="$(echo "${PROGSIZE}" "${MAX_PROG_SIZE}" | awk "{ printf \"%02.01f\", \$1 / \$2 * 100 }")"
|
||||
|
||||
# we want the sed there, doing with shell builtins would be worse.
|
||||
# shellcheck disable=SC2001 disable=SC1117
|
||||
echo "${output}" | sed -e "s/\(Program:.*\)(\([0-9\.]*%\) Full)/\1(${PERCENT}% Full)/"
|
||||
else
|
||||
echo "Unable to determine image size."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
find_sketch () {
|
||||
if [ -z "${SKETCH}" ]; then
|
||||
echo "SKETCH needs to be set before including this file!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SKETCH_DIR="${SKETCH}"
|
||||
SKETCH_BASE_NAME=$(basename "$SKETCH")
|
||||
SKETCH_FILE_NAME="${SKETCH_BASE_NAME}.ino"
|
||||
|
||||
for path in "${SKETCH_DIR}" \
|
||||
"src" \
|
||||
"."; do
|
||||
if [ -f "${path}/${SKETCH_FILE_NAME}" ]; then
|
||||
SKETCH_DIR="${path}"
|
||||
SKETCH_FILE_PATH=$(absolute_filename "${SKETCH_DIR}/${SKETCH_FILE_NAME}")
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "I couldn't find your sketch (.ino file)" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
prompt_before_flashing () {
|
||||
flashing_instructions=$(get_arduino_pref 'build.flashing_instructions')
|
||||
|
||||
if [ "x${flashing_instructions}x" = "xx" ]; then
|
||||
flashing_instructions="If your keyboard needs you to do something to put it in flashing mode, do that now."
|
||||
fi
|
||||
|
||||
printf '%b\n\n' "${flashing_instructions}"
|
||||
echo ""
|
||||
echo "When you're ready to proceed, press 'Enter'."
|
||||
|
||||
# We do not want to permit line continuations here. We just want a newline.
|
||||
# shellcheck disable=SC2162
|
||||
read
|
||||
}
|
||||
|
||||
flash () {
|
||||
compile "$@"
|
||||
|
||||
# Check to see if we can see a keyboard bootloader port.
|
||||
# If we -can-, then we should skip over the "reset to bootloader" thing
|
||||
find_bootloader_ports
|
||||
if [ -z "${DEVICE_PORT_BOOTLOADER}" ]; then
|
||||
prompt_before_flashing
|
||||
|
||||
# This is defined in the (optional) user config.
|
||||
# shellcheck disable=SC2154
|
||||
${preFlash_HOOKS}
|
||||
|
||||
# If we're -not- doing a manual reset, then try to do it automatically
|
||||
if [ -z "${MANUAL_RESET}" ]; then
|
||||
reset_device
|
||||
sleep 2
|
||||
find_bootloader_ports
|
||||
# Otherwise, poll for a bootloader port.
|
||||
else
|
||||
wait_for_bootloader_port
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
check_bootloader_port_and_flash
|
||||
|
||||
# This is defined in the (optional) user config.
|
||||
# shellcheck disable=SC2154
|
||||
${postFlash_HOOKS}
|
||||
}
|
||||
|
||||
wait_for_bootloader_port() {
|
||||
declare -i tries
|
||||
tries=15
|
||||
|
||||
while [ "$tries" -gt 0 ] && [ -z "${DEVICE_PORT_BOOTLOADER}" ]; do
|
||||
sleep 1
|
||||
printf "."
|
||||
find_bootloader_ports
|
||||
# the variable annotations do appear to be necessary
|
||||
# shellcheck disable=SC2004
|
||||
tries=$(($tries-1))
|
||||
done
|
||||
|
||||
if [ "$tries" -gt 0 ]; then
|
||||
echo "Found."
|
||||
else
|
||||
echo "Timed out."
|
||||
fi
|
||||
}
|
||||
|
||||
check_bootloader_port () {
|
||||
if [ -z "${DEVICE_PORT_BOOTLOADER}" ]; then
|
||||
echo "Unable to detect a keyboard in bootloader mode."
|
||||
echo "You may need to hold a key or hit a reset button."
|
||||
echo "Please check your keyboard's documentation"
|
||||
return 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
check_bootloader_port_and_flash () {
|
||||
|
||||
if ! check_bootloader_port; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Flashing your keyboard:"
|
||||
|
||||
# If the flash fails, try a second time
|
||||
if ! flash_over_usb; then
|
||||
sleep 2
|
||||
if ! flash_over_usb; then
|
||||
if [ "${ARDUINO_VERBOSE}" != "-verbose" ]; then
|
||||
echo "Something went wrong."
|
||||
echo "You might want to try flashing again with the VERBOSE environment variable set"
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
echo "Keyboard flashed successfully!"
|
||||
return 0
|
||||
}
|
||||
|
||||
flash_over_usb () {
|
||||
|
||||
FLASH_CMD=$(${AVRDUDE} \
|
||||
-C "${AVRDUDE_CONF}" \
|
||||
-p"${MCU}" \
|
||||
-cavr109 \
|
||||
-D \
|
||||
-P "${DEVICE_PORT_BOOTLOADER}" \
|
||||
-b57600 \
|
||||
"-Uflash:w:${HEX_FILE_PATH}:i")
|
||||
|
||||
if [ "${ARDUINO_VERBOSE}" != "-verbose" ]; then
|
||||
${FLASH_CMD} 2>&1 |grep -v ^avrdude | grep -v '^$' |grep -v '^ ' | grep -vi programmer
|
||||
return "${PIPESTATUS[0]}"
|
||||
else
|
||||
${FLASH_CMD}
|
||||
return $?
|
||||
fi
|
||||
}
|
||||
|
||||
flash_from_bootloader() {
|
||||
compile "$@"
|
||||
prompt_before_flashing
|
||||
find_bootloader_ports
|
||||
check_bootloader_port_and_flash
|
||||
}
|
||||
|
||||
program() {
|
||||
compile "$@"
|
||||
prompt_before_flashing
|
||||
flash_with_programmer
|
||||
}
|
||||
|
||||
flash_with_programmer() {
|
||||
${AVRDUDE} -v \
|
||||
-C "${AVRDUDE_CONF}" \
|
||||
-p"${MCU}" \
|
||||
-cusbtiny \
|
||||
-D \
|
||||
-B 1 \
|
||||
"-Uflash:w:${HEX_FILE_PATH}:i"
|
||||
}
|
||||
|
||||
find_bootloader_path() {
|
||||
BOOTLOADER_FILE=$( get_arduino_pref 'bootloader.file' )
|
||||
: "${BOOTLOADER_FILE:=caterina/Caterina.hex}"
|
||||
: "${BOOTLOADER_PATH:=${BOARD_HARDWARE_PATH}/keyboardio/avr/bootloaders/${BOOTLOADER_FILE}}"
|
||||
}
|
||||
|
||||
|
||||
hex_with_bootloader () {
|
||||
compile
|
||||
|
||||
find_bootloader_path
|
||||
|
||||
awk '/^:00000001FF/ == 0' "${HEX_FILE_PATH}" > "${HEX_FILE_WITH_BOOTLOADER_PATH}"
|
||||
echo "Using ${BOOTLOADER_PATH}"
|
||||
${MD5} "${BOOTLOADER_PATH}"
|
||||
cat "${BOOTLOADER_PATH}" >> "${HEX_FILE_WITH_BOOTLOADER_PATH}"
|
||||
ln -sf -- "${OUTPUT_FILE_PREFIX}-with-bootloader.hex" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest-with-bootloader.hex"
|
||||
cat <<- EOF
|
||||
|
||||
Combined firmware and bootloader are now at ${HEX_FILE_WITH_BOOTLOADER_PATH}
|
||||
Make sure you have the bootloader version you expect.
|
||||
|
||||
And TEST THIS ON REAL HARDWARE BEFORE YOU GIVE IT TO ANYONE
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
build () {
|
||||
compile "$@"
|
||||
size "$@"
|
||||
}
|
||||
|
||||
|
||||
prepare_ccache () {
|
||||
build_paths
|
||||
enable_ccache
|
||||
}
|
||||
compile () {
|
||||
find_sketch
|
||||
build_version
|
||||
build_paths
|
||||
build_filenames
|
||||
# If the hex file is older than the sketch file, or the hex file does not exist
|
||||
# then rebuild. This is not as correct as letting make check our dependencies
|
||||
# But it's less broken for most user use cases
|
||||
# TODO(anyone): Make this suck less
|
||||
if [ "${HEX_FILE_PATH}" -ot "${SKETCH_FILE_PATH}" ]; then
|
||||
do_compile "$@"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
|
||||
do_compile () {
|
||||
prepare_ccache
|
||||
|
||||
install -d "${OUTPUT_PATH}"
|
||||
|
||||
echo "Building ${SKETCH_FILE_PATH}"
|
||||
|
||||
# This is defined in the (optional) user config.
|
||||
# shellcheck disable=SC2154
|
||||
${compile_HOOKS}
|
||||
|
||||
if [ -d "${ARDUINO_LOCAL_LIB_PATH}/libraries" ]; then
|
||||
# shellcheck disable=SC2089
|
||||
# We want literal backslashes here, not arrays.
|
||||
local_LIBS="-libraries \"${ARDUINO_LOCAL_LIB_PATH}/libraries\""
|
||||
fi
|
||||
|
||||
ARDUINO_PACKAGES=""
|
||||
if [ -d "${ARDUINO_PACKAGE_PATH}" ]; then
|
||||
# shellcheck disable=SC2089
|
||||
# We want literal backslashes here, not arrays.
|
||||
ARDUINO_PACKAGES="-hardware \"${ARDUINO_PACKAGE_PATH}\""
|
||||
fi
|
||||
|
||||
SAVED_BOARD="${BOARD}"
|
||||
SAVED_FQBN="${FQBN}"
|
||||
if [ -e "${SKETCH_DIR}/.kaleidoscope-builder.conf" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
BOARD="$(. "${SKETCH_DIR}"/.kaleidoscope-builder.conf && echo "${BOARD}")"
|
||||
# shellcheck disable=SC1090
|
||||
FQBN="$(. "${SKETCH_DIR}"/.kaleidoscope-builder.conf && echo "${FQBN}")"
|
||||
if [ -n "${BOARD}" ]; then
|
||||
if [ -z "${ARCH}" ]; then
|
||||
FQBN="keyboardio:avr:${BOARD}"
|
||||
else
|
||||
FQBN="keyboardio:${ARCH}:${BOARD}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
_CMD_CXX="${CXX:-${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}${COMPILER_SUFFIX}}"
|
||||
_CMD_CC="${CC:-${COMPILER_PREFIX}${C_COMPILER_BASENAME}${COMPILER_SUFFIX}}"
|
||||
_CMD_AR="${AR:-${COMPILER_PREFIX}${AR_BASENAME}${COMPILER_SUFFIX}}"
|
||||
|
||||
# SC2091: We do not care if quotes or backslashes are not respected.
|
||||
# SC2086: We want word splitting.
|
||||
# shellcheck disable=SC2086,SC2090
|
||||
"${ARDUINO_BUILDER}" \
|
||||
-compile \
|
||||
${ARDUINO_PACKAGES} \
|
||||
-hardware "${ARDUINO_PATH}/hardware" \
|
||||
-hardware "${BOARD_HARDWARE_PATH}" \
|
||||
${ARDUINO_TOOLS_FLAG:+"${ARDUINO_TOOLS_FLAG}"} ${ARDUINO_TOOLS_PARAM:+"${ARDUINO_TOOLS_PARAM}"} \
|
||||
-tools "${ARDUINO_BUILDER_TOOLS_PATH}" \
|
||||
-fqbn "${FQBN}" \
|
||||
-libraries "." \
|
||||
-libraries "${KALEIDOSCOPE_DIR}" \
|
||||
-libraries "${BOARD_HARDWARE_PATH}/.." \
|
||||
${local_LIBS} \
|
||||
${EXTRA_BUILDER_ARGS} \
|
||||
-build-cache "${CORE_CACHE_PATH}" \
|
||||
-build-path "${BUILD_PATH}" \
|
||||
-ide-version "${ARDUINO_IDE_VERSION}" \
|
||||
-built-in-libraries "${ARDUINO_PATH}/libraries" \
|
||||
-prefs "compiler.cpp.extra_flags=${ARDUINO_CFLAGS} ${LOCAL_CFLAGS}" \
|
||||
-prefs "compiler.path=${COMPILER_PATH}" \
|
||||
-prefs "compiler.c.cmd=${_CMD_CC}" \
|
||||
-prefs "compiler.cpp.cmd=${_CMD_CXX}" \
|
||||
-prefs "compiler.ar.cmd=${_CMD_AR}" \
|
||||
-prefs "compiler.c.elf.cmd=${_CMD_CXX}" \
|
||||
$CCACHE_ENABLE \
|
||||
-warnings all \
|
||||
${ARDUINO_VERBOSE} \
|
||||
${ARDUINO_AVR_GCC_PREFIX_PARAM} \
|
||||
"${SKETCH_FILE_PATH}"
|
||||
|
||||
if [ -z "${LIBONLY}" ]; then
|
||||
cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.hex" "${HEX_FILE_PATH}"
|
||||
cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.elf" "${ELF_FILE_PATH}"
|
||||
ln -sf "${OUTPUT_FILE_PREFIX}.hex" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.hex"
|
||||
ln -sf "${OUTPUT_FILE_PREFIX}.elf" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.elf"
|
||||
else
|
||||
cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.a" "${LIB_FILE_PATH}"
|
||||
ln -sf "${OUTPUT_FILE_PREFIX}.a" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.a"
|
||||
fi
|
||||
|
||||
if [ "${ARDUINO_VERBOSE}" = "-verbose" ]; then
|
||||
echo "Build artifacts can be found in ${BUILD_PATH}";
|
||||
fi
|
||||
|
||||
BOARD="${SAVED_BOARD}"
|
||||
FQBN="${SAVED_FQBN}"
|
||||
}
|
||||
|
||||
find_all_sketches () {
|
||||
for plugin in ./*.ino \
|
||||
$([ -d examples ] && find examples -name '*.ino') \
|
||||
src/*.ino; do
|
||||
if [ -d "$(dirname "${plugin}")" ] || [ -f "${plugin}" ]; then
|
||||
p="$(basename "${plugin}" .ino)"
|
||||
if [ "${p}" != '*' ]; then
|
||||
case "${plugin}" in
|
||||
examples/*/${p}/${p}.ino)
|
||||
echo "${plugin}" | sed -e "s,examples/,," | sed -e "s,/${p}\\.ino,,"
|
||||
;;
|
||||
*)
|
||||
echo "${p}"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
done | sort
|
||||
}
|
||||
|
||||
build_all () {
|
||||
plugins="$(find_all_sketches)"
|
||||
|
||||
for plugin in ${plugins}; do
|
||||
export SKETCH="${plugin}"
|
||||
$0 "${plugin}" build
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
compile_all () {
|
||||
plugins="$(find_all_sketches)"
|
||||
|
||||
for plugin in ${plugins}; do
|
||||
export SKETCH="${plugin}"
|
||||
$0 "${plugin}" compile
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
size () {
|
||||
compile
|
||||
|
||||
echo "- Size: ${ELF_FILE_PATH}"
|
||||
# shellcheck disable=SC2086
|
||||
firmware_size "${AVR_SIZE}" ${AVR_SIZE_FLAGS} "${ELF_FILE_PATH}"
|
||||
echo
|
||||
}
|
||||
|
||||
size_map () {
|
||||
compile
|
||||
|
||||
"${AVR_NM}" --size-sort -C -r -l -t decimal "${ELF_FILE_PATH}"
|
||||
}
|
||||
|
||||
disassemble () {
|
||||
compile
|
||||
|
||||
"${AVR_OBJDUMP}" -C -d "${ELF_FILE_PATH}"
|
||||
}
|
||||
|
||||
decompile () {
|
||||
disassemble
|
||||
}
|
||||
|
||||
clean () {
|
||||
find_sketch
|
||||
build_paths
|
||||
rm -rf -- "${OUTPUT_PATH}"
|
||||
rm -rf -- "${BUILD_PATH}"
|
||||
kaleidoscope_dir="$(dirname "$0")/.."
|
||||
if [ -d "${kaleidoscope_dir}/testing/googletest/build" ]; then
|
||||
( cd "${kaleidoscope_dir}/testing/googletest/build" &&
|
||||
cmake .. &&
|
||||
make clean)
|
||||
fi
|
||||
}
|
||||
|
||||
reset_device() {
|
||||
find_device_port
|
||||
check_device_port
|
||||
reset_device_cmd
|
||||
}
|
||||
|
||||
check_device_port () {
|
||||
if [ -z "$DEVICE_PORT" ]; then
|
||||
cat <<EOF >&2
|
||||
|
||||
I couldn't autodetect the keyboard's serial port.
|
||||
|
||||
If you see this message and your keyboard is connected to your computer,
|
||||
it may mean that our serial port detection logic is buggy or incomplete.
|
||||
In that case, please report this issue at:
|
||||
https://github.com/keyboardio/Kaleidoscope
|
||||
EOF
|
||||
exit 1
|
||||
elif echo "$DEVICE_PORT" | grep -q '[[:space:]]'; then
|
||||
cat <<EOF >&2
|
||||
Unexpected whitespace found in detected serial port:
|
||||
|
||||
$DEVICE_PORT
|
||||
|
||||
If you see this message, it means that our serial port
|
||||
detection logic is buggy or incomplete.
|
||||
|
||||
Please report this issue at:
|
||||
https://github.com/keyboardio/Kaleidoscope
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -w "$DEVICE_PORT" ]; then
|
||||
cat <<EOF >&2
|
||||
|
||||
In order to update your keyboard's firmware you need to have permission
|
||||
to write to its serial port $DEVICE_PORT.
|
||||
|
||||
It appears that you do not have this permission:
|
||||
|
||||
$(ls -l "$DEVICE_PORT")
|
||||
|
||||
This may be because you're not in the correct unix group:
|
||||
|
||||
$(stat -c %G "$DEVICE_PORT").
|
||||
|
||||
You are currently in the following groups:
|
||||
|
||||
$(id -Gn)
|
||||
|
||||
Please ensure you have followed the instructions on setting up your
|
||||
account to be in the right group:
|
||||
|
||||
https://github.com/keyboardio/Kaleidoscope/wiki/Install-Arduino-support-on-Linux
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
deprecation_message() {
|
||||
echo "kaleidoscope-builder is deprecated and will be removed"
|
||||
echo "by February 15, 2021"
|
||||
echo ""
|
||||
echo "To switch to the new build system, replace your sketch's"
|
||||
echo "Makefile with a copy of:"
|
||||
echo ""
|
||||
echo "${KALEIDOSCOPE_DIR}/etc/Makefile.sketch"
|
||||
echo ""
|
||||
}
|
||||
|
||||
usage () {
|
||||
cat <<- EOF
|
||||
Usage: $0 SKETCH commands...
|
||||
|
||||
Runs all of the commands in the context of the Sketch.
|
||||
|
||||
Available commands:
|
||||
|
||||
help
|
||||
This help screen.
|
||||
|
||||
compile
|
||||
Compiles the sketch.
|
||||
|
||||
size
|
||||
Reports the size of the compiled sketch.
|
||||
|
||||
build
|
||||
Runs compile and report-size.
|
||||
|
||||
clean
|
||||
Cleans up the output directory.
|
||||
|
||||
size-map
|
||||
Displays the size map for the sketch.
|
||||
|
||||
disassemble
|
||||
Decompile the sketch.
|
||||
|
||||
reset-device
|
||||
Reset the device.
|
||||
|
||||
flash
|
||||
Flashes the firmware using avrdude.
|
||||
|
||||
build-all
|
||||
Build all Sketches we can find.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
KALEIDOSCOPE_DIR="$(cd "$(dirname "$0")"/..; pwd)"
|
||||
# shellcheck disable=SC2034
|
||||
KALEIDOSCOPE_BIN_DIR="${KALEIDOSCOPE_DIR}/bin/"
|
||||
|
||||
|
||||
help () {
|
||||
usage
|
||||
}
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## Parse the command-line
|
||||
## - anything that has a =, is an env var
|
||||
## - from the remaining stuff, the first one is the Library/Sketch
|
||||
## - everything else are commands
|
||||
##
|
||||
## - if there is only one argument, that's a command
|
||||
|
||||
|
||||
deprecation_message
|
||||
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
export SOURCEDIR="$(pwd)"
|
||||
|
||||
|
||||
for conf_file in \
|
||||
"${HOME}/.kaleidoscope-builder.conf" \
|
||||
"${SOURCEDIR}/.kaleidoscope-builder.conf" \
|
||||
"${SOURCEDIR}/kaleidoscope-builder.conf" \
|
||||
"${KALEIDOSCOPE_DIR}/etc/kaleidoscope-builder.conf"; do
|
||||
if [ -e "${conf_file}" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "${conf_file}"
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
|
||||
|
||||
if [ -n "${VERBOSE}" ] && [[ "${VERBOSE}" -gt 0 ]]; then
|
||||
ARDUINO_VERBOSE="-verbose"
|
||||
else
|
||||
ARDUINO_VERBOSE="-quiet"
|
||||
fi
|
||||
|
||||
cmds=""
|
||||
|
||||
## Export vars
|
||||
for i in $(seq 1 $#); do
|
||||
v="$1"
|
||||
shift
|
||||
|
||||
case "${v}" in
|
||||
*=*)
|
||||
# Exporting an expansion is *precisely* what we want here.
|
||||
# shellcheck disable=SC2086,SC2163
|
||||
export ${v}
|
||||
;;
|
||||
*)
|
||||
cmds="${cmds} ${v}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Word splitting is desired here.
|
||||
# shellcheck disable=SC2086
|
||||
set -- ${cmds}
|
||||
|
||||
if [ $# -eq 1 ]; then
|
||||
cmd="$(echo "$1" | tr '-' '_')"
|
||||
${cmd}
|
||||
exit $?
|
||||
fi
|
||||
|
||||
SKETCH="$1"
|
||||
shift
|
||||
|
||||
if [ "${SKETCH}" = "default" ]; then
|
||||
SKETCH="${DEFAULT_SKETCH}"
|
||||
fi
|
||||
|
||||
cmds=""
|
||||
|
||||
# shellcheck disable=2034
|
||||
for i in $(seq 1 $#); do
|
||||
cmds="${cmds} $(echo "$1" | tr '-' '_')"
|
||||
shift
|
||||
done
|
||||
|
||||
for cmd in ${cmds}; do
|
||||
${cmd}
|
||||
done
|
@ -0,0 +1,15 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# This script sets all of the files inside src and example to have mtimes
|
||||
# that match the times of the last git commit that touched each file
|
||||
|
||||
# This can be useful when build tools depend on file timestamps to
|
||||
# make caching decisions
|
||||
|
||||
find src examples -type f -exec sh -c '
|
||||
timestamp=$(git log --pretty=format:%ad --date=format:%Y%m%d%H%M.%S -n 1 HEAD "$1" 2> /dev/null)
|
||||
if [ "x$timestamp" != "x" ]; then
|
||||
touch -t "$timestamp" "$1"
|
||||
fi
|
||||
' sh {} \;
|
||||
|
@ -1,25 +0,0 @@
|
||||
# Docker
|
||||
|
||||
It's possible to use Docker to run Kaleidoscope's test suite.
|
||||
|
||||
## Running tests in Docker
|
||||
|
||||
```
|
||||
# make docker-simulator-tests
|
||||
```
|
||||
|
||||
## Cleaning out stale data in the Docker image:
|
||||
|
||||
```
|
||||
# make docker-clean
|
||||
```
|
||||
|
||||
|
||||
## Removing the Kaleidoscope Docker image entirely:
|
||||
|
||||
```
|
||||
# docker volume rm kaleidoscope-persist
|
||||
# docker volume rm kaleidoscope-googletest-build
|
||||
# docker volume rm kaleidoscope-build
|
||||
# docker image rm kaleidoscope/docker
|
||||
```
|
@ -0,0 +1,274 @@
|
||||
# -*- shell-script -*-
|
||||
|
||||
## NEEDS: SKETCH
|
||||
## Should be included when the current directory is the dir of the Sketch.
|
||||
|
||||
SKETCH="${SKETCH:-${DEFAULT_SKETCH}}"
|
||||
|
||||
########
|
||||
######## Keyboard hardware definitions
|
||||
########
|
||||
|
||||
: "${BOARD:=model01}"
|
||||
: "${MCU:=atmega32u4}"
|
||||
|
||||
if [ -z "${ARCH}" ]; then
|
||||
ARCH=$(echo "${FQBN}" | sed -n -e 's/^[^:]\+:\([^:]\+\).*/\1/p')
|
||||
fi
|
||||
|
||||
if [ "${ARCH}" = "virtual" ]; then
|
||||
: "${FQBN:=keyboardio:virtual:${BOARD}}"
|
||||
|
||||
# Set the compiler path for virtual builds
|
||||
#
|
||||
if [ -z "${COMPILER_PATH}" ]; then
|
||||
COMPILER_PATH="/usr/bin/"
|
||||
fi
|
||||
|
||||
COMPILER_PREFIX=""
|
||||
else
|
||||
ARCH="avr"
|
||||
fi
|
||||
|
||||
if [ -z "${FQBN}" ]; then
|
||||
: "${FQBN:=keyboardio:avr:${BOARD}}"
|
||||
fi
|
||||
|
||||
########
|
||||
######## Host OS specific commands
|
||||
########
|
||||
|
||||
## Platform-specific overrides
|
||||
# Shamelessly stolen from git's Makefile
|
||||
uname_S=$(uname -s 2>/dev/null || echo not)
|
||||
uname_O=$(uname -o 2>/dev/null || echo not)
|
||||
|
||||
|
||||
find_device_vid_pid() {
|
||||
: ${VID:=$(get_arduino_pref 'build.vid')}
|
||||
: ${SKETCH_PID:=$(get_arduino_pref 'build.pid')}
|
||||
: ${BOOTLOADER_PID:=$(get_arduino_pref 'bootloader.pid')}
|
||||
: ${BOOTLOADER_VID:=$(get_arduino_pref 'bootloader.vid')}
|
||||
}
|
||||
|
||||
|
||||
get_arduino_pref() {
|
||||
pref=$1
|
||||
# Strip the preference name. And then strip leading and trailing quotations
|
||||
MESSAGE=$(dump_arduino_prefs | grep --max-count=1 ${pref}= | sed -e s/^.*${pref}=// -e 's/^"//' -e 's/"$//')
|
||||
echo $MESSAGE
|
||||
}
|
||||
|
||||
|
||||
dump_arduino_prefs() {
|
||||
# SKETCH and -build-path in this command are here because of a bug introduced in Arduino 1.8.10
|
||||
# https://github.com/arduino/arduino-builder/issues/341
|
||||
|
||||
if [ "x${_ARDUINO_PREFS}x" == "xx" ]; then
|
||||
|
||||
_ARDUINO_PREFS=$("${ARDUINO_BUILDER}" \
|
||||
-hardware "${ARDUINO_PATH}/hardware" \
|
||||
-hardware "${BOARD_HARDWARE_PATH}" \
|
||||
${ARDUINO_TOOLS_FLAG:+"${ARDUINO_TOOLS_FLAG}"} ${ARDUINO_TOOLS_PARAM:+"${ARDUINO_TOOLS_PARAM}"} \
|
||||
-tools "${ARDUINO_BUILDER_TOOLS_PATH}" \
|
||||
-fqbn "${FQBN}" \
|
||||
-build-path "${ARDUINO_PATH}" \
|
||||
-dump-prefs "${SKETCH_DIR}/${SKETCH}.ino" )
|
||||
fi
|
||||
echo "$_ARDUINO_PREFS"
|
||||
|
||||
}
|
||||
|
||||
find_device_port() {
|
||||
find_device_vid_pid
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-linux-udev"
|
||||
if [[ "${DEVICE_PORT}" = "" ]]; then
|
||||
DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID})"
|
||||
else
|
||||
echo "DEVICE_PORT=\"${DEVICE_PORT}\" predefined."
|
||||
fi
|
||||
}
|
||||
|
||||
reset_device_cmd() {
|
||||
if [ -z ${NO_RESET} ]; then
|
||||
stty -F ${DEVICE_PORT} 1200 hupcl
|
||||
fi
|
||||
}
|
||||
|
||||
find_bootloader_ports() {
|
||||
find_device_vid_pid
|
||||
: "${BOOTLOADER_VID:=${VID}}"
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-linux-udev"
|
||||
if [[ "${DEVICE_PORT_BOOTLOADER}" = "" ]]; then
|
||||
DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID})"
|
||||
else
|
||||
echo "DEVICE_PORT_BOOTLOADER=\"${DEVICE_PORT_BOOTLOADER}\" predefined."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
MD5="md5sum"
|
||||
|
||||
if [ "${uname_S}" = "Darwin" ]; then
|
||||
|
||||
find_device_port() {
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-macos"
|
||||
DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID})"
|
||||
}
|
||||
|
||||
reset_device_cmd() {
|
||||
/bin/stty -f ${DEVICE_PORT} 1200
|
||||
}
|
||||
|
||||
: "${ARDUINO_PATH:=/Applications/Arduino.app/Contents/Java/}"
|
||||
: "${ARDUINO_PACKAGE_PATH:=${HOME}/Library/Arduino15/packages}"
|
||||
: "${ARDUINO_LOCAL_LIB_PATH:=${HOME}/Documents/Arduino}"
|
||||
|
||||
MD5="md5"
|
||||
|
||||
find_bootloader_ports() {
|
||||
find_device_vid_pid
|
||||
: "${BOOTLOADER_VID:=${VID}}"
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-macos"
|
||||
if [[ "${DEVICE_PORT_BOOTLOADER}" = "" ]]; then
|
||||
DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID})"
|
||||
else
|
||||
echo "DEVICE_PORT_BOOTLOADER=\"${DEVICE_PORT_BOOTLOADER}\" predefined."
|
||||
fi
|
||||
}
|
||||
|
||||
elif [ "${uname_S}" = "FreeBSD" ]; then
|
||||
|
||||
find_device_port() {
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-freebsd"
|
||||
DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER})"
|
||||
}
|
||||
|
||||
reset_device_cmd() {
|
||||
/bin/stty -f ${DEVICE_PORT} 1200
|
||||
}
|
||||
|
||||
MD5="md5"
|
||||
: "${AVR_SIZE:=/usr/local/bin/avr-size}"
|
||||
: "${AVR_NM:=/usr/local/bin/avr-nm}"
|
||||
: "${AVR_OBJDUMP:=/usr/local/bin/avr-objdump}"
|
||||
: "${AVRDUDE:=/usr/local/bin/avrdude}"
|
||||
: "${AVRDUDE_CONF:=/usr/local/etc/avrdude.conf}"
|
||||
: "${ARDUINO_BUILDER:=/usr/local/bin/arduino-builder}"
|
||||
|
||||
find_bootloader_ports() {
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCPE_BIN_DIR}/find-device-port-freebsd"
|
||||
DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER})"
|
||||
}
|
||||
|
||||
if [ "${ARCH}" = "virtual" ]; then
|
||||
: "${COMPILER_PATH:=/usr/local/bin/}"
|
||||
fi
|
||||
|
||||
elif [ "${uname_O}" = "Cygwin" ]; then
|
||||
# The Windows arduino-builder.exe doesn't understand being told to exec against Cygwin symlinks
|
||||
CCACHE_NOT_SUPPORTED=1
|
||||
|
||||
# Note: the default ARDUINO_PATH here is the default Arduino installation path on Windows, but it won't actually
|
||||
# work in practice right now since we haven't fixed all bugs related to interpretation of spaces in these paths.
|
||||
#
|
||||
# It's important that all of these be underneath /cygdrive/c so they can be converted to Windows paths that the
|
||||
# Windows Arduino binaries can understand.
|
||||
: "${ARDUINO_PATH:=/cygdrive/c/Program\ Files\ (x86)/Arduino}"
|
||||
: "${ARDUINO_PACKAGE_PATH:=/cygdrive/c/Users/${USER}/AppData/Local/Arduino15/packages}"
|
||||
: "${ARDUINO_LOCAL_LIB_PATH:=/cygdrive/c/Users/${USER}/Arduino}"
|
||||
TMPDIR="${ARDUINO_LOCAL_LIB_PATH:-/cygdrive/c/Users/${USER}/AppData/Local/Temp}"
|
||||
|
||||
# We need to prevent Windows executables from being passed parameters that are absolute paths, since they won't
|
||||
# be interpretable when of the form /cygdrive/c/foo. To work around this, we set the common path root variables
|
||||
# to use relative paths instead of absolute paths, since those have mostly platform-agnostic behavior.
|
||||
#
|
||||
# Note that this trick requires that all of these paths exist on the same drive letter as the current directory,
|
||||
# since otherwise even the relative paths would include Cygwin-specific components. So...
|
||||
if [[ $(realpath --relative-base=/cygdrive/c .) == /* ]]; then
|
||||
echo "kaleidoscope-builder's Cygwin support is currently limited to running from within /cygdrive/c"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ARDUINO_PATH="$(realpath --relative-to=./ ${ARDUINO_PATH})"
|
||||
ARDUINO_PACKAGE_PATH="$(realpath --relative-to=./ ${ARDUINO_PACKAGE_PATH})"
|
||||
ARDUINO_LOCAL_LIB_PATH="$(realpath --relative-to=./ ${ARDUINO_LOCAL_LIB_PATH})"
|
||||
TMPDIR="$(realpath --relative-to=./ ${ARDUINO_PATH})"
|
||||
|
||||
find_device_port() {
|
||||
find_device_vid_pid
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-windows.ps1"
|
||||
DEVICE_PORT="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID} -Format Cygwin)"
|
||||
DEVICE_COM_PORT="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID} -Format COM)"
|
||||
}
|
||||
|
||||
reset_device_cmd() {
|
||||
cmd /c mode ${DEVICE_COM_PORT} baud=1200
|
||||
}
|
||||
|
||||
find_bootloader_ports() {
|
||||
find_device_vid_pid
|
||||
: "${BOOTLOADER_VID:=${VID}}"
|
||||
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-windows.ps1"
|
||||
DEVICE_PORT_BOOTLOADER="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID} -Format COM)"
|
||||
}
|
||||
|
||||
fi
|
||||
|
||||
######
|
||||
###### Arduino tools configuration
|
||||
######
|
||||
|
||||
: "${ARDUINO_PATH:=/usr/local/arduino}"
|
||||
: "${ARDUINO_LOCAL_LIB_PATH:=${HOME}/Arduino}"
|
||||
: "${ARDUINO_TOOLS_PATH:=${ARDUINO_PATH}/hardware/tools}"
|
||||
: "${ARDUINO_PACKAGE_PATH:=${HOME}/.arduino15/packages}"
|
||||
: "${ARDUINO_BUILDER:=${ARDUINO_PATH}/arduino-builder}"
|
||||
: "${ARDUINO_BUILDER_TOOLS_PATH:=${ARDUINO_PATH}/tools-builder}"
|
||||
|
||||
ARDUINO_IDE_VERSION="10607"
|
||||
|
||||
######
|
||||
###### Executable paths
|
||||
######
|
||||
|
||||
# Allow the compiler path to be empty for virtual builds
|
||||
: "${COMPILER_PATH=${ARDUINO_TOOLS_PATH}/avr/bin/}"
|
||||
|
||||
COMPILER_SUFFIX=""
|
||||
|
||||
C_COMPILER_BASENAME=$(basename ${CC:-gcc})
|
||||
CXX_COMPILER_BASENAME=$(basename ${CXX:-g++})
|
||||
AR_BASENAME=$(basename ${AR:-ar})
|
||||
OBJCOPY_BASENAME=$(basename ${OBJCOPY:-objcopy})
|
||||
|
||||
# Allow the compiler prefix to be empty for virtual builds
|
||||
COMPILER_PREFIX="${COMPILER_PREFIX-avr-}"
|
||||
: "${AVR_SIZE:=${COMPILER_PATH}/${COMPILER_PREFIX}size}"
|
||||
: "${AVR_SIZE_FLAGS:=-C --mcu=${MCU}}"
|
||||
: "${AVR_OBJDUMP:=${COMPILER_PATH}/${COMPILER_PREFIX}objdump}"
|
||||
: "${AVR_OBJCOPY:=${COMPILER_PATH}/${COMPILER_PREFIX}objcopy}"
|
||||
: "${AVR_NM:=${COMPILER_PATH}/${COMPILER_PREFIX}nm}"
|
||||
: "${AVR_AR:=${COMPILER_PATH}/${COMPILER_PREFIX}ar}"
|
||||
: "${AVR_GCC:=${COMPILER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}}"
|
||||
AVR_GPLUSPLUS="${AVR_GCC:-${COMPILER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}}"
|
||||
|
||||
|
||||
|
||||
: "${AVRDUDE:=${ARDUINO_TOOLS_PATH}/avr/bin/avrdude}"
|
||||
: "${AVRDUDE_CONF:=${ARDUINO_TOOLS_PATH}/avr/etc/avrdude.conf}"
|
||||
|
||||
######
|
||||
###### Source files and dependencies
|
||||
######
|
||||
|
||||
: "${BOARD_HARDWARE_PATH:=${ARDUINO_LOCAL_LIB_PATH}/hardware}"
|
||||
|
||||
if [ ! -z "${ARDUINO_TOOLS_PATH}" ]; then
|
||||
ARDUINO_TOOLS_PARAM="${ARDUINO_TOOLS_PATH}"
|
||||
ARDUINO_TOOLS_FLAG="-tools"
|
||||
fi
|
||||
|
||||
if [ ! -z "${AVR_GCC_PREFIX}" ]; then
|
||||
ARDUINO_AVR_GCC_PREFIX_PARAM="-prefs \"runtime.tools.avr-gcc.path=${AVR_GCC_PREFIX}\""
|
||||
fi
|
@ -0,0 +1,2 @@
|
||||
DEFAULT_SKETCH="Atreus"
|
||||
BOARD="keyboardio_atreus"
|
@ -1,618 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
// Copyright 2016-2022 Keyboardio, inc. <jesse@keyboard.io>
|
||||
// See "LICENSE" for license details
|
||||
|
||||
/**
|
||||
* These #include directives pull in the Kaleidoscope firmware core,
|
||||
* as well as the Kaleidoscope plugins we use in the Model 100's firmware
|
||||
*/
|
||||
|
||||
// The Kaleidoscope core
|
||||
#include "Kaleidoscope.h"
|
||||
|
||||
// Support for storing the keymap in EEPROM
|
||||
#include "Kaleidoscope-EEPROM-Settings.h"
|
||||
#include "Kaleidoscope-EEPROM-Keymap.h"
|
||||
|
||||
// Support for communicating with the host via a simple Serial protocol
|
||||
#include "Kaleidoscope-FocusSerial.h"
|
||||
|
||||
// Support for querying the firmware version via Focus
|
||||
#include "Kaleidoscope-FirmwareVersion.h"
|
||||
|
||||
// Support for keys that move the mouse
|
||||
// #include "Kaleidoscope-MouseKeys.h"
|
||||
|
||||
// Support for macros
|
||||
#include "Kaleidoscope-Macros.h"
|
||||
|
||||
// Support for controlling the keyboard's LEDs
|
||||
#include "Kaleidoscope-LEDControl.h"
|
||||
|
||||
// Support for "Numpad" mode, which is mostly just the Numpad specific LED mode
|
||||
// #include "Kaleidoscope-NumPad.h"
|
||||
|
||||
// Support for the "Boot greeting" effect, which pulses the 'LED' button for 10s
|
||||
// when the keyboard is connected to a computer (or that computer is powered on)
|
||||
#include "Kaleidoscope-LEDEffect-BootGreeting.h"
|
||||
|
||||
// Support for LED modes that set all LEDs to a single color
|
||||
#include "Kaleidoscope-LEDEffect-SolidColor.h"
|
||||
|
||||
// Support for an LED mode that makes all the LEDs 'breathe'
|
||||
#include "Kaleidoscope-LEDEffect-Breathe.h"
|
||||
|
||||
// Support for an LED mode that makes a red pixel chase a blue pixel across the keyboard
|
||||
#include "Kaleidoscope-LEDEffect-Chase.h"
|
||||
|
||||
// Support for LED modes that pulse the keyboard's LED in a rainbow pattern
|
||||
#include "Kaleidoscope-LEDEffect-Rainbow.h"
|
||||
|
||||
// Support for an LED mode that lights up the keys as you press them
|
||||
#include "Kaleidoscope-LED-Stalker.h"
|
||||
|
||||
// Support for an LED mode that prints the keys you press in letters 4px high
|
||||
#include "Kaleidoscope-LED-AlphaSquare.h"
|
||||
|
||||
// Support for shared palettes for other plugins, like Colormap below
|
||||
#include "Kaleidoscope-LED-Palette-Theme.h"
|
||||
|
||||
// Support for an LED mode that lets one configure per-layer color maps
|
||||
#include "Kaleidoscope-Colormap.h"
|
||||
|
||||
// Support for turning the LEDs off after a certain amount of time
|
||||
#include "Kaleidoscope-IdleLEDs.h"
|
||||
|
||||
// Support for setting and saving the default LED mode
|
||||
#include "Kaleidoscope-DefaultLEDModeConfig.h"
|
||||
|
||||
// Support for Keyboardio's internal keyboard testing mode
|
||||
#include "Kaleidoscope-HardwareTestMode.h"
|
||||
|
||||
// Support for host power management (suspend & wakeup)
|
||||
#include "Kaleidoscope-HostPowerManagement.h"
|
||||
|
||||
// Support for magic combos (key chords that trigger an action)
|
||||
#include "Kaleidoscope-MagicCombo.h"
|
||||
|
||||
// Support for USB quirks, like changing the key state report protocol
|
||||
#include "Kaleidoscope-USB-Quirks.h"
|
||||
|
||||
// Support for secondary actions on keys
|
||||
#include "Kaleidoscope-Qukeys.h"
|
||||
|
||||
// Support for one-shot modifiers and layer keys
|
||||
#include "Kaleidoscope-OneShot.h"
|
||||
#include "Kaleidoscope-Escape-OneShot.h"
|
||||
|
||||
// Support for dynamic, Chrysalis-editable macros
|
||||
#include "Kaleidoscope-DynamicMacros.h"
|
||||
|
||||
// Support for SpaceCadet keys
|
||||
#include "Kaleidoscope-SpaceCadet.h"
|
||||
|
||||
// Support for editable layer names
|
||||
#include "Kaleidoscope-LayerNames.h"
|
||||
|
||||
// Support for the GeminiPR Stenography protocol
|
||||
// #include "Kaleidoscope-Steno.h"
|
||||
|
||||
/** This 'enum' is a list of all the macros used by the Model 100's firmware
|
||||
* The names aren't particularly important. What is important is that each
|
||||
* is unique.
|
||||
*
|
||||
* These are the names of your macros. They'll be used in two places.
|
||||
* The first is in your keymap definitions. There, you'll use the syntax
|
||||
* `M(MACRO_NAME)` to mark a specific keymap position as triggering `MACRO_NAME`
|
||||
*
|
||||
* The second usage is in the 'switch' statement in the `macroAction` function.
|
||||
* That switch statement actually runs the code associated with a macro when
|
||||
* a macro key is pressed.
|
||||
*/
|
||||
|
||||
enum {
|
||||
MACRO_VERSION_INFO,
|
||||
MACRO_ANY,
|
||||
};
|
||||
|
||||
|
||||
/** The Model 100's key layouts are defined as 'keymaps'. By default, there are three
|
||||
* keymaps: The standard QWERTY keymap, the "Function layer" keymap and the "Numpad"
|
||||
* keymap.
|
||||
*
|
||||
* Each keymap is defined as a list using the 'KEYMAP_STACKED' macro, built
|
||||
* of first the left hand's layout, followed by the right hand's layout.
|
||||
*
|
||||
* Keymaps typically consist mostly of `Key_` definitions. There are many, many keys
|
||||
* defined as part of the USB HID Keyboard specification. You can find the names
|
||||
* (if not yet the explanations) for all the standard `Key_` defintions offered by
|
||||
* Kaleidoscope in these files:
|
||||
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keyboard.h
|
||||
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/consumerctl.h
|
||||
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/sysctl.h
|
||||
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keymaps.h
|
||||
*
|
||||
* Additional things that should be documented here include
|
||||
* using ___ to let keypresses fall through to the previously active layer
|
||||
* using XXX to mark a keyswitch as 'blocked' on this layer
|
||||
* using ShiftToLayer() and LockLayer() keys to change the active keymap.
|
||||
* keeping NUM and FN consistent and accessible on all layers
|
||||
*
|
||||
* The PROG key is special, since it is how you indicate to the board that you
|
||||
* want to flash the firmware. However, it can be remapped to a regular key.
|
||||
* When the keyboard boots, it first looks to see whether the PROG key is held
|
||||
* down; if it is, it simply awaits further flashing instructions. If it is
|
||||
* not, it continues loading the rest of the firmware and the keyboard
|
||||
* functions normally, with whatever binding you have set to PROG. More detail
|
||||
* here: https://community.keyboard.io/t/how-the-prog-key-gets-you-into-the-bootloader/506/8
|
||||
*
|
||||
* The "keymaps" data structure is a list of the keymaps compiled into the firmware.
|
||||
* The order of keymaps in the list is important, as the ShiftToLayer(#) and LockLayer(#)
|
||||
* macros switch to key layers based on this list.
|
||||
*
|
||||
*
|
||||
|
||||
* A key defined as 'ShiftToLayer(FUNCTION)' will switch to FUNCTION while held.
|
||||
* Similarly, a key defined as 'LockLayer(NUMPAD)' will switch to NUMPAD when tapped.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Layers are "0-indexed" -- That is the first one is layer 0. The second one is layer 1.
|
||||
* The third one is layer 2.
|
||||
* This 'enum' lets us use names like QWERTY, FUNCTION, and NUMPAD in place of
|
||||
* the numbers 0, 1 and 2.
|
||||
*
|
||||
*/
|
||||
|
||||
enum {
|
||||
PRIMARY,
|
||||
// NUMPAD,
|
||||
FUNCTION,
|
||||
ETC,
|
||||
}; // layers
|
||||
|
||||
|
||||
/**
|
||||
* To change your keyboard's layout from QWERTY to DVORAK or COLEMAK, comment out the line
|
||||
*
|
||||
* #define PRIMARY_KEYMAP_QWERTY
|
||||
*
|
||||
* by changing it to
|
||||
*
|
||||
* // #define PRIMARY_KEYMAP_QWERTY
|
||||
*
|
||||
* Then uncomment the line corresponding to the layout you want to use.
|
||||
*
|
||||
*/
|
||||
|
||||
// #define PRIMARY_KEYMAP_QWERTY
|
||||
// #define PRIMARY_KEYMAP_DVORAK
|
||||
// #define PRIMARY_KEYMAP_COLEMAK
|
||||
#define PRIMARY_KEYMAP_CUSTOM
|
||||
|
||||
|
||||
/* This comment temporarily turns off astyle's indent enforcement
|
||||
* so we can make the keymaps actually resemble the physical key layout better
|
||||
*/
|
||||
// clang-format off
|
||||
|
||||
KEYMAPS(
|
||||
|
||||
[PRIMARY] = KEYMAP_STACKED
|
||||
(___, ___, ___, ___, ___, ___, Key_LEDEffectNext,
|
||||
___, Key_Q, Key_W, Key_D, Key_F, Key_K, Key_Tab,
|
||||
___, Key_A, Key_S, Key_E, Key_T, Key_G,
|
||||
Key_Backtick, Key_Z, Key_X, Key_C, Key_V, Key_B, LCTRL(LALT(Key_LeftGui)),
|
||||
GUI_T(Tab), ALT_T(Backspace), CTL_T(Escape), Key_LeftShift,
|
||||
ShiftToLayer(FUNCTION),
|
||||
|
||||
M(MACRO_ANY), ___, Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___,
|
||||
Consumer_VolumeIncrement, Key_J, Key_U, Key_R, Key_L, Key_Semicolon, Key_Backslash,
|
||||
Key_Y, Key_N, Key_I, Key_O, Key_H, Key_Quote,
|
||||
Consumer_VolumeDecrement, Key_P, Key_M, Key_Comma, Key_Period, Key_Slash, ___,
|
||||
Key_RightShift, ALT_T(Enter), Key_Spacebar, GUI_T(Tab),
|
||||
ShiftToLayer(FUNCTION)),
|
||||
|
||||
[FUNCTION] = KEYMAP_STACKED
|
||||
(___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, ___,
|
||||
___, LSHIFT(Key_1), LSHIFT(Key_2), LSHIFT(Key_LeftBracket), LSHIFT(Key_RightBracket), LSHIFT(Key_Backslash), ___,
|
||||
LSHIFT(Key_7), LSHIFT(Key_3), LSHIFT(Key_4), LSHIFT(Key_9), LSHIFT(Key_0), Key_Backslash,
|
||||
LSHIFT(Key_Backtick), LSHIFT(Key_5), LSHIFT(Key_6), Key_LeftBracket, Key_RightBracket, LSHIFT(Key_8), ___,
|
||||
___, Key_Delete, ___, ___,
|
||||
___,
|
||||
|
||||
___, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11,
|
||||
___, Key_Equals, Key_7, Key_8, Key_9, LSHIFT(Key_Equals), Key_F12,
|
||||
Key_Minus, Key_4, Key_5, Key_6, Key_Quote, ___,
|
||||
___, LSHIFT(Key_Minus), Key_1, Key_2, Key_3, LSHIFT(Key_Quote), ___,
|
||||
___, ___, Key_Enter, Key_0,
|
||||
___),
|
||||
|
||||
[ETC] = KEYMAP_STACKED
|
||||
(___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___,
|
||||
___,
|
||||
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, Key_F7, Key_F8, Key_F9, Key_Home, ___,
|
||||
___, Key_F4, Key_F5, Key_F6, Key_End, ___,
|
||||
___, ___, Key_F1, Key_F2, Key_F3, Key_Insert, ___,
|
||||
___, ___, ___, ___,
|
||||
___)
|
||||
|
||||
) // KEYMAPS(
|
||||
|
||||
/* Re-enable astyle's indent enforcement */
|
||||
// clang-format on
|
||||
|
||||
/** versionInfoMacro handles the 'firmware version info' macro
|
||||
* When a key bound to the macro is pressed, this macro
|
||||
* prints out the firmware build information as virtual keystrokes
|
||||
*/
|
||||
|
||||
static void versionInfoMacro(uint8_t key_state) {
|
||||
if (keyToggledOn(key_state)) {
|
||||
Macros.type(PSTR("Keyboardio Model 100 - Firmware version "));
|
||||
Macros.type(PSTR(KALEIDOSCOPE_FIRMWARE_VERSION));
|
||||
}
|
||||
}
|
||||
|
||||
/** anyKeyMacro is used to provide the functionality of the 'Any' key.
|
||||
*
|
||||
* When the 'any key' macro is toggled on, a random alphanumeric key is
|
||||
* selected. While the key is held, the function generates a synthetic
|
||||
* keypress event repeating that randomly selected key.
|
||||
*
|
||||
*/
|
||||
|
||||
static void anyKeyMacro(KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
event.key.setKeyCode(Key_A.getKeyCode() + (uint8_t)(millis() % 36));
|
||||
event.key.setFlags(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** macroAction dispatches keymap events that are tied to a macro
|
||||
to that macro. It takes two uint8_t parameters.
|
||||
|
||||
The first is the macro being called (the entry in the 'enum' earlier in this file).
|
||||
The second is the state of the keyswitch. You can use the keyswitch state to figure out
|
||||
if the key has just been toggled on, is currently pressed or if it's just been released.
|
||||
|
||||
The 'switch' statement should have a 'case' for each entry of the macro enum.
|
||||
Each 'case' statement should call out to a function to handle the macro in question.
|
||||
|
||||
*/
|
||||
|
||||
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
|
||||
switch (macro_id) {
|
||||
|
||||
case MACRO_VERSION_INFO:
|
||||
versionInfoMacro(event.state);
|
||||
break;
|
||||
|
||||
case MACRO_ANY:
|
||||
anyKeyMacro(event);
|
||||
break;
|
||||
}
|
||||
return MACRO_NONE;
|
||||
}
|
||||
|
||||
|
||||
// These 'solid' color effect definitions define a rainbow of
|
||||
// LED color modes calibrated to draw 500mA or less on the
|
||||
// Keyboardio Model 100.
|
||||
|
||||
|
||||
static kaleidoscope::plugin::LEDSolidColor solidRed(160, 0, 0);
|
||||
static kaleidoscope::plugin::LEDSolidColor solidOrange(140, 70, 0);
|
||||
static kaleidoscope::plugin::LEDSolidColor solidYellow(130, 100, 0);
|
||||
static kaleidoscope::plugin::LEDSolidColor solidGreen(0, 160, 0);
|
||||
static kaleidoscope::plugin::LEDSolidColor solidBlue(0, 70, 130);
|
||||
static kaleidoscope::plugin::LEDSolidColor solidIndigo(0, 0, 170);
|
||||
static kaleidoscope::plugin::LEDSolidColor solidViolet(130, 0, 120);
|
||||
|
||||
/** toggleLedsOnSuspendResume toggles the LEDs off when the host goes to sleep,
|
||||
* and turns them back on when it wakes up.
|
||||
*/
|
||||
void toggleLedsOnSuspendResume(kaleidoscope::plugin::HostPowerManagement::Event event) {
|
||||
switch (event) {
|
||||
case kaleidoscope::plugin::HostPowerManagement::Suspend:
|
||||
LEDControl.disable();
|
||||
break;
|
||||
case kaleidoscope::plugin::HostPowerManagement::Resume:
|
||||
LEDControl.enable();
|
||||
break;
|
||||
case kaleidoscope::plugin::HostPowerManagement::Sleep:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** hostPowerManagementEventHandler dispatches power management events (suspend,
|
||||
* resume, and sleep) to other functions that perform action based on these
|
||||
* events.
|
||||
*/
|
||||
void hostPowerManagementEventHandler(kaleidoscope::plugin::HostPowerManagement::Event event) {
|
||||
toggleLedsOnSuspendResume(event);
|
||||
}
|
||||
|
||||
/** This 'enum' is a list of all the magic combos used by the Model 100's
|
||||
* firmware The names aren't particularly important. What is important is that
|
||||
* each is unique.
|
||||
*
|
||||
* These are the names of your magic combos. They will be used by the
|
||||
* `USE_MAGIC_COMBOS` call below.
|
||||
*/
|
||||
enum {
|
||||
// Toggle between Boot (6-key rollover; for BIOSes and early boot) and NKRO
|
||||
// mode.
|
||||
COMBO_TOGGLE_NKRO_MODE,
|
||||
// Enter test mode
|
||||
COMBO_ENTER_TEST_MODE
|
||||
};
|
||||
|
||||
/** Wrappers, to be used by MagicCombo. **/
|
||||
|
||||
/**
|
||||
* This simply toggles the keyboard protocol via USBQuirks, and wraps it within
|
||||
* a function with an unused argument, to match what MagicCombo expects.
|
||||
*/
|
||||
static void toggleKeyboardProtocol(uint8_t combo_index) {
|
||||
USBQuirks.toggleKeyboardProtocol();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles between using the built-in keymap, and the EEPROM-stored one.
|
||||
*/
|
||||
static void toggleKeymapSource(uint8_t combo_index) {
|
||||
if (Layer.getKey == Layer.getKeyFromPROGMEM) {
|
||||
Layer.getKey = EEPROMKeymap.getKey;
|
||||
} else {
|
||||
Layer.getKey = Layer.getKeyFromPROGMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This enters the hardware test mode
|
||||
*/
|
||||
static void enterHardwareTestMode(uint8_t combo_index) {
|
||||
HardwareTestMode.runTests();
|
||||
}
|
||||
|
||||
|
||||
/** Magic combo list, a list of key combo and action pairs the firmware should
|
||||
* recognise.
|
||||
*/
|
||||
USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
|
||||
// Left Fn + Esc + Shift
|
||||
.keys = {R3C6, R2C6, R3C7}},
|
||||
{.action = enterHardwareTestMode,
|
||||
// Left Fn + Prog + LED
|
||||
.keys = {R3C6, R0C0, R0C6}},
|
||||
{.action = toggleKeymapSource,
|
||||
// Left Fn + Prog + Shift
|
||||
.keys = {R3C6, R0C0, R3C7}});
|
||||
|
||||
// First, tell Kaleidoscope which plugins you want to use.
|
||||
// The order can be important. For example, LED effects are
|
||||
// added in the order they're listed here.
|
||||
KALEIDOSCOPE_INIT_PLUGINS(
|
||||
// The EEPROMSettings & EEPROMKeymap plugins make it possible to have an
|
||||
// editable keymap in EEPROM.
|
||||
EEPROMSettings,
|
||||
EEPROMKeymap,
|
||||
|
||||
// SpaceCadet can turn your shifts into parens on tap, while keeping them as
|
||||
// Shifts when held. SpaceCadetConfig lets Chrysalis configure some aspects of
|
||||
// the plugin.
|
||||
// SpaceCadet,
|
||||
// SpaceCadetConfig,
|
||||
|
||||
// Focus allows bi-directional communication with the host, and is the
|
||||
// interface through which the keymap in EEPROM can be edited.
|
||||
Focus,
|
||||
|
||||
// FocusSettingsCommand adds a few Focus commands, intended to aid in
|
||||
// changing some settings of the keyboard, such as the default layer (via the
|
||||
// `settings.defaultLayer` command)
|
||||
FocusSettingsCommand,
|
||||
|
||||
// FocusEEPROMCommand adds a set of Focus commands, which are very helpful in
|
||||
// both debugging, and in backing up one's EEPROM contents.
|
||||
FocusEEPROMCommand,
|
||||
|
||||
// The boot greeting effect pulses the LED button for 10 seconds after the
|
||||
// keyboard is first connected
|
||||
BootGreetingEffect,
|
||||
|
||||
// The hardware test mode, which can be invoked by tapping Prog, LED and the
|
||||
// left Fn button at the same time.
|
||||
HardwareTestMode,
|
||||
|
||||
// LEDControl provides support for other LED modes
|
||||
LEDControl,
|
||||
|
||||
// We start with the LED effect that turns off all the LEDs.
|
||||
LEDOff,
|
||||
|
||||
// The rainbow effect changes the color of all of the keyboard's keys at the same time
|
||||
// running through all the colors of the rainbow.
|
||||
LEDRainbowEffect,
|
||||
|
||||
// The rainbow wave effect lights up your keyboard with all the colors of a rainbow
|
||||
// and slowly moves the rainbow across your keyboard
|
||||
LEDRainbowWaveEffect,
|
||||
|
||||
// The chase effect follows the adventure of a blue pixel which chases a red pixel across
|
||||
// your keyboard. Spoiler: the blue pixel never catches the red pixel
|
||||
LEDChaseEffect,
|
||||
|
||||
// These static effects turn your keyboard's LEDs a variety of colors
|
||||
solidRed,
|
||||
solidOrange,
|
||||
solidYellow,
|
||||
solidGreen,
|
||||
solidBlue,
|
||||
solidIndigo,
|
||||
solidViolet,
|
||||
|
||||
// The breathe effect slowly pulses all of the LEDs on your keyboard
|
||||
LEDBreatheEffect,
|
||||
|
||||
// The AlphaSquare effect prints each character you type, using your
|
||||
// keyboard's LEDs as a display
|
||||
AlphaSquareEffect,
|
||||
|
||||
// The stalker effect lights up the keys you've pressed recently
|
||||
StalkerEffect,
|
||||
|
||||
// The LED Palette Theme plugin provides a shared palette for other plugins,
|
||||
// like Colormap below
|
||||
LEDPaletteTheme,
|
||||
|
||||
// The Colormap effect makes it possible to set up per-layer colormaps
|
||||
ColormapEffect,
|
||||
|
||||
// The numpad plugin is responsible for lighting up the 'numpad' mode
|
||||
// with a custom LED effect
|
||||
// NumPad,
|
||||
|
||||
// The macros plugin adds support for macros
|
||||
Macros,
|
||||
|
||||
// The MouseKeys plugin lets you add keys to your keymap which move the mouse.
|
||||
// MouseKeys,
|
||||
|
||||
// The HostPowerManagement plugin allows us to turn LEDs off when then host
|
||||
// goes to sleep, and resume them when it wakes up.
|
||||
HostPowerManagement,
|
||||
|
||||
// The MagicCombo plugin lets you use key combinations to trigger custom
|
||||
// actions - a bit like Macros, but triggered by pressing multiple keys at the
|
||||
// same time.
|
||||
MagicCombo,
|
||||
|
||||
// The USBQuirks plugin lets you do some things with USB that we aren't
|
||||
// comfortable - or able - to do automatically, but can be useful
|
||||
// nevertheless. Such as toggling the key report protocol between Boot (used
|
||||
// by BIOSes) and Report (NKRO).
|
||||
USBQuirks,
|
||||
|
||||
// The Qukeys plugin enables the "Secondary action" functionality in
|
||||
// Chrysalis. Keys with secondary actions will have their primary action
|
||||
// performed when tapped, but the secondary action when held.
|
||||
Qukeys,
|
||||
|
||||
// Enables the "Sticky" behavior for modifiers, and the "Layer shift when
|
||||
// held" functionality for layer keys.
|
||||
OneShot,
|
||||
EscapeOneShot,
|
||||
EscapeOneShotConfig,
|
||||
|
||||
// Turns LEDs off after a configurable amount of idle time.
|
||||
IdleLEDs,
|
||||
PersistentIdleLEDs,
|
||||
|
||||
// Enables dynamic, Chrysalis-editable macros.
|
||||
DynamicMacros,
|
||||
|
||||
// The FirmwareVersion plugin lets Chrysalis query the version of the firmware
|
||||
// programmatically.
|
||||
FirmwareVersion,
|
||||
|
||||
// The LayerNames plugin allows Chrysalis to display - and edit - custom layer
|
||||
// names, to be shown instead of the default indexes.
|
||||
LayerNames,
|
||||
|
||||
// Enables setting, saving (via Chrysalis), and restoring (on boot) the
|
||||
// default LED mode.
|
||||
DefaultLEDModeConfig
|
||||
|
||||
// Enables the GeminiPR Stenography protocol. Unused by default, but with the
|
||||
// plugin enabled, it becomes configurable - and then usable - via Chrysalis.
|
||||
//GeminiPR
|
||||
);
|
||||
|
||||
/** The 'setup' function is one of the two standard Arduino sketch functions.
|
||||
* It's called when your keyboard first powers up. This is where you set up
|
||||
* Kaleidoscope and any plugins.
|
||||
*/
|
||||
void setup() {
|
||||
// First, call Kaleidoscope's internal setup function
|
||||
Kaleidoscope.setup();
|
||||
|
||||
// Set the hue of the boot greeting effect to something that will result in a
|
||||
// nice green color.
|
||||
BootGreetingEffect.hue = 85;
|
||||
|
||||
// While we hope to improve this in the future, the NumPad plugin
|
||||
// needs to be explicitly told which keymap layer is your numpad layer
|
||||
// NumPad.numPadLayer = NUMPAD;
|
||||
|
||||
// We configure the AlphaSquare effect to use RED letters
|
||||
AlphaSquare.color = CRGB(255, 0, 0);
|
||||
|
||||
// We set the brightness of the rainbow effects to 150 (on a scale of 0-255)
|
||||
// This draws more than 500mA, but looks much nicer than a dimmer effect
|
||||
LEDRainbowEffect.brightness(255);
|
||||
LEDRainbowWaveEffect.brightness(255);
|
||||
|
||||
// Set the action key the test mode should listen for to Left Fn
|
||||
HardwareTestMode.setActionKey(R3C6);
|
||||
|
||||
// The LED Stalker mode has a few effects. The one we like is called
|
||||
// 'BlazingTrail'. For details on other options, see
|
||||
// https://github.com/keyboardio/Kaleidoscope/blob/master/docs/plugins/LED-Stalker.md
|
||||
StalkerEffect.variant = STALKER(BlazingTrail);
|
||||
|
||||
// To make the keymap editable without flashing new firmware, we store
|
||||
// additional layers in EEPROM. For now, we reserve space for eight layers. If
|
||||
// one wants to use these layers, just set the default layer to one in EEPROM,
|
||||
// by using the `settings.defaultLayer` Focus command, or by using the
|
||||
// `keymap.onlyCustom` command to use EEPROM layers only.
|
||||
EEPROMKeymap.setup(8);
|
||||
|
||||
// We need to tell the Colormap plugin how many layers we want to have custom
|
||||
// maps for. To make things simple, we set it to eight layers, which is how
|
||||
// many editable layers we have (see above).
|
||||
ColormapEffect.max_layers(8);
|
||||
|
||||
// For Dynamic Macros, we need to reserve storage space for the editable
|
||||
// macros. A kilobyte is a reasonable default.
|
||||
DynamicMacros.reserve_storage(1024);
|
||||
|
||||
// If there's a default layer set in EEPROM, we should set that as the default
|
||||
// here.
|
||||
Layer.move(EEPROMSettings.default_layer());
|
||||
|
||||
// To avoid any surprises, SpaceCadet is turned off by default. However, it
|
||||
// can be permanently enabled via Chrysalis, so we should only disable it if
|
||||
// no configuration exists.
|
||||
// SpaceCadetConfig.disableSpaceCadetIfUnconfigured();
|
||||
// SpaceCadet.disable();
|
||||
|
||||
// Editable layer names are stored in EEPROM too, and we reserve 16 bytes per
|
||||
// layer for them. We need one extra byte per layer for bookkeeping, so we
|
||||
// reserve 17 / layer in total.
|
||||
LayerNames.reserve_storage(17 * 8);
|
||||
|
||||
// Unless configured otherwise with Chrysalis, we want to make sure that the
|
||||
// firmware starts with LED effects off. This avoids over-taxing devices that
|
||||
// don't have a lot of power to share with USB devices
|
||||
DefaultLEDModeConfig.activateLEDModeIfUnconfigured(&LEDOff);
|
||||
}
|
||||
|
||||
/** loop is the second of the standard Arduino sketch functions.
|
||||
* As you might expect, it runs in a loop, never exiting.
|
||||
*
|
||||
* For Kaleidoscope-based keyboard firmware, you usually just want to
|
||||
* call Kaleidoscope.loop(); and not do anything custom here.
|
||||
*/
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"cpu": {
|
||||
"fqbn": "keyboardio:gd32:keyboardio_model_100",
|
||||
"port": ""
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope - A Kaleidoscope example
|
||||
* Copyright (C) 2016-2022 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DEBUG_SERIAL false
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-MouseKeys.h>
|
||||
|
||||
enum {
|
||||
PRIMARY,
|
||||
MOUSEKEYS,
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
KEYMAPS(
|
||||
[PRIMARY] = KEYMAP_STACKED
|
||||
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
|
||||
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
|
||||
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
|
||||
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
|
||||
|
||||
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
|
||||
ShiftToLayer(MOUSEKEYS),
|
||||
|
||||
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
|
||||
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
|
||||
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
|
||||
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
|
||||
|
||||
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
|
||||
LockLayer(MOUSEKEYS)
|
||||
),
|
||||
|
||||
[MOUSEKEYS] = KEYMAP_STACKED
|
||||
(___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, Key_mouseWarpNW, Key_mouseWarpNE, ___,
|
||||
___, ___, ___, ___, Key_mouseWarpSW, Key_mouseWarpSE,
|
||||
___, ___, Key_mouseBtnL, Key_mouseBtnM, Key_mouseBtnR, ___, ___,
|
||||
___, ___, ___, ___,
|
||||
___,
|
||||
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, Key_mouseUp, ___, ___, ___,
|
||||
___, Key_mouseUp, Key_mouseDn, Key_mouseR, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___,
|
||||
___)
|
||||
)
|
||||
// clang-format on
|
||||
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(MouseKeys);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
MouseKeys.setSpeedLimit(100);
|
||||
MouseKeys.setWarpGridSize(MOUSE_WARP_GRID_2X2);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"cpu": {
|
||||
"fqbn": "keyboardio:avr:model01",
|
||||
"port": ""
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* ShiftBlocker -- A Kaleidoscope Example
|
||||
* Copyright (C) 2016-2022 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Kaleidoscope.h"
|
||||
#include "Kaleidoscope-Macros.h"
|
||||
|
||||
// clang-format off
|
||||
KEYMAPS(
|
||||
[0] = KEYMAP_STACKED
|
||||
(
|
||||
Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
|
||||
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
|
||||
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
|
||||
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
|
||||
|
||||
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
|
||||
M(0),
|
||||
|
||||
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
|
||||
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
|
||||
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
|
||||
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
|
||||
|
||||
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
|
||||
M(0)
|
||||
),
|
||||
)
|
||||
// clang-format on
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
|
||||
// When activated, this plugin will suppress any `Shift` key (including modifier
|
||||
// combos with `Shift`) before it's added to the HID report.
|
||||
class ShiftBlocker : public Plugin {
|
||||
|
||||
public:
|
||||
EventHandlerResult onAddToReport(Key key) {
|
||||
if (active_ && key.isKeyboardShift())
|
||||
return EventHandlerResult::ABORT;
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
void enable() {
|
||||
active_ = true;
|
||||
}
|
||||
void disable() {
|
||||
active_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool active_{false};
|
||||
};
|
||||
|
||||
} // namespace plugin
|
||||
} // namespace kaleidoscope
|
||||
|
||||
kaleidoscope::plugin::ShiftBlocker ShiftBlocker;
|
||||
|
||||
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
switch (macro_id) {
|
||||
case 0:
|
||||
// First, enable ShiftBlocker to suppress any held `Shift` key(s).
|
||||
ShiftBlocker.enable();
|
||||
// Tap `AltGr` + `7` to activate the grave accent dead key.
|
||||
Macros.tap(RALT(Key_7));
|
||||
// Disable ShiftBlocker so it won't affect the `E` event.
|
||||
ShiftBlocker.disable();
|
||||
// Change the Macros key into a plain `E` key before its press event is
|
||||
// processed.
|
||||
event.key = Key_E;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return MACRO_NONE;
|
||||
}
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(Macros,
|
||||
ShiftBlocker);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
// Uncomment to manually set the OS, as Kaleidoscope will not autodetect it.
|
||||
// (Possible values are in HostOS.h.)
|
||||
// HostOS.os(kaleidoscope::hostos::LINUX);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"cpu": {
|
||||
"fqbn": "keyboardio:avr:model01",
|
||||
"port": ""
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
/* This example demonstrates the Model 01 / Model 100 butterfly logo key as a
|
||||
* tmux prefix key. When the key is held, Ctrl-B is pressed prior to the key
|
||||
* you pressed.
|
||||
*
|
||||
* This example also demonstrates the purpose of using an entire layer for this
|
||||
* plugin: the h/j/k/l keys in the TMUX layer are swapped for arrow keys to
|
||||
* make switching between panes easier.
|
||||
*/
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-PrefixLayer.h>
|
||||
|
||||
enum {
|
||||
PRIMARY,
|
||||
TMUX,
|
||||
}; // layers
|
||||
|
||||
/* Used in setup() below. */
|
||||
static const kaleidoscope::plugin::PrefixLayer::Entry prefix_layers[] PROGMEM = {
|
||||
kaleidoscope::plugin::PrefixLayer::Entry(TMUX, LCTRL(Key_B)),
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
KEYMAPS(
|
||||
[PRIMARY] = KEYMAP_STACKED
|
||||
(XXX, Key_1, Key_2, Key_3, Key_4, Key_5, XXX,
|
||||
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
|
||||
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
|
||||
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
|
||||
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
|
||||
XXX,
|
||||
|
||||
XXX, Key_6, Key_7, Key_8, Key_9, Key_0, XXX,
|
||||
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
|
||||
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
|
||||
ShiftToLayer(TMUX), Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
|
||||
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
|
||||
XXX),
|
||||
|
||||
[TMUX] = KEYMAP_STACKED
|
||||
(___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___,
|
||||
___,
|
||||
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___,
|
||||
___),
|
||||
)
|
||||
// clang-format on
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(PrefixLayer);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
/* Configure the previously-defined prefix layers. */
|
||||
PrefixLayer.setPrefixLayers(prefix_layers);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"cpu": {
|
||||
"fqbn": "keyboardio:avr:model01",
|
||||
"port": ""
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue