Commit f5b6d404 authored by David Milligan's avatar David Milligan
Browse files

lua scripting: implement 'reflection' for fields of api modules by using a...

lua scripting: implement 'reflection' for fields of api modules by using a custom __pairs metamethod and iterator (cherry picked commits from lua_touch branch)

this allows us to do e.g.:
for k,v in pairs(camera) do
  printf("%s = %s",k,tostring(v))
end

should make writing simple, automatic, generic API tests easier

--HG--
branch : lua_fix
parent 81618678d562
......@@ -115,7 +115,7 @@ char * copy_string(const char * str)
return copy;
}
/***
/***
Event Handlers.
Scripts can repsond to events by setting the functions in the 'event' table.
......@@ -123,7 +123,7 @@ char * copy_string(const char * str)
that specifies whether or not the backend should continue executing event handlers
for this particular event.
Event handlers will not run if there's already a script or another event handler
Event handlers will not run if there's already a script or another event handler
actively executing at the same time.
@author Magic Lantern Team
......@@ -131,10 +131,10 @@ char * copy_string(const char * str)
@license GPL
@module event
@usage
event.keypress = function(key)
print("You pressed a key: "..key)
return false
end
event.keypress = function(key)
print("You pressed a key: "..key)
return false
end
*/
......@@ -190,7 +190,7 @@ static unsigned int lua_do_cbr(unsigned int ctx, struct script_event_entry * eve
#define LUA_CBR_FUNC(name, arg, timeout)\
static struct script_event_entry * name##_cbr_scripts = NULL;\
static unsigned int lua_##name##_cbr(unsigned int ctx) {\
return lua_do_cbr(arg, name##_cbr_scripts, #name, timeout, CBR_RET_CONTINUE, CBR_RET_STOP);\
return lua_do_cbr(arg, name##_cbr_scripts, #name, timeout, CBR_RET_CONTINUE, CBR_RET_STOP);\
}\
LUA_CBR_FUNC(pre_shoot, ctx, 500)
......@@ -218,9 +218,9 @@ static unsigned int lua_keypress_cbr(unsigned int ctx)
#define SCRIPT_CBR_SET(event) \
if(!strcmp(key, #event))\
{\
lua_pushvalue(L, 3);\
set_event_script_entry(&event##_cbr_scripts, L, lua_isfunction(L, -1) ? luaL_ref(L, LUA_REGISTRYINDEX) : LUA_NOREF); \
return 0;\
lua_pushvalue(L, 3);\
set_event_script_entry(&event##_cbr_scripts, L, lua_isfunction(L, -1) ? luaL_ref(L, LUA_REGISTRYINDEX) : LUA_NOREF); \
return 0;\
}\
static void set_event_script_entry(struct script_event_entry ** root, lua_State * L, int function_ref)
......@@ -316,6 +316,19 @@ static int luaCB_event_newindex(lua_State * L)
return 0;
}
static const char * lua_event_fields[] =
{
"pre_shoot",
"post_shoot",
"shoot_task",
"seconds_clock",
"keypress",
"custom_picture_taking",
"intervalometer",
"config_save",
NULL
};
static const luaL_Reg eventlib[] =
{
{ NULL, NULL }
......@@ -372,6 +385,81 @@ static int luaCB_global_index(lua_State * L)
return 0;
}
int do_lua_next(lua_State * L)
{
int r = lua_next(L, 1);
if (!r)
{
lua_pushnil(L);
lua_pushnil(L);
}
return 2;
}
int luaCB_next(lua_State * L)
{
lua_getmetatable(L, 1);
lua_getfield(L, -1, "fields");
const char ** fields = (const char **)lua_touserdata(L, -1);
lua_pop(L, 2);
if(lua_isnil(L, 2))
{
if(fields && fields[0])
{
lua_pushstring(L, fields[0]);
}
else
{
lua_pushvalue(L, 2);
return do_lua_next(L);
}
}
else if(lua_type(L, 2) == LUA_TSTRING)
{
LUA_PARAM_STRING(key, 2);
int found = 0;
for(int i = 0; fields[i]; i++)
{
if(!strcmp(key, fields[i]))
{
if(fields && fields[i+1])
{
lua_pushstring(L, fields[i+1]);
found = true;
break;
}
else
{
lua_pushnil(L);
return do_lua_next(L);
}
}
}
if(!found)
{
lua_pushvalue(L, 2);
return do_lua_next(L);
}
}
else
{
lua_pushvalue(L, 2);
return do_lua_next(L);
}
lua_pushvalue(L, -1);
lua_gettable(L, 1);
return 2;
}
int luaCB_pairs(lua_State * L)
{
lua_pushcfunction(L, luaCB_next);
lua_pushvalue(L, 1);
lua_pushnil(L);
return 3;
}
static lua_State * load_lua_state()
{
lua_State* L = luaL_newstate();
......@@ -453,24 +541,24 @@ static unsigned int lua_deinit()
}
MODULE_INFO_START()
MODULE_INIT(lua_init)
MODULE_DEINIT(lua_deinit)
MODULE_INIT(lua_init)
MODULE_DEINIT(lua_deinit)
MODULE_INFO_END()
MODULE_CBRS_START()
MODULE_CBR(CBR_PRE_SHOOT, lua_pre_shoot_cbr, 0)
MODULE_CBR(CBR_POST_SHOOT, lua_post_shoot_cbr, 0)
MODULE_CBR(CBR_SHOOT_TASK, lua_shoot_task_cbr, 0)
MODULE_CBR(CBR_SECONDS_CLOCK, lua_seconds_clock_cbr, 0)
MODULE_CBR(CBR_KEYPRESS, lua_keypress_cbr, 0)
MODULE_CBR(CBR_CUSTOM_PICTURE_TAKING, lua_custom_picture_taking_cbr, 0)
MODULE_CBR(CBR_INTERVALOMETER, lua_intervalometer_cbr, 0)
MODULE_CBR(CBR_CONFIG_SAVE, lua_config_save_cbr, 0)
MODULE_CBR(CBR_PRE_SHOOT, lua_pre_shoot_cbr, 0)
MODULE_CBR(CBR_POST_SHOOT, lua_post_shoot_cbr, 0)
MODULE_CBR(CBR_SHOOT_TASK, lua_shoot_task_cbr, 0)
MODULE_CBR(CBR_SECONDS_CLOCK, lua_seconds_clock_cbr, 0)
MODULE_CBR(CBR_KEYPRESS, lua_keypress_cbr, 0)
MODULE_CBR(CBR_CUSTOM_PICTURE_TAKING, lua_custom_picture_taking_cbr, 0)
MODULE_CBR(CBR_INTERVALOMETER, lua_intervalometer_cbr, 0)
MODULE_CBR(CBR_CONFIG_SAVE, lua_config_save_cbr, 0)
#ifdef CONFIG_VSYNC_EVENTS
MODULE_CBR(CBR_VSYNC, lua_vsync_cbr, 0)
MODULE_CBR(CBR_DISPLAY_FILTER, lua_display_filter_cbr, 0)
MODULE_CBR(CBR_VSYNC_SETPARAM, lua_vsync_setparam_cbr, 0)
MODULE_CBR(CBR_VSYNC, lua_vsync_cbr, 0)
MODULE_CBR(CBR_DISPLAY_FILTER, lua_display_filter_cbr, 0)
MODULE_CBR(CBR_VSYNC_SETPARAM, lua_vsync_setparam_cbr, 0)
#endif
MODULE_CBRS_END()
......
......@@ -76,6 +76,16 @@ static int luaCB_battery_newindex(lua_State * L)
return 0;
}
static const char * lua_battery_fields[] =
{
"level",
"id",
"performance",
"time",
"drain_rate",
NULL
};
static const luaL_Reg batterylib[] =
{
{ NULL, NULL }
......
......@@ -42,6 +42,49 @@ static int luaCB_fec_index(lua_State * L);
static int luaCB_fec_newindex(lua_State * L);
static int luaCB_fec_tostring(lua_State * L);
static const char * lua_camera_shutter_fields[] =
{
"raw",
"apex",
"ms",
"value",
NULL
};
static const char * lua_camera_aperture_fields[] =
{
"raw",
"apex",
"value",
"min",
"max",
NULL
};
static const char * lua_camera_aperture_minmax_fields[] =
{
"raw",
"apex",
"value",
NULL
};
static const char * lua_camera_iso_fields[] =
{
"raw",
"apex",
"value",
NULL
};
static const char * lua_camera_ec_fields[] =
{
"raw",
"value",
NULL
};
static int luaCB_camera_index(lua_State * L)
{
LUA_PARAM_STRING_OPTIONAL(key, 2, "");
......@@ -49,6 +92,7 @@ static int luaCB_camera_index(lua_State * L)
// @tfield shutter shutter
if(!strcmp(key, "shutter"))
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, luaCB_shutter_tostring);
lua_setfield(L, -2, "__tostring");
......@@ -56,13 +100,17 @@ static int luaCB_camera_index(lua_State * L)
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_shutter_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_camera_shutter_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Gets an @{aperture} object that represents the lens' aperture
// @tfield aperture aperture
else if(!strcmp(key, "aperture"))
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, luaCB_aperture_tostring);
lua_setfield(L, -2, "__tostring");
......@@ -70,13 +118,17 @@ static int luaCB_camera_index(lua_State * L)
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_aperture_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_camera_aperture_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Gets an @{iso} object that represents camera's ISO
// @tfield iso iso
else if(!strcmp(key, "iso"))
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, luaCB_iso_tostring);
lua_setfield(L, -2, "__tostring");
......@@ -84,13 +136,17 @@ static int luaCB_camera_index(lua_State * L)
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_iso_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_camera_iso_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Gets an @{ec} object that represents exposure compensation
// @tfield ec ec
else if(!strcmp(key, "ec"))
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, luaCB_ec_tostring);
lua_setfield(L, -2, "__tostring");
......@@ -98,13 +154,17 @@ static int luaCB_camera_index(lua_State * L)
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_ec_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_camera_ec_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Gets an @{ec} object that represents flash exposure compensation
// @tfield ec flash_ec
else if(!strcmp(key, "flash_ec"))
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, luaCB_fec_tostring);
lua_setfield(L, -2, "__tostring");
......@@ -112,7 +172,10 @@ static int luaCB_camera_index(lua_State * L)
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_fec_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_camera_ec_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Get/Set kelvin white balance
......@@ -324,24 +387,32 @@ static int luaCB_aperture_index(lua_State * L)
// @tfield aperture min
else if(!strcmp(key,"min"))
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, luaCB_min_aperture_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, luaCB_min_aperture_index);
lua_setfield(L, -2, "__index");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_camera_aperture_minmax_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Get/Set maximum (closed) aperture value (aperture object)
// @tfield aperture max
else if(!strcmp(key,"max"))
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, luaCB_max_aperture_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, luaCB_max_aperture_index);
lua_setfield(L, -2, "__index");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_camera_aperture_minmax_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
else lua_rawget(L, 1);
......@@ -585,6 +656,26 @@ static int luaCB_fec_newindex(lua_State * L)
return 0;
}
static const char * lua_camera_fields[] =
{
"shutter",
"aperture",
"iso",
"ec",
"flash_ec",
"kelvin",
"mode",
"af_mode",
"metering_mode",
"drive_mode",
"model",
"model_short",
"firmware",
"temperature",
"state",
NULL
};
static const luaL_Reg cameralib[] =
{
{ "shoot", luaCB_camera_shoot },
......
......@@ -70,6 +70,10 @@ int luaopen_##name(lua_State * L) {\
lua_setfield(L, -2, "__index");\
lua_pushcfunction(L, luaCB_##name##_newindex);\
lua_setfield(L, -2, "__newindex");\
lua_pushcfunction(L, luaCB_pairs);\
lua_setfield(L, -2, "__pairs");\
lua_pushlightuserdata(L, lua_##name##_fields);\
lua_setfield(L, -2, "fields");\
lua_setmetatable(L, -2);\
return 1;\
}
......@@ -95,6 +99,9 @@ int docall(lua_State *L, int narg, int nres);
int lua_take_semaphore(lua_State * L, int timeout, struct semaphore ** assoc_semaphore);
int lua_give_semaphore(lua_State * L, struct semaphore ** assoc_semaphore);
int luaCB_next(lua_State * L);
int luaCB_pairs(lua_State * L);
int luaopen_globals(lua_State * L);
int luaopen_console(lua_State * L);
int luaopen_camera(lua_State * L);
......
......@@ -56,6 +56,12 @@ static int luaCB_console_write(lua_State * L)
static int luaCB_console_index(lua_State * L) { lua_rawget(L, 1); return 1; }
static int luaCB_console_newindex(lua_State * L) { lua_rawset(L, 1); return 0; }
static const char * lua_console_fields[] =
{
NULL
};
static const luaL_Reg consolelib[] =
{
{ "show", luaCB_console_show },
......
......@@ -388,6 +388,14 @@ static int luaCB_bitmap_newindex(lua_State * L)
return luaL_error(L, "'bitmap' type is readonly");
}
static const char * lua_display_fields[] =
{
"idle",
"height",
"width",
NULL
};
const luaL_Reg displaylib[] =
{
{"on", luaCB_display_on},
......
......@@ -57,6 +57,29 @@ static int luaCB_dryos_call(lua_State * L)
return 1;
}
const char * lua_dryos_directory_fields[] =
{
"path",
"exists",
"create",
"children",
"files",
"parent",
NULL
};
const char * lua_dryos_card_fields[] =
{
"_card_ptr",
"cluster_size",
"drive_letter",
"file_number",
"folder_number",
"free_space",
"type",
NULL
};
/***
Creates a @{directory} object that is used to get information about a directory
......@@ -94,11 +117,15 @@ static int luaCB_dryos_directory(lua_State * L)
lua_pushvalue(L, 1);
}
lua_setfield(L, -2, "path");
lua_newtable(L);
lua_pushcfunction(L, luaCB_directory_index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_directory_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_dryos_directory_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
return 1;
}
......@@ -154,11 +181,15 @@ static int luaCB_dryos_index(lua_State * L)
lua_setfield(L, -2, "_card_ptr");
lua_pushfstring(L, "%s:/", card->drive_letter);
lua_setfield(L, -2, "path");
lua_newtable(L);
lua_pushcfunction(L, luaCB_card_index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_card_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_dryos_card_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Get the shooting card
......@@ -172,18 +203,21 @@ static int luaCB_dryos_index(lua_State * L)
lua_setfield(L, -2, "_card_ptr");
lua_pushfstring(L, "%s:/", card->drive_letter);
lua_setfield(L, -2, "path");
lua_newtable(L);
lua_pushcfunction(L, luaCB_card_index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, luaCB_card_newindex);
lua_setfield(L, -2, "__newindex");
lua_pushvalue(L, -1);
lua_pushcfunction(L, luaCB_pairs);
lua_setfield(L, -2, "__pairs");
lua_pushlightuserdata(L, lua_dryos_card_fields);
lua_setfield(L, -2, "fields");
lua_setmetatable(L, -2);
}
/// Gets a table representing the current date/time
// @tfield date date
else if(!strcmp(key, "date"))
{
/// Represents a date/time
// @type date
struct tm tm;
......@@ -426,6 +460,19 @@ static int luaCB_card_newindex(lua_State * L)
return luaL_error(L, "'card' type is readonly");
}
static const char * lua_dryos_fields[] =
{
"clock",
"ms_clock",
"prefix",
"dcim_dir",
"config_dir",
"ml_card",
"shooting_card",
"date",
NULL
};
const luaL_Reg dryoslib[] =
{
{"call", luaCB_dryos_call},
......
......@@ -58,6 +58,14 @@ static int luaCB_interval_stop(lua_State * L)
return 0;
}
static const char * lua_interval_fields[] =
{
"time",
"count",
"running",
NULL
};
static const luaL_Reg intervallib[] =
{
{ "stop", luaCB_interval_stop },
......
......@@ -82,6 +82,12 @@ static int luaCB_key_newindex(lua_State * L)
return 0;
}
static const char * lua_key_fields[] =
{
"last",
NULL
};
const luaL_Reg keylib[] =
{
{"press", luaCB_key_press},
......
......@@ -74,6 +74,18 @@ static int luaCB_lens_focus(lua_State * L)
return 1;
}
static const char * lua_lens_fields[] =
{
"name",
"focal_length",
"focal_distance",
"hyperfocal",
"dof_near",
"dof_far",
"af",
NULL
};
static const luaL_Reg lenslib[] =
{
{ "focus", luaCB_lens_focus },
......
......@@ -318,6 +318,12 @@ static int luaCB_lvinfo_newindex(lua_State * L)
return 0;
}