diff --git a/editor/alps64/firmware.js b/editor/alps64/firmware.js index d38bd7e8..0c92b384 100644 --- a/editor/alps64/firmware.js +++ b/editor/alps64/firmware.js @@ -55,13 +55,13 @@ keymaps = [ [ 0x2C, 0x06, 0x19, 0x0A, 0x1C, 0x18, 0x23, 0x24, ], [ 0x65, 0x05, 0x11, 0x0B, 0x0D, 0x0C, 0x25, 0x26, ], ], - no_map(), - no_map(), - no_map(), - no_map(), - no_map(), - no_map(), - no_map(), + transparent_map(), + transparent_map(), + transparent_map(), + transparent_map(), + transparent_map(), + transparent_map(), + transparent_map(), ]; fn_actions = [ diff --git a/editor/alps64/index.html b/editor/alps64/index.html index 2db78b40..f93b6431 100644 --- a/editor/alps64/index.html +++ b/editor/alps64/index.html @@ -4,201 +4,11 @@ + + - - @@ -235,82 +45,82 @@ See
-
Esc
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
0
-
-
-
=
-
Iso1
-
BSpc
+
Esc
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
0
+
-
+
=
+
Iso1
+
BSpc
-
Tab
-
Q
-
W
-
E
-
R
-
T
-
Y
-
U
-
I
-
O
-
P
-
[
-
]
-
\
+
Tab
+
Q
+
W
+
E
+
R
+
T
+
Y
+
U
+
I
+
O
+
P
+
[
+
]
+
\
-
Ctrl
-
A
-
S
-
D
-
F
-
G
-
H
-
J
-
K
-
L
-
;
-
'
-
Enter
+
Ctrl
+
A
+
S
+
D
+
F
+
G
+
H
+
J
+
K
+
L
+
;
+
'
+
Enter
-
Shift
-
Z
-
Iso2
-
X
-
C
-
V
-
B
-
N
-
M
-
,
-
.
-
/
-
Shift
-
Fn
+
Shift
+
Z
+
Iso2
+
X
+
C
+
V
+
B
+
N
+
M
+
,
+
.
+
/
+
Shift
+
Fn
-
LCtl
-
LGui
-
LAlt
-
Space
-
App
-
RAlt
-
RGui
-
RCtl
+
LCtl
+
LGui
+
LAlt
+
Space
+
App
+
RAlt
+
RGui
+
RCtl
diff --git a/editor/alps64/keymap_editor.js b/editor/alps64/keymap_editor.js index 66c3992f..aab041ec 100644 --- a/editor/alps64/keymap_editor.js +++ b/editor/alps64/keymap_editor.js @@ -1,19 +1,217 @@ /* * TMK keymap editor */ +// key id under editing +var editing_key; +// layer under editing +var editing_layer = 0; + +// load keymap on keyboard key buttons +var load_keymap_on_keyboard = function(layer, keymap) { + for (var row in keymap) { + for (var col in keymap[row]) { + var code = keymap[row][col]; + var key = keycodes[code]; + // row and column takes range of 0-32(0-9a-v) + $("#key-" + parseInt(row).toString(32) + parseInt(col).toString(32)).text(key.name); + $("#key-" + parseInt(row).toString(32) + parseInt(col).toString(32)).attr({ title: key.desc }); + } + } +}; + +$(function() { + // Title + document.title = "TMK Keymap Editor for " + KEYBOARD_DESC; + $("#page-title").text("TMK Keymap Editor for " + KEYBOARD_DESC); + + /* + * load keymap from URL hash + */ + var decoded = decode_keymap(document.location.hash.substring(1)); + if (decoded != null) { + keymaps = decoded['keymaps']; + } + + + + /* + * Layer selector + */ + $("#layer_radio").buttonset(); + + // layer change + $(".layer").click(function(ev, ui) { + var layer = parseInt($(this).attr('id').match(/layer-(\d+)/)[1]); + editing_layer = layer; + load_keymap_on_keyboard(layer, keymaps[layer]); + }); + + + + /* + * Keyboard(key buttons) + */ + // load default keymap on startup + load_keymap_on_keyboard(0, keymaps[0]); + + // Select key button to edit + $(".key").click(function(ev, ui) { + editing_key = $(this).attr('id'); + + // grey-out key to indicate being under editing + $(".key").removeClass("key-editing"); + $(this).addClass("key-editing"); + }).focus(function(ev, ui) { + // select editing_key with tab key focus + $(this).click(); + }); + + + + /* + * Keycodes button tab + */ + $("#keycode_tabs").tabs({ + heightStyle: "auto", + }); + + // Keycodes: read name and description from code table + $(".action").each(function(index) { + // get code from code button id: code-[0x]CCCC where CCCC is dec or hex number + var code = parseInt($(this).attr('id').match(/code-((0x){0,1}[0-9a-fA-F]+)/)[1]); + $(this).text(keycodes[code].name); + $(this).attr({ title: keycodes[code].desc }); + }); + + $(".action").click(function(ev,ui) { + console.log("action click"); + if (!editing_key) return; + + // get matrix position from key id: key-RC where R is row and C is column in "0-v"(radix 32) + var pos = editing_key.match(/key-([0-9a-v])([0-9a-v])/i); + if (!pos) return; + var row = parseInt(pos[1], 32), col = parseInt(pos[2], 32); + + // set text and tooltip to key button under editing + $("#" + editing_key).text($(this).text()); + $("#" + editing_key).attr({ title: $(this).attr('title'), }); + + // change keymap array + // get code from keycode button id: code-[0x]CC where CC is dec or hex number + var code = $(this).attr('id').match(/code-((0x){0,1}[0-9a-fA-F]+)/)[1]; + keymaps[editing_layer][row][col] = parseInt(code); + + // give focus on editing_key for next tab key operation + $("#" + editing_key).focus(); + }); + + + /* + * Share URL + */ + // Share URL + $("#keymap-share").click(function(ev, ui) { + var hash = encode_keymap({ keymaps: keymaps }); + $("#share-url").text(document.location.origin + document.location.pathname + "#" + hash); + }); + + // Shorten URL + $("#shorten-url").click(function(ev, ui) { + var hash = encode_keymap({ keymaps: keymaps }); + var editor_url = document.location.origin + document.location.pathname; + window.open("https://bitly.com/shorten/?url=" + encodeURIComponent(editor_url + "#" + hash)); + //window.open("http://tinyurl.com/create.php?url=" + encodeURIComponent(editor_url + "#" + hash)); + }); + + + // Hex Save + $("#keymap-download").click(function(ev, ui) { + var keymap_data = fn_actions.concat(keymaps); + var content = firmware_hex() + + hex_output(KEYMAP_START_ADDRESS, keymap_data) + + hex_eof(); + + // download hex file + var blob = new Blob([content], {type: "application/octet-stream"}); + var hex_link = $("#hex-download"); + hex_link.attr('href', window.URL.createObjectURL(blob)); + hex_link.attr('download', KEYBOARD_ID + "_firmware.hex"); + // jQuery click() doesn't work straight for 'a' element + // http://stackoverflow.com/questions/1694595/ + hex_link[0].click(); + }); + + + + /* + * Output options + */ + //$("#keymap-output").resizable(); // resizable textarea + + // Hex output + $("#keymap-hex-generate").click(function(ev, ui) { + var keymap_data = fn_actions.concat(keymaps); + $("#keymap-output").text(hex_output(KEYMAP_START_ADDRESS, keymap_data)); + }); + + // C source output + $("#keymap-source-generate").click(function(ev, ui) { + $("#keymap-output").text(source_output(keymaps)); + }); + + // JSON output + //$("#keymap-json-generate").css('display', 'none'); // hide + $("#keymap-json-generate").click(function(ev, ui) { + var keymap_output; + //keymap_output = JSON.stringify(keymaps, null, 4); + keymap_output = JSON.stringify({ keymaps: keymaps }); + $("#keymap-output").text(keymap_output); + }); + + // encode keymap + $("#keymap-encode").click(function(ev, ui) { + var keymap_output = encode_keymap({ keymaps: keymaps }); + $("#keymap-output").text(keymap_output); + }); + + // decode keymap + $("#keymap-decode").click(function(ev, ui) { + var hash = $("#keymap-output").text(); + var keymap_output = decode_keymap(hash); + $("#keymap-output").text(JSON.stringify(keymap_output)); + }); + + + + // lost keymap under edting when leave the page + /* TODO: Needed when released + $(window).bind('beforeunload', function(){ + return 'CAUTION: You will lost your change.'; + }); + */ +}); /* * Share URL */ -function encode_keymap(keymap) +function encode_keymap(obj) { - return window.btoa(JSON.stringify(keymap)); + if (typeof LZString != "undefined" && typeof Base64 != "undefined") { + return Base64.encode(LZString.compress(JSON.stringify(obj))); + } + return window.btoa(JSON.stringify(obj)); } -function decode_keymap(hash) +function decode_keymap(str) { try { - return JSON.parse(window.atob(hash)); + /* lz-string-1.3.3.js: LZString.decompress() runs away if given short string. */ + if (str == null || typeof str != "string" || str.length < 30) return null; + + if (typeof LZString != "undefined" && typeof Base64 != "undefined") { + return JSON.parse(LZString.decompress(Base64.decode(str))); + } + return JSON.parse(window.atob(str)); } catch (err) { return null; } @@ -55,12 +253,6 @@ function hex_eof() return ":00000001FF\r\n"; } -/* -function flatten(array) -{ -}; -*/ - function hex_output(address, data) { var output = ''; var line = []; @@ -100,7 +292,7 @@ function source_output(keymaps) { output += "#include \"action_macro.h\"\n"; output += "#include \"keymap.h\"\n\n"; - output += "#ifdef KEYMAP_SECTION\n"; + output += "#ifdef KEYMAP_SECTION_ENABLE\n"; output += "const uint16_t fn_actions[] __attribute__ ((section (\".keymap.fn_actions\"))) = {\n"; output += "#else\n"; output += "static const uint16_t fn_actions[] PROGMEM = {\n"; @@ -140,7 +332,7 @@ function source_output(keymaps) { output += "};\n\n"; // keymaps - output += "#ifdef KEYMAP_SECTION\n"; + output += "#ifdef KEYMAP_SECTION_ENABLE\n"; output += "const uint8_t keymaps[]["; output += keymaps[0].length; // row output += "]["; @@ -165,24 +357,7 @@ function source_output(keymaps) { } output += " },\n"; } - output += "};\n\n"; - output += "/* translates key to keycode */\n"; - output += "uint8_t keymap_key_to_keycode(uint8_t layer, key_t key)\n"; - output += "{\n"; - output += " return pgm_read_byte(&keymaps[(layer)][(key.row)][(key.col)]);\n"; - output += "}\n"; - output += "\n"; - output += "/* translates Fn index to action */\n"; - output += "action_t keymap_fn_to_action(uint8_t keycode)\n"; - output += "{\n"; - output += " action_t action;\n"; - output += " if (FN_INDEX(keycode) < sizeof(fn_actions) / sizeof(fn_actions[0])) {\n"; - output += " action.code = pgm_read_word(&fn_actions[FN_INDEX(keycode)]);\n"; - output += " } else {\n"; - output += " action.code = ACTION_NO;\n"; - output += " }\n"; - output += " return action;\n"; - output += "}\n"; + output += "};\n"; return output; }; @@ -191,7 +366,7 @@ function source_output(keymaps) { /* * keycodes */ -var code_display = [ +var keycodes = [ // {id, name(text), description(tooltip)} {id: 'NO ', name: 'NO', desc: 'No action'}, {id: 'TRNS', name: 'TRNS', desc: 'Transparent'}, @@ -408,7 +583,7 @@ var code_display = [ {id: 'FN15', name: 'J (L3)', desc: 'J with with L3(Tap key)'}, {id: 'FN16', name: 'Space (L4)', desc: 'Space with L4(Tap key)'}, {id: 'FN17', name: '; (L5)', desc: 'Semicolon with L5(Tap key)'}, - {id: 'FN18', name: '\'( L6)', desc: 'Quote with L6(Tap key)'}, + {id: 'FN18', name: '\'(L6)', desc: 'Quote with L6(Tap key)'}, {id: 'FN19', name: '/ (L7)', desc: 'Slash with with L7(Tap key)'}, /* Modifier on alpha key(Tap key, Dual-role key) */ {id: 'FN20', name: 'Space (LShift)', desc: 'Space with Left Sfhit(Tap key)'}, diff --git a/editor/common/base64.js b/editor/common/base64.js new file mode 100644 index 00000000..9cb51082 --- /dev/null +++ b/editor/common/base64.js @@ -0,0 +1,142 @@ +/** +* +* Base64 encode / decode +* http://www.webtoolkit.info/ +* +**/ + +var Base64 = { + + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + input = Base64._utf8_encode(input); + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + + } + + return output; + }, + + // public method for decoding + decode : function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + output = Base64._utf8_decode(output); + + return output; + + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; + } + +} diff --git a/editor/common/lz-string-1.0.2.js b/editor/common/lz-string-1.0.2.js new file mode 100644 index 00000000..86aafd08 --- /dev/null +++ b/editor/common/lz-string-1.0.2.js @@ -0,0 +1,204 @@ +// Copyright © 2013 Pieroxy +// This work is free. You can redistribute it and/or modify it +// under the terms of the WTFPL, Version 2 +// For more information see LICENSE.txt or http://www.wtfpl.net/ +// +// LZ-based compression algorithm, version 1.0.2-rc1 +var LZString = { + + writeBit : function(value, data) { + data.val = (data.val << 1) | value; + if (data.position == 15) { + data.position = 0; + data.string += String.fromCharCode(data.val); + data.val = 0; + } else { + data.position++; + } + }, + + writeBits : function(numBits, value, data) { + if (typeof(value)=="string") + value = value.charCodeAt(0); + for (var i=0 ; i> 1; + } + }, + + produceW : function (context) { + if (Object.prototype.hasOwnProperty.call(context.dictionaryToCreate,context.w)) { + if (context.w.charCodeAt(0)<256) { + this.writeBits(context.numBits, 0, context.data); + this.writeBits(8, context.w, context.data); + } else { + this.writeBits(context.numBits, 1, context.data); + this.writeBits(16, context.w, context.data); + } + this.decrementEnlargeIn(context); + delete context.dictionaryToCreate[context.w]; + } else { + this.writeBits(context.numBits, context.dictionary[context.w], context.data); + } + this.decrementEnlargeIn(context); + }, + + decrementEnlargeIn : function(context) { + context.enlargeIn--; + if (context.enlargeIn == 0) { + context.enlargeIn = Math.pow(2, context.numBits); + context.numBits++; + } + }, + + compress: function (uncompressed) { + var context = { + dictionary: {}, + dictionaryToCreate: {}, + c:"", + wc:"", + w:"", + enlargeIn: 2, // Compensate for the first entry which should not count + dictSize: 3, + numBits: 2, + result: "", + data: {string:"", val:0, position:0} + }, i; + + for (i = 0; i < uncompressed.length; i += 1) { + context.c = uncompressed.charAt(i); + if (!Object.prototype.hasOwnProperty.call(context.dictionary,context.c)) { + context.dictionary[context.c] = context.dictSize++; + context.dictionaryToCreate[context.c] = true; + } + + context.wc = context.w + context.c; + if (Object.prototype.hasOwnProperty.call(context.dictionary,context.wc)) { + context.w = context.wc; + } else { + this.produceW(context); + // Add wc to the dictionary. + context.dictionary[context.wc] = context.dictSize++; + context.w = String(context.c); + } + } + + // Output the code for w. + if (context.w !== "") { + this.produceW(context); + } + + // Mark the end of the stream + this.writeBits(context.numBits, 2, context.data); + + // Flush the last char + while (context.data.val>0) this.writeBit(0,context.data) + return context.data.string; + }, + + readBit : function(data) { + var res = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = 32768; + data.val = data.string.charCodeAt(data.index++); + } + //data.val = (data.val << 1); + return res>0 ? 1 : 0; + }, + + readBits : function(numBits, data) { + var res = 0; + var maxpower = Math.pow(2,numBits); + var power=1; + while (power!=maxpower) { + res |= this.readBit(data) * power; + power <<= 1; + } + return res; + }, + + decompress: function (compressed) { + var dictionary = {}, + next, + enlargeIn = 4, + dictSize = 4, + numBits = 3, + entry = "", + result = "", + i, + w, + c, + errorCount=0, + literal, + data = {string:compressed, val:compressed.charCodeAt(0), position:32768, index:1}; + + for (i = 0; i < 3; i += 1) { + dictionary[i] = i; + } + + next = this.readBits(2, data); + switch (next) { + case 0: + c = String.fromCharCode(this.readBits(8, data)); + break; + case 1: + c = String.fromCharCode(this.readBits(16, data)); + break; + case 2: + return ""; + } + dictionary[3] = c; + w = result = c; + while (true) { + c = this.readBits(numBits, data); + + switch (c) { + case 0: + if (errorCount++ > 10000) return "Error"; + c = String.fromCharCode(this.readBits(8, data)); + dictionary[dictSize++] = c; + c = dictSize-1; + enlargeIn--; + break; + case 1: + c = String.fromCharCode(this.readBits(16, data)); + dictionary[dictSize++] = c; + c = dictSize-1; + enlargeIn--; + break; + case 2: + return result; + } + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + if (dictionary[c]) { + entry = dictionary[c]; + } else { + if (c === dictSize) { + entry = w + w.charAt(0); + } else { + return null; + } + } + result += entry; + + // Add w+entry[0] to the dictionary. + dictionary[dictSize++] = w + entry.charAt(0); + enlargeIn--; + + w = entry; + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + } + return result; + } +};