keyboardio / kaleidoscope Goto Github PK
View Code? Open in Web Editor NEWFirmware for Keyboardio keyboards and other keyboards with AVR or ARM MCUs.
Home Page: http://keyboard.io
License: GNU General Public License v3.0
Firmware for Keyboardio keyboards and other keyboards with AVR or ARM MCUs.
Home Page: http://keyboard.io
License: GNU General Public License v3.0
At the moment, ConsumerControl
keys are not repeating, because we use ConsumerControl.write()
, and only when the key toggles on. This means that if I want to fiddle with volume control (iirc that is part of ConsumerControl
, but SystemControl
suffers from the same issue), I need to repeatedly tap a key, instead of being able to hold it.
I think we should be using .press()
, and .releaseAll()
in the loop, like we do for Keyboard
, but I have to test if this works as one would expect.
make astyle
is great, but it reformats the KEYMAP
and KEYMAP_STACKED
macros too in the examples. Those should be guarded with hints that stop astyle from doing so.
See here
handle_keyswitch_event (Key_Backspace, UNKNOWN_KEYSWITCH_LOCATION, IS_PRESSED | INJECTED);
Keyboard.sendReport ();
handle_keyswitch_event (Key_Backspace, UNKNOWN_KEYSWITCH_LOCATION, WAS_PRESSED | INJECTED);
Keyboard.sendReport ();
is a pattern we repeat more than a few times. Perhaps we should have a helper for it
When I overwrote the Micro for the first time testing, my keys changed. I thought I had royally screwed up the Micro's program.
I'm also looking to get back the start/stop keys.
Thanks guys!
I got the following question on the wiki of my fork, from @NicholasSterling, copying it here to make it easier to answer:
In Model01.cpp
, act_on_matrix_scan()
makes two calls to handle_key_event
-- the first takes the left-hand state, and the second takes the right-hand state. Does that mean that we could not have Ctrl
and Alt
under the left thumb and Shift
under the right thumb, and use them to produce, say Ctrl-Alt-Shift-L
?
I've been trying to make the Akela plugins work, and one issue I ran into is that injecting events is not the most straightforward thing. Or, rather, injecting is reasonably straightforward, but changing one key into another, is not. Reinjecting a new one has a number of issues, ranging from making it too easy to create endless loops, to not handling keys we should, for fear of an endless loop.
So, I thought that if the handlers would not return bool
, but a Key
, one that would be passed on to the next handler, that would solve most of my problems. Seeing a Key_NoKey
would short circuit the loop.
I have not tried this method yet, but its yet another change around event handling, so I figured I'll open an issue first.
Currently in Linux when you try to program the keyboard, you will get a programming error.
The error looks similar to this.
Sketch uses 20684 bytes (72%) of program storage space. Maximum is 28672 bytes.
Global variables use 1700 bytes (66%) of dynamic memory, leaving 860 bytes for local variables. Maximum is 2560 bytes.
processing.app.debug.RunnerException
at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:160)
at cc.arduino.UploaderUtils.upload(UploaderUtils.java:78)
at processing.app.SketchController.upload(SketchController.java:713)
at processing.app.SketchController.exportApplet(SketchController.java:686)
at processing.app.Editor$DefaultExportHandler.run(Editor.java:2149)
at java.lang.Thread.run(Thread.java:745)
Caused by: processing.app.SerialException: Error touching serial port '/dev/ttyACM0'.
at processing.app.Serial.touchForCDCReset(Serial.java:99)
at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:144)
... 5 more
Caused by: jssc.SerialPortException: Port name - /dev/ttyACM0; Method name - openPort(); Exception type - Permission denied.
at jssc.SerialPort.openPort(SerialPort.java:170)
at processing.app.Serial.touchForCDCReset(Serial.java:93)
... 6 more
The wiki doesn't have any instructions on how to add this.
I'm going to add a wiki page on how to add a udev rule to linux. I have done this for work a couple of times.
The expected behavior is that the keyboard goes out of numlock mode when the fn keys are released. However it remains in numlock mode until you press the numlock button.
I noticed that the LICENSE
file now says GPLv2, which seems fine.
But the NOTICE
file still mentions that the Keyboard.IO driver is licensed under the Apache 2.0.
I’d suggest you update the NOTICE file to point to the right license.
Since NOTICE
file is not needed under GPL (but is needed if you contribute to an Apache Foundation project), it might even make sense to change the NOTICE
file to README
or README.markdown
. Feel free to keep the filename as it is, if that suits your style better. Not a biggie there 😸
[0] $ make
/usr/bin/avr-g++ -c -g -w -Wa,-adhlns=build/mouse_movement.lst -DVERSION=\"e220-dirty\" -DUSB_VID=0x1209 -DUSB_PID=0x2301 '-DUSB_PRODUCT="Keyboardio model01"' -DARDUINO_ARCH_AVR -DUSING_MAKEFILE=1 -Wall -Wextra -Wformat=2 -Wuninitialized -Wshadow -Wconversion -Os -freorder-blocks -fno-inline-small-functions -fno-exceptions -ffunction-sections -fdata-sections -MMD -DF_CPU=16000000L -DARDUINO=165 -mmcu=atmega32u4 -I/usr/share/arduino/hardware/arduino/avr/libraries/EEPROM -I/usr/share/arduino/hardware/arduino/avr/libraries/Wire -I/usr/share/arduino/hardware/arduino/avr/libraries/SPI -I/usr/share/arduino/hardware/arduino/avr/libraries/SoftwareSerial -I/usr/share/arduino/hardware/arduino/avr/libraries/EEPROM -I/usr/share/arduino/hardware/arduino/avr/libraries/Wire -I/usr/share/arduino/hardware/arduino/avr/libraries/EEPROM -I/usr/share/arduino/hardware/arduino/avr/libraries/Wire -I/usr/share/arduino/hardware/arduino/avr/libraries/Wire/utility -I/usr/share/arduino/hardware/arduino/avr/libraries/Wire/utility -I/usr/share/arduino/hardware/arduino/avr/libraries/Wire/utility -Ihardware/keyboardio/avr/variants/model01 -Ihardware/keyboardio/avr/cores/keyboardio -MD -MT 'build/libsketch.a/(mouse_movement.o)' -MF build/libsketch.a/._mouse_movement.dep mouse_movement.cpp -o build/mouse_movement.o
mouse_movement.cpp:157:1: fatal error: opening dependency file build/libsketch.a/._mouse_movement.dep: No such file or directory
}
^
compilation terminated.
_Makefile.Master:661: recipe for target 'build/libsketch.a/(mouse_movement.o)' failed
make: *** [build/libsketch.a/(mouse_movement.o)] Error 1
I'm not sure why this is specifically missing or why there are parens around the file name.
I think we need that to be able to use it for a trivial sketch.
@algernon I'm happy to make the fix, but wanted your input before I do so in core. For now, I've forked it into the Model01-Firmware repo
Regardless of what the previous setting was, the keyboard will be in the red state after going in and out of numpad mode. This doesn't 100% consistently happen though. It seems to happen the first time after logging back into my computer but not again until I log out of my computer and come back to my desk later. So the problem may be with persisting the selected color scheme during computer sleep states?
Hi,
I hate to bother you with this question as I saw in one of the files in this project that you intend to attach an open source license when you release the board, but I could really use your code as it is right now.
I am building a keyboard using an Arduino Micro and an old typewriter (http://imgur.com/a/40a9m#k3JBMMv). I'd really like to use your code, but I'd have to fork it and add some code (I use a shift register for extra I/O) so if you could find a little bit of time to to assign a license to it I'd be very grateful and I promise I won't bug you with (more) issues or feature requests :)
Kind regards,
Tinco
I recently tried to flash the firmware and was super confused from the docs whether I should use the makefile or flash from the Arduino app. Some consistency and clarity would be super helpful, as would a note that holding down the prog
button is necessary to successfully flash.
Thanks!
I have some key bindings for meta keys + arrow keys or brackets. It is currently impossible to execute these key bindings with keyboardio 01 because pressing fn disables the meta keys. There is nothing mapped to those keys in the fn down mode, so why not have them be active in both states?
Currently, when handle_key_event
is called, it does not touch the mappedKey
argument, and defers the lookup to the first handler that needs the keycode. This is reasonable when the only handler that needs it is the default one, but if there are more handles, each may have to do their own lookup, or at least, have the logic to do a lookup if mappedKey
is Key_NoKey
.
To make things simpler, I believe that handle_key_event
should have this logic, and the handlers should assume that the keycode they received is the one they should act upon, and avoid a lookup unless there is a good reason to do one.
Will submit a PR later, just recording the idea here, lest I forget it. This came up while working on one-shot keys, where the handler has to decide if it should handle the event, and it has to do an avoidable lookup right now.
One thing that made Kaleidoscope-Focus possible was the use of linked lists for hook-like things. I plan to experiment with changing the loop and eventHandler hooks into linked lists too, but first, want to document the reasons why, because like everything else, they come with a few trade-offs.
Pros:
Cons:
(hook, nextItem)
tuple, but only have the hook, so we use a macro to turn that into a list-item struct.The code to handle them would not be terribly different, as far as I see.
@obra mentioned on IRC the other day that Kaleidoscope could really use a coding style guide. Though some of it is enforced now via make astyle
, that's mostly whitespace, and formatting. Naming, code organization, and similar things are not covered, and there are a number of different styles used throughout the codebase.
It would be incredibly useful not only for future authors, but for bringing the current set of plugins to a common ground, too.
(I'm going through a number of existing guides, and will share my opinion in followup comments)
At the moment, when we look up a key, we look at each layer from the highest active one, to the lowest. Every cycle. For the base layer, this is no extra work, but the higher the top layer is, the more work we have to perform each cycle, for each key.
This is suboptimal, the number of active layers should not affect the speed of the scan cycle, or at least not this much.
So I propose a solution, which may not be the most efficient (it requires ROWS * COLS
number of bytes, or half the size of a layer), but will net us a lot higher speed, even when using many layers: we'll keep an index of what layer each key has been found at, and only do the more expensive lookup, if the index is marked invalid.
We'll use a byte array, and whenever we change layers, we clear the array (fill it with 0xff
). The next scan cycle will do the expensive lookups, yes, but all the other scans that happen inbetween this expensive one, and the next that changes the layer state, will have a much easier job.
Even for a short, momentary layer switch, this would mean that we have half a dozen faster cycles.
This comes at a cost of some data space, however. We can optimize it further later, by using less bits / index, but I'd do that as a separate step.
@obra noticed that the current firmware feels a little laggier than previously, even with the layer lookup speedups. This may very well be the case, because handle_key_events
runs through plugins, and we do two runs over the loop hooks every cycle, which may slow things down. Should investigate which part is slow, and what can be done to speed things up.
As a first step, Serial.println(millis())
here and there may shed some light on which part to look at first.
(And this issue is mostly a reminder for myself not to forget doing this.)
@bergey writes:
On the PVT with default firmware, on Windows 10, releasing the fn key while still holding one of uiopprints both characters - {u, [o- and so forth. I find I roll my hand in a way that I do this frequently. Does anyone want this behavior
After following the instructions in the README, I needed to take the additional step of running make
in the bootloaders/caterina
directory to address a warning from the IDE about a missing Caterina.hex
file.
(Not sure if this should be added to the instructions, or if this is a consequence of running verify without an actual device to test on)
After following the instructions listed here to install the Arduino IDE and set up the Kaleidoscope firmware, I get two errors when building my sketch (The sketch is currently a copy of the example firmware at https://github.com/keyboardio/Model01-Firmware).
The first error complains about their being two HID.h libraries, one in the arduino install directory and one in the keyboardio library. I tried moving the arduino one out of the way, but the second error stayed.
The second error indicates that it can't find the definition for various mouse-related things when compiling the MouseKeys plugin.
Full error message below:
Following the instructions in README.md on macOS yields an error on the last step:
Makefile:13: /Users/merlin/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-Plugin/build/rules.mk: No such file or directory
make: *** No rule to make target `/Users/merlin/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope-Plugin/build/rules.mk'. Stop.
I haven't had time to investigate, but presumably there's at least one step that has been skipped because a file is normally there when you guys do it, but isn't there for someone starting from scratch.
Just to help make it clear we're talking about switch events rather than hid events
Would be nice to have a LED color mode corresponding to the popular Miami
keycaps.
Kaleidoscope aims to be a keyboard-independent framework, but key_defs.h defines the following Keyboardio-specific Keys:
#define Key_LeftParen LSHIFT(Key_9)
#define Key_RightParen LSHIFT(Key_0)
#define Key_LeftCurlyBracket LSHIFT(Key_LeftBracket)
#define Key_RightCurlyBracket LSHIFT(Key_RightBracket)
#define Key_Pipe LSHIFT(Key_Backslash)
Not only would it make more sense from a modularity perspective to define these in Model01-Firmware/Model01-Firmware.ino, but in that location I think contributors are more likely to find it when looking for examples of how to create keys which type shifted characters without shift held down.
This would require also defining these in Kaleidoscope/examples/Kaleidoscope/Kaleidoscope.ino, which appears to be a kinda-duplication of Model01-Firmware.ino already anyway.
While trying to work out how to implement one-shot keys nicely, I ran into a couple of shortcomings with the current - very simplistic - hook helpers: the API only allows appending hooks, but to toggle one-shots on and off, I'd need a way to replace an existing hook, too.
While there, deleting a hook, and prepending (or rather, inserting at a specific position) would also be useful. So I'm proposing the following additional helpers, which I will implement at the first opportunity, too:
// delete all occurrences of the hook function from the array, pulling up later handlers
void event_handler_hook_delete (custom_handler_t hook);
// replace one hook with another
void event_handler_hook_replace (custom_handler_t old, custom_handler_t new);
// insert a hook at a specific index, pushing down the rest, if need be
void event_handler_hook_insert (byte idx, custom_handler_t hook);
...and the same for the loop()
hooks.
In a plugin, one would use them like this:
void OneShotMods::on() {
if (isOn)
return;
event_handler_hook_replace (this->passthroughHandler, this->oneShotHandler);
loop_hook_add (this->oneShotLoop);
isOn = true;
}
void oneShotMods::off() {
if (!isOn)
return;
event_handler_hook_replace (this->oneShotHandler, this->passthoughHandler);
loop_hook_delete (this->oneShotLoop);
isOn = false;
}
While here, it may make plugin authors' life more convenient, if the _replace
stuff would delete if the second argument is NULL
, and add, if the first one is. Why? Because then, we can introduce a helper:
void hooks_replace (custom_handler_t oldEvent, custom_handler_t newEvent,
custom_loop_t oldLoop, custom_loop_t newLoop);
With this, the above code could be simplified to:
void OneShotMods::on() {
if (isOn)
return;
hooks_replace (this->passthroughHandler, this->oneShotHandler,
NULL, this->oneShotLoop);
isOn = true;
}
void oneShotMods::off() {
if (!isOn)
return;
hooks_replace (this->oneShotHandler, this->passthroughHandler,
this->oneShotLoop, NULL);
isOn = false;
}
We could even have a #define NO_HANDLER NULL
to make the code look even nicer.
The point: turn auto-repeat for symbol only after 3 presses. E.g. for "=" key,
user should press "=", release "=", press "=", release "=", hold "=".
Here we have 2 advantages:
And it still it's pretty easy yet to press same key 3 times.
Surely, it require turning off software key auto repeating in OS for best UX.
Universal modifier key (Mod or Quasi, which name would be better?).
Main idea: "press Quasi", "press <trigger key> (e.g. Ctrl)", "release <trigger key>", "press <target key>" (with holden "Quasi") => acts like "modifier key" (from "trigger to modifier map" according to pressed <trigger key>) plus "<target key>".
It may be convenient when you need to hold few modifiers.
Example:
we can customize "trigger to modifier map" to press e.g. "C" key for "Ctrl" modifier.
Here original idea author's blog. I'm not the author. Initially idea based on Jef Raskin's principles from "The Humane Interface".
I just noticed a bug in how the Fn keys are handled. Press one Fn, press the other, then release one. The Fn modifier state should still be active, but it's not.
FWIW, I'm using Fn=Key_Keymap1_Momentary instead of Fn=Key_KeymapNext_Momentary, to avoid having dual-Fn turn on numlock.
For @algernon , context from IRC:
<algernon> regarding the Fn keys: that's going to be a tricky fix.
<algernon> or perhaps not...
<algernon> anyway, the root of the issue is that both keys have the same keycode, and the layer code turns the layer off when the keycode is released (it doesn't care where that release originates from)
<algernon> it also turns the layer on only on key press, not when its held, so the other Fn key being held does not re-trigger the layer
<algernon> one workaround would be to call layer.on() not only when the layer key toggles on, but every cycle while it is held
<algernon> that would fix the issue, but I'm not sure about the costs yet.
<ToyKeeper> Maybe it could use a counter instead of a flag? Bump up on each press, down on each release, and the layer is active whenever the count is non-zero?
<algernon> yeah... doing so would trigger mergeLayers() each cycle, which would be costy.
<ToyKeeper> I haven't looked at that part of the code at all; no idea how it works.
<algernon> using a counter would mean we need a counter for each layer key, of which there may be... many.
<algernon> and we can't know ahead of time how many layer keys we'd have.
<algernon> an easier solution would likely be to keep track of previous layer state, and only call mergeLayers if that changes
<algernon> that's only an uint32_t and a comparison away, easier and smaller than a counter.
<algernon> OTOH, if one Fn is released, we'd clear the layer first, continue processing keys until we reach the other, and only then re-enable the layer.
<algernon> that would not be good, either.
<algernon> so perhaps we should delay layer toggling to the end of the cycle..
<algernon> ToyKeeper: can you report it as a bug against Kaleidoscope?
<algernon> I'll dump the above monologue into it then, so its captured in an issue too.
<ToyKeeper> Yeah, I was just checking if other keyboards do the same thing. It seems some do, some don't. (with other mod keys)
Investigate how to set up udev (if possible) so that ModemManager won't jump on keyboardio. At the moment when modemmanager is installed, the keyboard rarely manages to enumerate. May be a bug in modemmanager, too.
Another issue is libinput, which fails to handle the absolute positions coming from a relative device. See here for the patch that introduced this behavior. Removing the return from the error message improves things, but the virtual resolution is wrong - report both cases to libinput upstream.
Both issues will be handled by me, this is just a reminder, so I won't forget the progress we made so far.
I have a couple of plugins that would need to hook into the loop()
hooks, because their functionality does not depend on keys being pressed. Or at least, the action they trigger is best caught in the loop, where the full state is available.
Some of these plugins need to see the report (to check modifier state, for example, so they can light up LEDs), some would need to register keys for the next iteration (to keep one-shots pressed, for example)
For both of these to work, I'd need either two sets of hooks (yuck!), or call each twice, with an appropriate parameter, so the hooks themselves can decide whether to act or not. I'd prefer this, because I have a few ideas which would benefit from running the same function twice, in both places.
What do you think?
I've found it frustrating to hack on the code. I may be overly opinionated as my day job is building source control systems at a large company that loves monorepos. However, I do feel like it is more difficult to work on this project than the big repos at work!
Arduino-Boards
repo that tracks them.That we re-unite the projects that are referenced from Arduino-Boards into a single repo.
This script: https://gist.github.com/wez/81fcfdd90e5ae9cbc6fd614beacdf1fe
produced this branch as an example of what things might look like: https://github.com/wez/Kaleidoscope/tree/unify
make
(or some script in the top level dir).--follow
flag in its history view. Blame is correctly attributed, but you'll need to run some manual git log --follow
commands to review history, should you need to review history. It is possible for the migration script to use git filter-branch
to rewrite the paths in the history to workaround this, but it is labor intensive to deal with conflicting paths in the history with that approach. (I tried this and decided it was too much work to do without getting buy-in from the other contributors!)Today, it is technically possible for someone to download and use one of the libraries and use it independently from the rest of the Kaleidoscope project.
With a unified repo this is still possible, but slightly more work: they just have to move the libraries subdirectory of interest out of the repo and put it in their own libraries folder.
Building a firmware project is easiest to do from the command line. There are too many libraries to manually copy into the local sketch library folder today, so I don't believe that anyone would want to maintain those by hand. If that is a flow we want to support, we can write a little script to symlink things to make it look right.
One argument for having separate repos is that it forces the code to be more modular.
Moving to a unified repo makes it easier to make a change anywhere in the repo but doesn't inherently lead to poor choices; they're still possible in both models. For the reasons I mentioned in the rationale section, I am more inclined to put code into the wrong module just so that I can reduce the number of PRs I'd have to deal with. Moving to a monorepo means that I can more easily prepare a series of small well-encapsulated commits.
Please add them below as comments!
Volume up and down work fine. The mute button doesn't seem to do anything in OSX 10.12.5
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.