twnlua: bindgen.py capable of converting share/twn_api.h spec to lua bindings

This commit is contained in:
veclavtalica 2024-12-23 20:59:00 +03:00
parent c4c097f050
commit e7ed72dfc0
6 changed files with 540 additions and 212 deletions

View File

@ -6,7 +6,7 @@ insert_final_newline = true
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.{c,h}] [*.{c,h,py}]
indent_style = space indent_style = space
indent_size = 4 indent_size = 4

View File

@ -8,10 +8,19 @@ endif()
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR}) add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
COMMAND ${PYTHON3} ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json > ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py
)
set(SOURCE_FILES set(SOURCE_FILES
game.c game.c
state.h state.h
${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
lua/src/lapi.c lua/src/lapi.c
lua/src/lapi.h lua/src/lapi.h
lua/src/lauxlib.c lua/src/lauxlib.c

112
apps/twnlua/bindgen.py Executable file
View File

@ -0,0 +1,112 @@
#!/bin/env python3
import sys, json
with open(sys.argv[1], 'r') if sys.argv[1] != "-" else sys.stdin as f:
api_source = f.read()
api = json.loads(api_source)
def default(parameter):
basetype = parameter["type"].rsplit(' *', 1)[0]
if parameter["type"] == "float":
return parameter["default"]
elif parameter["type"] == "bool":
return "true" if parameter["default"] else "false"
elif parameter["type"] == "char *":
if parameter["default"] == {}:
return "NULL"
else: return '"' + parameter["default"] + '"'
elif basetype in api["types"]:
if parameter["type"].endswith("*"):
if parameter["default"] == {}:
return "NULL"
else:
return "&(%s){\n%s\n }" % \
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
else:
return "(%s){\n%s\n }" % \
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
raise BaseException("Unhandled default value of type '%s'" % parameter["type"])
print('#include "twn_game_api.h"')
print('#include <lua.h>')
print('#include <lualib.h>')
print('#include <lauxlib.h>\n')
for typename, typedesc in api["types"].items():
converter = "static %s table_to_%s(lua_State *L, int idx) {\n" % (typename, typename.lower())
converter += " %s %s;\n" % (typename, typename.lower());
if not "fields" in typedesc:
continue
for field in typedesc["fields"]:
converter += " lua_getfield(L, idx, \"%s\");\n" % (field["name"]);
if field["type"] == "float":
converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]);
elif field["type"] == "uint8_t":
converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]);
else:
raise BaseException("Unhandled converter field type '%s'" % (field["type"]))
converter += " lua_pop(L, 1);\n";
converter += " return %s;\n}\n" % (typename.lower())
print(converter)
for procedure, procedure_desc in api["procedures"].items():
binding = "static int binding_%s(lua_State *L) {\n" % procedure
binding += " luaL_checktype(L, 1, LUA_TTABLE);\n"
if "params" in procedure_desc:
for parameter in procedure_desc["params"]:
basetype = parameter["type"].rsplit(' *', 1)[0]
if parameter["type"].endswith("*"):
binding += " %s %s_value;\n" % (basetype, parameter["name"])
binding += " %s %s;\n" % (parameter["type"], parameter["name"])
binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"]
if "default" in parameter:
binding += " if (lua_isnoneornil(L, -1))\n"
binding += " %s = %s;\n" % (parameter["name"], default(parameter))
binding += " else\n "
if parameter["type"] == "float":
binding += " %s = (float)lua_tonumber(L, -1);\n" % (parameter["name"]);
elif parameter["type"] == "bool":
binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]);
elif parameter["type"] == "char *":
binding += " %s = lua_tostring(L, -1);\n" % (parameter["name"]);
elif basetype in api["types"]:
if "enums" in api["types"][basetype]:
binding += " %s = lua_tointeger(L, -1);\n" % (parameter["name"]);
elif parameter["type"].endswith("*"):
binding += " { %s_value = table_to_%s(L, -1); %s = &%s_value; }\n" % (parameter["name"], basetype.lower(), parameter["name"], parameter["name"]);
else:
binding += " %s = table_to_%s(L, -1);\n" % (parameter["name"], basetype.lower());
else:
raise BaseException("Unhandled parameter type '%s'" % (parameter["type"]))
binding += " %s(%s);\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
binding += "}\n"
print(binding)
loader = "void bindgen_load_%s(lua_State *L) {\n" % api["name"]
modules = set(api["procedures"][procedure]["module"] for procedure in api["procedures"])
for module in modules:
loader += " lua_newtable(L);\n"
for procedure, procedure_desc in api["procedures"].items():
if procedure_desc["module"] == module:
loader += " lua_pushstring(L, \"%s\");\n" % procedure_desc["symbol"]
loader += " lua_pushcfunction(L, binding_%s);\n" % procedure
loader += " lua_settable(L, -3);\n"
loader += " lua_setglobal(L, \"%s\");\n" % module
loader += "}"
print(loader)

