geekhack
geekhack Projects => Making Stuff Together! => Topic started by: tofgerl on Tue, 17 November 2015, 12:54:37
-
I've been thinking about this for a while, and I have some ideas. The idea is that this little CLI tool can be used by anyone who wants to participate in a GB by simply feeding it two files - one JSON file with the kits available in the GB, and one JSON file with his keyboard configuration. Obviously the most common keyboards will simply be included in the tool, but I still need a good format for it.
The TMK format won't do, since it just lists the keys in one single row with no width information, ISO specifications or anything else.
The Keyboard Layout Editor format is better, but it's extremely messy and is mostly made for formatting information specific to KLE.
So I've decided I need to make a standard keyboard layout JSON format, unless anyone has a better idea. My idea is to simply give each key in the universe it's own unique name.
The format is like this: 'position.width.legend'.
So 'K' would be a normal K-key, while a 'shift.225' would be the normal left shift, and 'caps.175.control' would be a caps-lock key with the word 'control' instead of whatever else would be there. This is not perfect, but it's better than anything else.
But I still have some wrinkles to figure out. For example, what about novelty keys? For Carbon for example there's a key with 1.25u width and just a carbon symbol on the front. It can be used for any 1.25u key, and the legend cannot easily be described.
Suggestions?
I'm not going to bother with making it unless it will actually work, and if any keyset with novelty keys can't be used with the tool, what's the point?
-
I'm slightly confused what this is intended to do, but I'm guessing it's supposed to work out which kits you need to buy to cover your custom board.
I'd suggest that most buys have standard legends as well as novelties for the mods, if not in the definition of the (e.g) Carbon key that is used to replace Win would have to be called Win when the designer writes the code for the set. If people want more novelties they can always add sets to their order manually, but your tool will get them a basic working set.
Or novelties could be called generic "Novelty A" etc and which picture that is is detailed in the thread?
The other fun things you'll need are homing keys and profiles (if it's a profiled set) - QWERTZ people may have a 1u Z in the base kit but it won't work, unless this would be called an "R4.Z" key? Then when the newbie wants an "R4.Z" in a non profiled set there won't be one unless the designer added it to the main kit even though it doesn't exist, that could be fun...
I'll keep thinking about this but wanted to post to check I'm thinking along the right lines :)
-
Yep, right lines. You're right about the homing keys.
And I want to specify that the dot-syntax is only for manually adding keys and talking about it here, in the actual JSON it will be filled out into {keycode: 'k', width: 225, legend: 'K'} for example. Way too much bother otherwise.
-
So you will need {keycode: 'k', row: '3', width: '1', legend: 'K', homing: 'no'} to cover the entire definition of a standard K key? I think I'd get rid of the keycode, unless the row and lack of homing was somehow defined by the letter.
Also you'll also have to input the price of the add-on kits to decide whether it's cheaper to buy two small ones or one big one to get the caps required?
Not saying it's impossible but this would certainly be interesting to code :)
-
I would stick with the KLE layout and just use a subset of the data. You can add more data to it if you need, like homing keys. This is what I an doing for my builder. That way people have a ui to create their layout and can then just do a once over at the end to tweak things that KLE does not support. My 0.02$ anyway...
-
Hi, cool idea!
In my mind, wouldn't the 'defining characteristics' of a given keycap simply be its legend, width, and profile? (and associated 'design')
So {
'design': 'Quartz',
'legend': 'K',
'width': '1',
'type': {
'profile': 'DSA', 'row': '4'
}
}
or
{
'design': 'Nuclear Data',
'legend': 'K',
'width': '1',
'type': {
'profile': 'DSA', 'row': ''
}
}
then the next level up would bundle those into the particular kits spec'd with a given keycode arrangement.
{'Kit': 'Base', 'arrangement' : '60%', { 'Keycaps': { [...{ 'keycode' : 'b' , cap : { 'design': 'Nuclear Data', 'legend': 'K', 'width': '1', 'type': ['profile': 'SA', 'row': '']}}...]
Then a novelty key such as { 'design': 'Nuclear Data', 'legend': 'Beta Symbol', 'width': '1', 'type': ['profile': 'SA', 'row': '']}}
could be easily shown as a suitable replacement based on having the same 'width' and 'type'.
I have some more ideas, but wanted to at least put that out there. Cheers.
PSA: New to GH so apologies if the nomenclature's off.
-
Hi, cool idea!
In my mind, wouldn't the 'defining characteristics' of a given keycap simply be its legend, width, and profile? (and associated 'design')
So {'design': 'Quartz', 'legend': 'K', 'width': '1', 'type': ['profile': 'DSA', 'row': '4']}
or {'design': 'Nuclear Data', 'legend': 'K', 'width': '1', 'type': ['profile': 'DSA', 'row': '']}
then the next level up would bundle those into the particular kits spec'd with a given keycode arrangement.
{'Kit': 'Base', 'arrangement' : '60%', { 'Keycaps': { [...{ 'keycode' : 'b' , cap : { 'design': 'Nuclear Data', 'legend': 'K', 'width': '1', 'type': ['profile': 'SA', 'row': '']}}...]
Then a novelty key such as { 'design': 'Nuclear Data', 'legend': 'Beta Symbol', 'width': '1', 'type': ['profile': 'SA', 'row': '']}}
could be easily shown as a suitable replacement based on having the same 'width' and 'type'.
I have some more ideas, but wanted to at least put that out there. Cheers.
PSA: New to GH so apologies if the nomenclature's off.
You will never get someone to fill out 104 keys of that huge and detailed format by hand and without a ui.
If you want adoption you have to make it easy for people.
-
those weren't referring to what the end user would populate, those would be for the keycap sets (and would largely be copy and paste between like sets with the exception of the custom modifiers).
I envisioned users makes files like:
{
'Groups' : [
{
'family' : 'Nuclear Data',
'kit' : 'Base Set'
}
],
'Singles' : [
{
keycode : 'b',
mod : {
'family' : 'Nuclear Data',
'legend' : 'Beta Symbol'
}
}
]
}
Maybe you short code as:
G:BaseSet,
S:[b.BetaSymbol]
and let the program pull family name from the submitted dataset.
So the commands would be: KitDecoderApp MyWishlist.json NuclearData.json Pok3r.json
And would output:
You Need: Base Set, Radiation Kit
You could also optionally build in keyboard layouts instead of Pok3r.json (but still allow for you to submit your own for custom layout) and add optional flags for 'replace-all' vs
'modify-differences-only'.
The main idea for me was decoupling the keycaps, from the layout, from the users desired modification.
I'm confused by your mention of a UI are you meaning graphical like KLE? If so that seems at odds with what OP was referring to with CLI.
Lastly, I agree with your point on adoption requiring simplicity, but feel that's more relative to presentation than data structure. In my mind, the data "is what is is", so whatever the characteristics of a keycap are, would need to be modeled, and then the burden is one the program to make the presented information easily understood.
*Typed from mobile so may be some errors.
-
Is the purpose of this tool to:
1) Have a json file representing all of the kits available in a GB (which would probably be provided by the GB creator)
2) Allow an end user to specify a custom layout in addition to the json file of kits available provided by the creator and be given a list of kits they need to buy in order to be compatible with the layout.
Am I close?
If this is the case, I think you should be accepting KLE from the end user to describe their custom layout because most (I would say 95%) will already have a KLE layout saved for their custom layout because that tool is likely where they were prototyping their ideas. Even if they were not using KLE before, the KLE UI makes it easy for an end user to specify what they want on the keyboard through a UI without having to figure things out in a text file. I have worked a lot with a json representation of a keyboard and trust me when I say that people won't do it by hand from scratch...
Maybe I am missing the point here, but that is my understanding...
-
If someone make a bot like the one 7bit, from Deskthority, uses to organize his group buys, that would be a good step.
-
A KLE2whatever would definitely be a part of it, yes. If not for anything else, I would need it to make the default keyboard JSONs. I sure as hell am not handcoding it ;)
But KLE doesn't have all the information necessary, that's why I need a custom format in the first place.
I've also been thinking about a function where you supply a picture of a standard 104key rendered or just mocked up with to look like the keyset - with standard dimensions, color specifications and so on of couse + and the tool cuts the picture up so that it can render a 60%, or a 660M or even a Planck, if you specify the key layout of your Planck.
That way the GB owner could just supply a zip-file and everyone could see what their specific keyboard would look like (horribly rendered) with that keyset.
There might need to be two tools in the end, a producer and a consumer... No reason for every customer in the community to install imagemagick and stuff after all.
-
A KLE2whatever would definitely be a part of it, yes. If not for anything else, I would need it to make the default keyboard JSONs. I sure as hell am not handcoding it ;)
But KLE doesn't have all the information necessary, that's why I need a custom format in the first place.
Right. That makes sense. I did the same thing for http://builder.swillkb.com. I have a bunch of labels which start with an underscore (_) to represent my own custom labels which are not native to KLE. That is just a simple convention so I didn't have to worry about name collision with KLE.
For example, here are some of the things my tool supports which is not supported by KLE:
{_t:<0-2>}: Change switch cutout type. Numbers are in the same order as the images. EG: {_t:0},""
{_s:<0-2>}: Change stabilizer cutout type. Numbers are in the same order as the dropdown list. EG: {_s:2},""
{_k:<mm>}: Specify a kerf value for this specific switch/stabilizer cutout which overrides the default. Values are in mm (without the unit). EG: {_k:0.05},""
{_r:<degrees>}: Rotate the switch cutout independent of the stabilizer cutout (assuming there is one). EG: {_r:90},""
{_rs:<degrees>}: Rotate the stabilizer cutout independent of the switch cutout. EG: {_rs:180},""
Your converter 'KLE2whatever' could take global variables to populate a bunch of the additional values specific to your tool. I use globals for things like 'switch type' etc, but you can override those globals on a key by key basis if you want.
I have spent quite a bit of time developing against the KLE format and adding my own functionality on top of it, so if you want suggestions on specific things just let me know. What language do you plan to use? I have written KLE parsers in both Golang and Python.
I have open sourced the original Python builder I wrote, so you can check that code if you are planning to do this in Python.
Here is where I parse the layout: https://github.com/swill/kb_builder/blob/master/lib/builder.py#L213
Here is where I use special layout fields: https://github.com/swill/kb_builder/blob/master/lib/builder.py#L313
My Golang code is simpler (in some ways) because I marshal everything from JSON into Golang structs, but the code is similar.
-
That's great! I was thinking Node.js, since that's what I'm using at the moment, but Python has a bigger installbase, and this might be a good reason the get back into it.
I'll think more about this and start coding this weekend I think.
The basic functionality of finding keycap A to fit keyboard B's position C shouldn't be more than a hundred lines of code anyway.
-
That's great! I was thinking Node.js, since that's what I'm using at the moment, but Python has a bigger installbase, and this might be a good reason the get back into it.
I'll think more about this and start coding this weekend I think.
The basic functionality of finding keycap A to fit keyboard B's position C shouldn't be more than a hundred lines of code anyway.
If there is anything I can help with, just let me know. If you are looking for something that is very cross platform friendly, I can't recommend Golang enough. You can cross compile your code into binaries for every OS platform and the end users don't have to have any specific runtime.
Golang takes a little getting used to, but it is a dream to work in once you get the hang of it. I have a bunch of code written already (obviously) for parsing KLE in Golang which I can give you if you want to go that way.
Here is what my Key representation is in Golang right now:
type Key struct {
Width float64 `json:"w"` // width in key units
Height float64 `json:"h"` // height in key units
Xpos float64 `json:"x"` // x position in key units
Ypos float64 `json:"y"` // y position in key units
Type int `json:"_t"` // switch type as int
Stab int `json:"_s"` // stab type as int
Kerf float64 `json:"_k"` // kerf for this key
Stacked bool
Bounds Points
Rotate float64 `json:"_r"` // rotate switch opening in degrees
RotateStab float64 `json:"_rs"` // rotate stabilizer opening in degrees
RotateCluster float64 `json:"r"` // rotate the following cluster of keys (in degrees)
Xcluster float64 `json:"rx"` // x position of the cluster
Ycluster float64 `json:"ry"` // y position of the cluster
}
Its nice because it will automatically parse the JSON file into your struct based on the `json:"name"` construct...
-
I keep hearing good things about Go, but I don't have the time to learn a new language right now. I think I might try node.js first, just to get going, and port it to Python once I have the basic functionality going. I hate starting from scratch with a language I don't know well.
-
I keep hearing good things about Go, but I don't have the time to learn a new language right now. I think I might try node.js first, just to get going, and port it to Python once I have the basic functionality going. I hate starting from scratch with a language I don't know well.
Yep, totally understood. Node is a good choice because you get the json handling natively... :)
-
Well, that's a bonus, but not a big one.
-
keeping a tab on this one
if you will keep a repo on github for this i volunteer to help out (edit: provided this is still going to be written in nodejs of course. i'm useless at python)
-
I need to think more about this. The amount of JSON fields for each key tells me that there has to be some error in the way I'm thinking about the layout.
-
I need to think more about this. The amount of JSON fields for each key tells me that there has to be some error in the way I'm thinking about the layout.
I have not put too much thought into this yet, but this is what I would probably do.
In the GB JSON, I would define shorthands for the color combos (since this is likely going to be the main thing that changes between GBs).
Something like this:
// colors: describe the color combos, labels could change...
{
"lite": {
"key":"#FFFFFF",
"txt":"#000000"
},
"dark": {
"key":"#000000",
"txt":"#FFFFFF"
}
}
// layout (numpad example)
[
[{"color":"dark"},"Num Lock",{"color":"dark"},"/",{"color":"dark"},"*",{"color":"dark"},"-"],
[{"color":"lite"},"7\nHome",{"color":"lite"},"8\n↑",{"color":"lite"},"9\nPgUp",{h:2,"color":"dark"},"+"],
[{"color":"lite"},"4\n←",{"color":"lite"},"5",{"color":"lite"},"6\n→"],
[{"color":"lite"},"1\nEnd",{"color":"lite"},"2\n↓",{"color":"lite"},"3\nPgDn",{h:2,"color":"dark"},"Enter"],
[{w:2,"color":"lite"},"0\nIns",{"color":"lite"},".\nDel"]
]
Am I missing anything? This would allow someone to easily modify their KLE layout to specify how they want their layout to look. You can easily determine what keys are needed based on the 'color' object. Then you can inventory things as needed and determine which kits are needed for the layout.
-
Oh, I don't care about the colours. That's just going to be specified as a root field called 'Keyset Name'.
It's the different sized keys that worry me. Ctrl.125, Ctrl.100 and Ctrl.150 are different keys and need to be treated differently.
And then there's the secondary problem of Caps.175.Control - the caps lock placement key with Control legend.
But if I skip the secondary problem for now, I might be able to work it out with just having size as an optional field, and using the normal ANSI layout as the default. Oh, and ISO will have to be a "size" for the enter key, which is interesting.
So it would look something like this:
{
"Keyboard": {
"Name": "Default 60%",
"Number of keys": 61,
"Layout": "ANSI",
"Rows": 5,
"Keys": [
["ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "BSPC"],
["TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\"],
["CAPS", "A", "S", "D", {"Key":"F", "Homing": true}, "G", "H", {"Key":"J","Homing": true},"K", "L", ";", "'", "ENTR" ],
["LSFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "RSFT"],
["LCTR", "LGUI", "LALT", "SPC", "RALT", "RGUI", "MENU", "RCTR"]
]
}
}
Although it might be better like this:
"Keys": {
"R1": ["ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "BSPC"],
"R2": ["TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\"],
"R3": ["CAPS", "A", "S", "D", {"Key":"F", "Homing": true}, "G", "H", {"Key":"J","Homing": true},"K", "L", ";", "'", "ENTR" ],
"R4": ["LSFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "RSFT"],
"R5": ["LCTR", "LGUI", "LALT", "SPC", "RALT", "RGUI", "MENU", "RCTR"]
}
And of course, the WKL layout would look like this:
{
"Keyboard": {
"Name": "Winkeyless",
"Number of keys": 58,
"Layout": "ANSI",
"Rows": 5,
"Keys": {
"R1": ["ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "BSPC"],
"R2": ["TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\"],
"R3": ["CAPS", "A", "S", "D", {"Key": "F","Homing": true}, "G", "H", {"Key": "J","Homing": true}, "K", "L", ";", "'", "ENTR"],
"R4": ["LSFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "RSFT"],
"R5": [{"Key:": "LCTR","Width": 150}, {"Key": "LALT","Width": 150}, {"Key": "SPC","Width": 700}, {"Key": "RALT","Width": 150}, {"Key": "RCTR","Width": 150}]
}
}
}
And finally, my personal Pok3r layout in a flattened JSON layout:
{
"Keyboard.Name": "Default 60%",
"Keyboard.Number of keys": 61,
"Keyboard.Layout": "ANSI",
"Keyboard.Rows": 5,
"Keyboard.Keys.R1.0": "ESC",
"Keyboard.Keys.R1.1": "1",
"Keyboard.Keys.R1.2": "2",
"Keyboard.Keys.R1.3": "3",
"Keyboard.Keys.R1.4": "4",
"Keyboard.Keys.R1.5": "5",
"Keyboard.Keys.R1.6": "6",
"Keyboard.Keys.R1.7": "7",
"Keyboard.Keys.R1.8": "8",
"Keyboard.Keys.R1.9": "9",
"Keyboard.Keys.R1.10": "0",
"Keyboard.Keys.R1.11": "-",
"Keyboard.Keys.R1.12": "=",
"Keyboard.Keys.R1.13": "BSPC",
"Keyboard.Keys.R2.0": "TAB",
"Keyboard.Keys.R2.1": "Q",
"Keyboard.Keys.R2.2": "W",
"Keyboard.Keys.R2.3": "E",
"Keyboard.Keys.R2.4": "R",
"Keyboard.Keys.R2.5": "T",
"Keyboard.Keys.R2.6": "Y",
"Keyboard.Keys.R2.7": "U",
"Keyboard.Keys.R2.8": "I",
"Keyboard.Keys.R2.9": "O",
"Keyboard.Keys.R2.10": "P",
"Keyboard.Keys.R2.11": "[",
"Keyboard.Keys.R2.12": "]",
"Keyboard.Keys.R2.13": "\\",
"Keyboard.Keys.R3.0.Key": "LCTR",
"Keyboard.Keys.R3.0.Width": 175,
"Keyboard.Keys.R3.1": "A",
"Keyboard.Keys.R3.2": "S",
"Keyboard.Keys.R3.3": "D",
"Keyboard.Keys.R3.4.Key": "F",
"Keyboard.Keys.R3.4.Homing": true,
"Keyboard.Keys.R3.5": "G",
"Keyboard.Keys.R3.6": "H",
"Keyboard.Keys.R3.7.Key": "J",
"Keyboard.Keys.R3.7.Homing": true,
"Keyboard.Keys.R3.8": "K",
"Keyboard.Keys.R3.9": "L",
"Keyboard.Keys.R3.10": ";",
"Keyboard.Keys.R3.11": "'",
"Keyboard.Keys.R3.12": "ENTR",
"Keyboard.Keys.R4.0": "LSFT",
"Keyboard.Keys.R4.1": "Z",
"Keyboard.Keys.R4.2": "X",
"Keyboard.Keys.R4.3": "C",
"Keyboard.Keys.R4.4": "V",
"Keyboard.Keys.R4.5": "B",
"Keyboard.Keys.R4.6": "N",
"Keyboard.Keys.R4.7": "M",
"Keyboard.Keys.R4.8": ",",
"Keyboard.Keys.R4.9": ".",
"Keyboard.Keys.R4.10": "/",
"Keyboard.Keys.R4.11": "RSFT",
"Keyboard.Keys.R5.0.Key": "FN",
"Keyboard.Keys.R5.0.Width": 125,
"Keyboard.Keys.R5.1": "LALT",
"Keyboard.Keys.R5.2": "LGUI",
"Keyboard.Keys.R5.3": "SPC",
"Keyboard.Keys.R5.4": "RGUI",
"Keyboard.Keys.R5.5": "RALT",
"Keyboard.Keys.R5.6.Key": "FN",
"Keyboard.Keys.R5.6.Width": 125,
"Keyboard.Keys.R5.7": "RCTR"
}
Anywho... Thoughts?
Edit: Ironically I wrote all this on my macbook pro, and now my hands hurt...
-
Why are you not just using the KLE 'w' (width) field combined with the 'label' for the different possibilities? I would limit your matching to only the main key and not the function layer of what the user supplies. I would also translate their input to a specific format according to what is supported in the GB.
For example, if the GB happens to support the following keys (subset, for example):
{w:1.75},"Control"
{w:1.5},"Ctrl"
{w:1.25},"Ctrl"
{w:1.75},"Caps Lock"
etc...
Then you can have a map of control legends:
map = {
"ctrl":{
"sizes":[1.25, 1.5],
"aliases":["control"]
},
"control":{
"sizes":[1.75],
"aliases":["ctrl"]
},
}
Then you parse the user input (eg, something like: {w:1.75},"CTRL\nfn_bind") like this to find a matching key:
# when you parse the label, you only take the main key and you change it to match your control structure, eg:
label = input_str.split("\\n")[0].strip().lower()
# so you would end up with a 'key' with something like this
key = {"size":1.75, "label":"ctrl"}
Then when you try to match the key to an available key in the group buy, you would do something like this.
if key["label"] in map:
if key["size"] in map[key["label"]]["sizes"]:
# handle the match
return
else:
for alias in map[key["label"]]["aliases"]:
if key["size"] in alias["sizes"]:
#handle the match
return
# handle no match found...
If I spend more time thinking about this I can probably come up with a cleaner solution, but this is what I came up with off the top of my head. This will ensure that as long as someone enters something that can be translated to something that is in the map, we can find a key for them (assuming a key exists). Yes, the guy wanted a 1.75 unit "CTRL" key, but that does not exist. However, we were able to find him a match for a 1.75 unit "Control" key, which is as good as anyone can expect.
This make any sense?
Edit: BTW, I would only handle the non-one-unit keys in the map to keep things simple...
-
First of all, KLE uses improper JSON, placing for some reason the width in a different field than the key itself, making it effing impossible to use any schema or proper formatting algorithms.
Secondly, that's what I am doing, it's just not clear because as I said, that problem comes next, and I'm trying to solve the first problem first. The way I lay it out now all metadata can be inserted as new fields in the individual key objects. Or maps, as you say, or array which it actually is. God I hate people who use words that already means something to describe something else in another language just because it seems easier at that time...
Edit: Ideally the output would be something like
You need these sets: Base, modifiers, numpad.
For proper labelling, you need these sets: Alternate modifiers (CTRL 1.75U, CAPS 1.25U)
where the keys in parens are the ones specifically requested for those sets.
Also, including prices might be fun. And quite easy...
And something like "You are ordering X extra keys that will not be needed, at a theoretical cost of Y."
-
Ya. The KLE format is annoying as FCK, I will give you that. :). It was a bloody mess building a parser for it the first time I tried.
-
I'm actually considering strongly ripping off your parser as a feature of my tool, which now has a name: Clacky
Completely unnecessary and basically empty repo here: https://github.com/to***erl/clacky http://bit.ly/1jjnpK8
Ohforf.... I can't even link my own effing github repo? I hate this forum software! Why don't they just use BB or IPB like everyone else in the whole world???
Anyway, the idea is that if you have either a TMK .c-file, or a KLE .json-file, you should be able to upgrade(!) that file to a proper file and then use that for anything else Clacky does. Which is nothing yet. :)
-
I'm actually considering strongly ripping off your parser as a feature of my tool, which now has a name: Clacky
Completely unnecessary and basically empty repo here: https://github.com/to***erl/clacky http://bit.ly/1jjnpK8
Ohforf.... I can't even link my own effing github repo? I hate this forum software! Why don't they just use BB or IPB like everyone else in the whole world???
Feel free to rip off my parser. That's why I posted it. :)
No reason to start from scratch when time and effort has already been spent building a parser. ;)