X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=theme.cpp;h=3f52c838fe53ecfcac11238cb0ffeff8a3cc44b6;hb=ad9585b7a6671903cadcf6c74491646736db8e1e;hp=3f387f81e3de2706b9d0c3c4c7ce3cddd00ff81b;hpb=fb5360d92864760d70d8a69c8dd4a9e738bcc0f0;p=nageru diff --git a/theme.cpp b/theme.cpp index 3f387f8..3f52c83 100644 --- a/theme.cpp +++ b/theme.cpp @@ -1,13 +1,15 @@ #include "theme.h" #include +#include +#include #include -#include -#include +#include #include #include #include #include +#include #include #include #include @@ -19,13 +21,19 @@ #include #include #include +#include #include #include -#include #include "defs.h" +#include "deinterlace_effect.h" +#include "flags.h" #include "image_input.h" -#include "mixer.h" +#include "input.h" +#include "input_state.h" +#include "pbo_frame_allocator.h" + +class Mixer; namespace movit { class ResourcePool; @@ -44,24 +52,28 @@ namespace { struct InputStateInfo { InputStateInfo(const InputState& input_state); - unsigned last_width[MAX_CARDS], last_height[MAX_CARDS]; - bool last_interlaced[MAX_CARDS]; - unsigned last_frame_rate_nom[MAX_CARDS], last_frame_rate_den[MAX_CARDS]; + unsigned last_width[MAX_VIDEO_CARDS], last_height[MAX_VIDEO_CARDS]; + bool last_interlaced[MAX_VIDEO_CARDS], last_has_signal[MAX_VIDEO_CARDS], last_is_connected[MAX_VIDEO_CARDS]; + unsigned last_frame_rate_nom[MAX_VIDEO_CARDS], last_frame_rate_den[MAX_VIDEO_CARDS]; }; InputStateInfo::InputStateInfo(const InputState &input_state) { - for (unsigned signal_num = 0; signal_num < MAX_CARDS; ++signal_num) { + for (unsigned signal_num = 0; signal_num < MAX_VIDEO_CARDS; ++signal_num) { BufferedFrame frame = input_state.buffered_frames[signal_num][0]; if (frame.frame == nullptr) { last_width[signal_num] = last_height[signal_num] = 0; last_interlaced[signal_num] = false; + last_has_signal[signal_num] = false; + last_is_connected[signal_num] = false; continue; } const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata; last_width[signal_num] = userdata->last_width[frame.field_number]; last_height[signal_num] = userdata->last_height[frame.field_number]; last_interlaced[signal_num] = userdata->last_interlaced; + last_has_signal[signal_num] = userdata->last_has_signal; + last_is_connected[signal_num] = userdata->last_is_connected; last_frame_rate_nom[signal_num] = userdata->last_frame_rate_nom; last_frame_rate_den[signal_num] = userdata->last_frame_rate_den; } @@ -134,6 +146,7 @@ Effect *get_effect(lua_State *L, int idx) luaL_testudata(L, idx, "IntegralPaddingEffect") || luaL_testudata(L, idx, "OverlayEffect") || luaL_testudata(L, idx, "ResizeEffect") || + luaL_testudata(L, idx, "MultiplyEffect") || luaL_testudata(L, idx, "MixEffect") || luaL_testudata(L, idx, "ImageInput")) { return *(Effect **)lua_touserdata(L, idx); @@ -324,6 +337,12 @@ int ResizeEffect_new(lua_State* L) return wrap_lua_object_nonowned(L, "ResizeEffect"); } +int MultiplyEffect_new(lua_State* L) +{ + assert(lua_gettop(L) == 0); + return wrap_lua_object_nonowned(L, "MultiplyEffect"); +} + int MixEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); @@ -360,6 +379,26 @@ int InputStateInfo_get_interlaced(lua_State* L) return 1; } +int InputStateInfo_get_has_signal(lua_State* L) +{ + assert(lua_gettop(L) == 2); + InputStateInfo *input_state_info = get_input_state_info(L, 1); + Theme *theme = get_theme_updata(L); + int signal_num = theme->map_signal(luaL_checknumber(L, 2)); + lua_pushboolean(L, input_state_info->last_has_signal[signal_num]); + return 1; +} + +int InputStateInfo_get_is_connected(lua_State* L) +{ + assert(lua_gettop(L) == 2); + InputStateInfo *input_state_info = get_input_state_info(L, 1); + Theme *theme = get_theme_updata(L); + int signal_num = theme->map_signal(luaL_checknumber(L, 2)); + lua_pushboolean(L, input_state_info->last_is_connected[signal_num]); + return 1; +} + int InputStateInfo_get_frame_rate_nom(lua_State* L) { assert(lua_gettop(L) == 2); @@ -514,6 +553,15 @@ const luaL_Reg ResizeEffect_funcs[] = { { NULL, NULL } }; +const luaL_Reg MultiplyEffect_funcs[] = { + { "new", MultiplyEffect_new }, + { "set_float", Effect_set_float }, + { "set_int", Effect_set_int }, + { "set_vec3", Effect_set_vec3 }, + { "set_vec4", Effect_set_vec4 }, + { NULL, NULL } +}; + const luaL_Reg MixEffect_funcs[] = { { "new", MixEffect_new }, { "set_float", Effect_set_float }, @@ -527,6 +575,8 @@ const luaL_Reg InputStateInfo_funcs[] = { { "get_width", InputStateInfo_get_width }, { "get_height", InputStateInfo_get_height }, { "get_interlaced", InputStateInfo_get_interlaced }, + { "get_has_signal", InputStateInfo_get_has_signal }, + { "get_is_connected", InputStateInfo_get_is_connected }, { "get_frame_rate_nom", InputStateInfo_get_frame_rate_nom }, { "get_frame_rate_den", InputStateInfo_get_frame_rate_den }, { NULL, NULL } @@ -665,8 +715,8 @@ int call_num_channels(lua_State *L) } // namespace -Theme::Theme(const char *filename, ResourcePool *resource_pool, unsigned num_cards) - : resource_pool(resource_pool), num_cards(num_cards) +Theme::Theme(const string &filename, const vector &search_dirs, ResourcePool *resource_pool, unsigned num_cards) + : resource_pool(resource_pool), num_cards(num_cards), signal_to_card_mapping(global_flags.default_stream_mapping) { L = luaL_newstate(); luaL_openlibs(L); @@ -680,14 +730,40 @@ Theme::Theme(const char *filename, ResourcePool *resource_pool, unsigned num_car register_class("IntegralPaddingEffect", IntegralPaddingEffect_funcs); register_class("OverlayEffect", OverlayEffect_funcs); register_class("ResizeEffect", ResizeEffect_funcs); + register_class("MultiplyEffect", MultiplyEffect_funcs); register_class("MixEffect", MixEffect_funcs); register_class("InputStateInfo", InputStateInfo_funcs); - // Run script. + // Run script. Search through all directories until we find a file that will load + // (as in, does not return LUA_ERRFILE); then run it. We store load errors + // from all the attempts, and show them once we know we can't find any of them. lua_settop(L, 0); - if (luaL_dofile(L, filename)) { - fprintf(stderr, "error: %s\n", lua_tostring(L, -1)); + vector errors; + bool success = false; + for (size_t i = 0; i < search_dirs.size(); ++i) { + string path = search_dirs[i] + "/" + filename; + int err = luaL_loadfile(L, path.c_str()); + if (err == 0) { + // Success; actually call the code. + if (lua_pcall(L, 0, LUA_MULTRET, 0)) { + fprintf(stderr, "Error when running %s: %s\n", path.c_str(), lua_tostring(L, -1)); + exit(1); + } + success = true; + break; + } + errors.push_back(lua_tostring(L, -1)); lua_pop(L, 1); + if (err != LUA_ERRFILE) { + // The file actually loaded, but failed to parse somehow. Abort; don't try the next one. + break; + } + } + + if (!success) { + for (const string &error : errors) { + fprintf(stderr, "%s\n", error.c_str()); + } exit(1); } assert(lua_gettop(L) == 0); @@ -731,7 +807,12 @@ Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned he exit(1); } - chain.chain = (EffectChain *)luaL_checkudata(L, -2, "EffectChain"); + chain.chain = (EffectChain *)luaL_testudata(L, -2, "EffectChain"); + if (chain.chain == nullptr) { + fprintf(stderr, "get_chain() for chain number %d did not return an EffectChain\n", + num); + exit(1); + } if (!lua_isfunction(L, -1)) { fprintf(stderr, "Argument #-1 should be a function\n"); exit(1); @@ -775,13 +856,56 @@ string Theme::get_channel_name(unsigned channel) fprintf(stderr, "error running function `channel_name': %s\n", lua_tostring(L, -1)); exit(1); } + const char *ret = lua_tostring(L, -1); + if (ret == nullptr) { + fprintf(stderr, "function `channel_name' returned nil for channel %d\n", channel); + exit(1); + } + + string retstr = ret; + lua_pop(L, 1); + assert(lua_gettop(L) == 0); + return retstr; +} + +int Theme::get_channel_signal(unsigned channel) +{ + unique_lock lock(m); + lua_getglobal(L, "channel_signal"); + lua_pushnumber(L, channel); + if (lua_pcall(L, 1, 1, 0) != 0) { + fprintf(stderr, "error running function `channel_signal': %s\n", lua_tostring(L, -1)); + exit(1); + } - string ret = lua_tostring(L, -1); + int ret = luaL_checknumber(L, 1); lua_pop(L, 1); assert(lua_gettop(L) == 0); return ret; } +std::string Theme::get_channel_color(unsigned channel) +{ + unique_lock lock(m); + lua_getglobal(L, "channel_color"); + lua_pushnumber(L, channel); + if (lua_pcall(L, 1, 1, 0) != 0) { + fprintf(stderr, "error running function `channel_color': %s\n", lua_tostring(L, -1)); + exit(1); + } + + const char *ret = lua_tostring(L, -1); + if (ret == nullptr) { + fprintf(stderr, "function `channel_color' returned nil for channel %d\n", channel); + exit(1); + } + + string retstr = ret; + lua_pop(L, 1); + assert(lua_gettop(L) == 0); + return retstr; +} + bool Theme::get_supports_set_wb(unsigned channel) { unique_lock lock(m); @@ -837,14 +961,23 @@ vector Theme::get_transition_names(float t) int Theme::map_signal(int signal_num) { + unique_lock lock(map_m); + if (signal_to_card_mapping.count(signal_num)) { + return signal_to_card_mapping[signal_num]; + } if (signal_num >= int(num_cards)) { - if (signals_warned_about.insert(signal_num).second) { - fprintf(stderr, "WARNING: Theme asked for input %d, but we only have %u card(s).\n", signal_num, num_cards); - fprintf(stderr, "Mapping to card %d instead.\n", signal_num % num_cards); - } - signal_num %= num_cards; + fprintf(stderr, "WARNING: Theme asked for input %d, but we only have %u card(s).\n", signal_num, num_cards); + fprintf(stderr, "Mapping to card %d instead.\n", signal_num % num_cards); } - return signal_num; + signal_to_card_mapping[signal_num] = signal_num % num_cards; + return signal_num % num_cards; +} + +void Theme::set_signal_mapping(int signal_num, int card_num) +{ + unique_lock lock(map_m); + assert(card_num < int(num_cards)); + signal_to_card_mapping[signal_num] = card_num; } void Theme::transition_clicked(int transition_num, float t)