View File

@ -9,17 +9,8 @@ function game_tick()
color = { r = 127, g = 0, b = 127, a = 255 }, color = { r = 127, g = 0, b = 127, a = 255 },
} }
input.action {
name = "move_up",
control = input.CONTROL_L,
}
if input.action_pressed "move_up" then
draw.text { string = "BOO!" }
end
draw.sprite { draw.sprite {
texture = "/assets/title.png", path = "/assets/title.png",
rect = { rect = {
x = 320 - (320 / 2), x = 320 - (320 / 2),
y = 180 - (128 / 2), y = 180 - (128 / 2),
@ -29,7 +20,7 @@ function game_tick()
} }
draw.text { draw.text {
string = "IT KEEPS HAPPENING", string = "it never happened",
position = offset, position = offset,
font = "/fonts/kenney-pixel.ttf", font = "/fonts/kenney-pixel.ttf",
} }

View File

@ -10,6 +10,10 @@
#include <malloc.h> #include <malloc.h>
/* generated by bindgen.py */
void bindgen_load_twn(lua_State *L);
/* require will go through physicsfs exclusively so that scripts can be in the data dir */ /* require will go through physicsfs exclusively so that scripts can be in the data dir */
static int physfs_loader(lua_State *L) { static int physfs_loader(lua_State *L) {
const char *name = luaL_checkstring(L, 1); const char *name = luaL_checkstring(L, 1);
@ -37,201 +41,6 @@ static int physfs_loader(lua_State *L) {
} }
static Rect table_to_rect(lua_State *L, int idx, const char *name) {
/* types are checked here to help prevent unexpected results */
Rect rect;
int is_num;
lua_getfield(L, idx, "x");
rect.x = (float)lua_tonumberx(L, -1, &is_num);
if (!is_num)
luaL_error(L, "bad field 'x' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
lua_pop(L, 1);
lua_getfield(L, idx, "y");
rect.y = (float)lua_tonumberx(L, -1, &is_num);
if (!is_num)
luaL_error(L, "bad field 'y' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
lua_pop(L, 1);
lua_getfield(L, idx, "w");
rect.w = (float)lua_tonumberx(L, -1, &is_num);
if (!is_num)
luaL_error(L, "bad field 'w' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
lua_pop(L, 1);
lua_getfield(L, idx, "h");
rect.h = (float)lua_tonumberx(L, -1, &is_num);
if (!is_num)
luaL_error(L, "bad field 'h' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
lua_pop(L, 1);
return rect;
}
static Color table_to_color(lua_State *L, int idx) {
Color color;
lua_getfield(L, idx, "r");
color.r = (uint8_t)lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "g");
color.g = (uint8_t)lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "b");
color.b = (uint8_t)lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "a");
color.a = (uint8_t)lua_tointeger(L, -1);
lua_pop(L, 1);
return color;
}
/* sprite(data [table]) */
/* data should contain the following fields: */
/*
* path [string]
* rect [table]
* texture_region [table], optional
* color [table], optional
* rotation [number], optional
* flip_x [boolean], optional
* flip_y [boolean], optional
* stretch [boolean], optional
*/
static int b_sprite(lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
DrawSpriteArgs args = { 0 };
lua_getfield(L, 1, "path");
const char *field_path = lua_tostring(L, -1);
if (field_path == NULL)
luaL_error(L, "bad field 'path' in 'data' (string expected, got %s)", luaL_typename(L, -1));
args.path = field_path;
lua_getfield(L, 1, "rect");
if (!lua_istable(L, -1))
luaL_error(L, "bad field 'rect' in 'data' (table expected, got %s)", luaL_typename(L, -1));
args.rect = table_to_rect(L, -1, "data.rect");
lua_getfield(L, 1, "texture_region");
if (lua_istable(L, -1)) {
args.texture_region_opt = table_to_rect(L, -1, "data.texture_region");
args.texture_region_opt_set = true;
}
lua_getfield(L, 1, "color");
if (lua_istable(L, -1)) {
args.color_opt = table_to_color(L, -1);
args.color_opt_set = true;
}
lua_getfield(L, 1, "rotation");
if (lua_isnumber(L, -1)) {
args.rotation_opt = (float)lua_tonumber(L, -1);
args.rotation_opt_set = true;
}
lua_getfield(L, 1, "flip_x");
if (lua_isboolean(L, -1)) {
args.flip_x_opt = lua_toboolean(L, -1);
args.flip_x_opt_set = true;
}
lua_getfield(L, 1, "flip_y");
if (lua_isboolean(L, -1)) {
args.flip_y_opt = lua_toboolean(L, -1);
args.flip_y_opt_set = true;
}
lua_getfield(L, 1, "stretch");
if (lua_isboolean(L, -1)) {
args.stretch_opt = lua_toboolean(L, -1);
args.stretch_opt_set = true;
}
draw_sprite_args(args);
return 0;
}
/* rectangle(data [table]) */
/* data should contain the following fields: */
/*
* rect [table]
* color [table], optional, defaults to white
*/
static int b_rectangle(lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_getfield(L, 1, "rect");
if (!lua_istable(L, -1))
luaL_error(L, "bad field 'rect' in 'data' (table expected, got %s)", luaL_typename(L, -1));
Rect rect = table_to_rect(L, -1, "data.rect");
Color color = { 255, 255, 255, 255 };
lua_getfield(L, 1, "color");
if (lua_istable(L, -1))
color = table_to_color(L, -1);
draw_rectangle(rect, color);
return 0;
}
/* text(data [table]) */
/* data should contain the following fields: */
/*
* string [string]
* position [table]
* height_px [number], optional, defaults to 22
* color [table], optional, defaults to black
* font [string]
*/
static int b_text(lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_getfield(L, 1, "string");
const char *string = lua_tostring(L, -1);
if (string == NULL)
luaL_error(L, "bad field 'string' in 'data' (string expected, got %s)", luaL_typename(L, -1));
lua_getfield(L, 1, "position");
if (!lua_istable(L, -1))
luaL_error(L, "bad field 'position' in 'data' (table expected, got %s)", luaL_typename(L, -1));
lua_getfield(L, -1, "x");
float x = (float)lua_tonumber(L, -1);
lua_getfield(L, -2, "y");
float y = (float)lua_tonumber(L, -1);
lua_getfield(L, 1, "height_px");
int is_num;
int height_px = (int)lua_tointegerx(L, -1, &is_num);
if (!is_num)
height_px = 22;
lua_getfield(L, 1, "color");
Color color = { 0, 0, 0, 255 };
if (lua_istable(L, -1))
color = table_to_color(L, -1);
lua_getfield(L, 1, "font");
const char *font = lua_tostring(L, -1);
if (font == NULL)
luaL_error(L, "bad field 'font' in 'data' (string expected, got %s)", luaL_typename(L, -1));
draw_text(string, (Vec2) { x, y }, height_px, color, font);
return 0;
}
void game_tick(void) { void game_tick(void) {
if (ctx.initialization_needed) { if (ctx.initialization_needed) {
if (!ctx.udata) if (!ctx.udata)
@ -246,14 +55,13 @@ void game_tick(void) {
} }
state->L = luaL_newstate(); state->L = luaL_newstate();
/* fakey version of luaL_openlibs() that excludes file i/o */ /* fakey version of luaL_openlibs() that excludes file i/o and os stuff */
{ {
static const luaL_Reg loaded_libs[] = { static const luaL_Reg loaded_libs[] = {
{ LUA_GNAME, luaopen_base }, { LUA_GNAME, luaopen_base },
{ LUA_LOADLIBNAME, luaopen_package }, { LUA_LOADLIBNAME, luaopen_package },
{ LUA_COLIBNAME, luaopen_coroutine }, { LUA_COLIBNAME, luaopen_coroutine },
{ LUA_TABLIBNAME, luaopen_table }, { LUA_TABLIBNAME, luaopen_table },
{ LUA_OSLIBNAME, luaopen_os },
{ LUA_STRLIBNAME, luaopen_string }, { LUA_STRLIBNAME, luaopen_string },
{ LUA_MATHLIBNAME, luaopen_math }, { LUA_MATHLIBNAME, luaopen_math },
{ LUA_UTF8LIBNAME, luaopen_utf8 }, { LUA_UTF8LIBNAME, luaopen_utf8 },
@ -280,9 +88,11 @@ void game_tick(void) {
lua_pop(state->L, 2); lua_pop(state->L, 2);
/* binding */ /* binding */
lua_register(state->L, "sprite", b_sprite); // lua_register(state->L, "sprite", b_sprite);
lua_register(state->L, "rectangle", b_rectangle); // lua_register(state->L, "rectangle", b_rectangle);
lua_register(state->L, "text", b_text); // lua_register(state->L, "text", b_text);
bindgen_load_twn(state->L);
/* now finally get to running the code */ /* now finally get to running the code */
unsigned char *game_buf = NULL; unsigned char *game_buf = NULL;

406
share/twn_api.json Normal file
View File

@ -0,0 +1,406 @@
{
"name": "twn",
"procedures": {
"input_action": {
"module": "input",
"symbol": "bind_action",
"header": "twn_input.h",
"params": [
{ "name": "name", "type": "char *" },
{ "name": "control", "type": "Control" }
]
},
"input_action_pressed": {
"module": "input",
"symbol": "action_pressed",
"header": "twn_input.h",
"params": [
{ "name": "name", "type": "char *" }
],
"return": "bool"
},
"input_action_just_pressed": {
"module": "input",
"symbol": "action_just_pressed",
"header": "twn_input.h",
"params": [
{ "name": "name", "type": "char *" }
],
"return": "bool"
},
"input_action_just_released": {
"module": "input",
"symbol": "action_just_released",
"header": "twn_input.h",
"params": [
{ "name": "name", "type": "char *" }
],
"return": "bool"
},
"input_action_position": {
"module": "input",
"symbol": "get_action_position",
"header": "twn_input.h",
"params": [
{ "name": "name", "type": "char *" }
],
"return": "Vec2"
},
"input_mouse_captured": {
"module": "input",
"symbol": "set_mouse_captured",
"header": "twn_input.h",
"params": [
{ "name": "enabled", "type": "bool" }
]
},
"draw_sprite": {
"module": "draw",
"symbol": "sprite",
"header": "twn_draw.h",
"params": [
{ "name": "path", "type": "char *" },
{ "name": "rect", "type": "Rect" },
{ "name": "texture_region", "type": "Rect *", "default": {} },
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } },
{ "name": "rotation", "type": "float", "default": 0.0 },
{ "name": "flip_x", "type": "bool", "default": false },
{ "name": "flip_y", "type": "bool", "default": false },
{ "name": "stretch", "type": "bool", "default": true }
]
},
"draw_rectangle": {
"module": "draw",
"symbol": "rectangle",
"header": "twn_draw.h",
"params": [
{ "name": "rect", "type": "Rect" },
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } }
]
},
"draw_circle": {
"module": "draw",
"symbol": "circle",
"header": "twn_draw.h",
"params": [
{ "name": "position", "type": "Vec2" },
{ "name": "radius", "type": "float" },
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } }
]
},
"draw_text": {
"module": "draw",
"symbol": "text",
"header": "twn_draw.h",
"params": [
{ "name": "string", "type": "char *" },
{ "name": "position", "type": "Vec2" },
{ "name": "height", "type": "float", "default": 22 },
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } },
{ "name": "font", "type": "char *", "default": {} }
]
},
"draw_text_width": {
"module": "draw",
"symbol": "text_width",
"header": "twn_draw.h",
"params": [
{ "name": "string", "type": "char *" },
{ "name": "height", "type": "float", "default": 22 },
{ "name": "font", "type": "char *", "default": {} }
],
"return": "float"
}
},
"types": {
"Vec2": {
"fields": [
{ "name": "x", "type": "float" },
{ "name": "y", "type": "float" }
]
},
"Vec3": {
"fields": [
{ "name": "x", "type": "float" },
{ "name": "y", "type": "float" },
{ "name": "z", "type": "float" }
]
},
"Vec4": {
"fields": [
{ "name": "x", "type": "float" },
{ "name": "y", "type": "float" },
{ "name": "z", "type": "float" },
{ "name": "w", "type": "float" }
]
},
"Color": {
"fields": [
{ "name": "r", "type": "uint8_t" },
{ "name": "g", "type": "uint8_t" },
{ "name": "b", "type": "uint8_t" },
{ "name": "a", "type": "uint8_t" }
]
},
"Rect": {
"fields": [
{ "name": "x", "type": "float" },
{ "name": "y", "type": "float" },
{ "name": "w", "type": "float" },
{ "name": "h", "type": "float" }
]
},
"Control": {
"enums": {
"A": 4,
"B": 5,
"C": 6,
"D": 7,
"E": 8,
"F": 9,
"G": 10,
"H": 11,
"I": 12,
"J": 13,
"K": 14,
"L": 15,
"M": 16,
"N": 17,
"O": 18,
"P": 19,
"Q": 20,
"R": 21,
"S": 22,
"T": 23,
"U": 24,
"V": 25,
"W": 26,
"X": 27,
"Y": 28,
"Z": 29,
"1": 30,
"2": 31,
"3": 32,
"4": 33,
"5": 34,
"6": 35,
"7": 36,
"8": 37,
"9": 38,
"0": 39,
"RETURN": 40,
"ESCAPE": 41,
"BACKSPACE": 42,
"TAB": 43,
"SPACE": 44,
"MINUS": 45,
"EQUALS": 46,
"LEFTBRACKET": 47,
"RIGHTBRACKET": 48,
"BACKSLASH": 49,
"NONUSHASH": 50,
"SEMICOLON": 51,
"APOSTROPHE": 52,
"GRAVE": 53,
"COMMA": 54,
"PERIOD": 55,
"SLASH": 56,
"CAPSLOCK": 57,
"F1": 58,
"F2": 59,
"F3": 60,
"F4": 61,
"F5": 62,
"F6": 63,
"F7": 64,
"F8": 65,
"F9": 66,
"F10": 67,
"F11": 68,
"F12": 69,
"PRINTSCREEN": 70,
"SCROLLLOCK": 71,
"PAUSE": 72,
"INSERT": 73,
"HOME": 74,
"PAGEUP": 75,
"DELETE": 76,
"END": 77,
"PAGEDOWN": 78,
"RIGHT": 79,
"LEFT": 80,
"DOWN": 81,
"UP": 82,
"NUMLOCKCLEAR": 83,
"KP_DIVIDE": 84,
"KP_MULTIPLY": 85,
"KP_MINUS": 86,
"KP_PLUS": 87,
"KP_ENTER": 88,
"KP_1": 89,
"KP_2": 90,
"KP_3": 91,
"KP_4": 92,
"KP_5": 93,
"KP_6": 94,
"KP_7": 95,
"KP_8": 96,
"KP_9": 97,
"KP_0": 98,
"KP_PERIOD": 99,
"NONUSBACKSLASH": 100,
"APPLICATION": 101,
"POWER": 102,
"KP_EQUALS": 103,
"F13": 104,
"F14": 105,
"F15": 106,
"F16": 107,
"F17": 108,
"F18": 109,
"F19": 110,
"F20": 111,
"F21": 112,
"F22": 113,
"F23": 114,
"F24": 115,
"EXECUTE": 116,
"HELP": 117,
"MENU": 118,
"SELECT": 119,
"STOP": 120,
"AGAIN": 121,
"UNDO": 122,
"CUT": 123,
"COPY": 124,
"PASTE": 125,
"FIND": 126,
"MUTE": 127,
"VOLUMEUP": 128,
"VOLUMEDOWN": 129,
"KP_COMMA": 133,
"KP_EQUALSAS400": 134,
"INTERNATIONAL1": 135,
"INTERNATIONAL2": 136,
"INTERNATIONAL3": 137,
"INTERNATIONAL4": 138,
"INTERNATIONAL5": 139,
"INTERNATIONAL6": 140,
"INTERNATIONAL7": 141,
"INTERNATIONAL8": 142,
"INTERNATIONAL9": 143,
"LANG1": 144,
"LANG2": 145,
"LANG3": 146,
"LANG4": 147,
"LANG5": 148,
"LANG6": 149,
"LANG7": 150,
"LANG8": 151,
"LANG9": 152,
"ALTERASE": 153,
"SYSREQ": 154,
"CANCEL": 155,
"CLEAR": 156,
"PRIOR": 157,
"RETURN2": 158,
"SEPARATOR": 159,
"OUT": 160,
"OPER": 161,
"CLEARAGAIN": 162,
"CRSEL": 163,
"EXSEL": 164,
"KP_00": 176,
"KP_000": 177,
"THOUSANDSSEPARATOR": 178,
"DECIMALSEPARATOR": 179,
"CURRENCYUNIT": 180,
"CURRENCYSUBUNIT": 181,
"KP_LEFTPAREN": 182,
"KP_RIGHTPAREN": 183,
"KP_LEFTBRACE": 184,
"KP_RIGHTBRACE": 185,
"KP_TAB": 186,
"KP_BACKSPACE": 187,
"KP_A": 188,
"KP_B": 189,
"KP_C": 190,
"KP_D": 191,
"KP_E": 192,
"KP_F": 193,
"KP_XOR": 194,
"KP_POWER": 195,
"KP_PERCENT": 196,
"KP_LESS": 197,
"KP_GREATER": 198,
"KP_AMPERSAND": 199,
"KP_DBLAMPERSAND": 200,
"KP_VERTICALBAR": 201,
"KP_DBLVERTICALBAR": 202,
"KP_COLON": 203,
"KP_HASH": 204,
"KP_SPACE": 205,
"KP_AT": 206,
"KP_EXCLAM": 207,
"KP_MEMSTORE": 208,
"KP_MEMRECALL": 209,
"KP_MEMCLEAR": 210,
"KP_MEMADD": 211,
"KP_MEMSUBTRACT": 212,
"KP_MEMMULTIPLY": 213,
"KP_MEMDIVIDE": 214,
"KP_PLUSMINUS": 215,
"KP_CLEAR": 216,
"KP_CLEARENTRY": 217,
"KP_BINARY": 218,
"KP_OCTAL": 219,
"KP_DECIMAL": 220,
"KP_HEXADECIMAL": 221,
"LCTRL": 224,
"LSHIFT": 225,
"LALT": 226,
"LGUI": 227,
"RCTRL": 228,
"RSHIFT": 229,
"RALT": 230,
"RGUI": 231,
"MODE": 257,
"KBDILLUMTOGGLE": 278,
"KBDILLUMDOWN": 279,
"KBDILLUMUP": 280,
"EJECT": 281,
"SLEEP": 282,
"APP1": 283,
"APP2": 284,
"AUDIOREWIND": 285,
"AUDIOFASTFORWARD": 286,
"SOFTLEFT": 287,
"SOFTRIGHT": 288,
"CALL": 289,
"ENDCALL": 290,
"LEFT_MOUSE": 513,
"RIGHT_MOUSE": 515,
"MIDDLE_MOUSE": 514,
"X1": 516,
"X2": 517
}
}
}
}