]> git.sesse.net Git - nageru/blobdiff - nageru/theme.cpp
Add a missing hint.
[nageru] / nageru / theme.cpp
index c881dc6891be2b1a32e90fb1b3fb8ec3b043f515..b53c65bc3434f61aae29904c242254893a672d75 100644 (file)
@@ -700,6 +700,69 @@ int InputStateInfo_get_last_subtitle(lua_State* L)
        return 1;
 }
 
+namespace {
+
+// Helper function to write e.g. “60” or “59.94”.
+string format_frame_rate(int nom, int den)
+{
+       char buf[256];
+       if (nom % den == 0) {
+               snprintf(buf, sizeof(buf), "%d", nom / den);
+       } else {
+               snprintf(buf, sizeof(buf), "%.2f", double(nom) / den);
+       }
+       return buf;
+}
+
+// Helper function to write e.g. “720p60”.
+string get_human_readable_resolution(const InputStateInfo *input_state_info, int signal_num)
+{
+       char buf[256];
+       if (input_state_info->last_interlaced[signal_num]) {
+               snprintf(buf, sizeof(buf), "%di", input_state_info->last_height[signal_num] * 2);
+
+               // Show field rate instead of frame rate; really for cosmetics only
+               // (and actually contrary to EBU recommendations, although in line
+               // with typical user expectations).
+               return buf + format_frame_rate(input_state_info->last_frame_rate_nom[signal_num] * 2,
+                       input_state_info->last_frame_rate_den[signal_num]);
+       } else {
+               snprintf(buf, sizeof(buf), "%dp", input_state_info->last_height[signal_num]);
+               return buf + format_frame_rate(input_state_info->last_frame_rate_nom[signal_num],
+                       input_state_info->last_frame_rate_den[signal_num]);
+       }
+}
+
+} // namespace
+
+int InputStateInfo_get_human_readable_resolution(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));
+
+       string str;
+       if (!input_state_info->last_is_connected[signal_num]) {
+               str = "disconnected";
+       } else if (input_state_info->last_height[signal_num]) {
+               str = "no signal";
+       } else if (!input_state_info->last_has_signal[signal_num]) {
+               if (input_state_info->last_height[signal_num]) {
+                       // Special mode for the USB3 cards.
+                       str = "no signal";
+               } else {
+                       str = get_human_readable_resolution(input_state_info, signal_num) + ", no signal";
+               }
+       } else {
+               str = get_human_readable_resolution(input_state_info, signal_num);
+       }
+
+       lua_pushstring(L, str.c_str());
+       return 1;
+}
+
+
 int EffectBlueprint_set_int(lua_State *L)
 {
        assert(lua_gettop(L) == 3);
@@ -921,6 +984,7 @@ const luaL_Reg InputStateInfo_funcs[] = {
        { "get_frame_rate_nom", InputStateInfo_get_frame_rate_nom },
        { "get_frame_rate_den", InputStateInfo_get_frame_rate_den },
        { "get_last_subtitle", InputStateInfo_get_last_subtitle },
+       { "get_human_readable_resolution", InputStateInfo_get_human_readable_resolution },
        { NULL, NULL }
 };
 
@@ -1148,6 +1212,7 @@ int call_num_channels(lua_State *L)
 
        if (lua_pcall(L, 0, 1, 0) != 0) {
                fprintf(stderr, "error running function `num_channels': %s\n", lua_tostring(L, -1));
+               fprintf(stderr, "Try Nageru.set_num_channels(...) at the start of the script instead.\n");
                abort();
        }
 
@@ -1159,6 +1224,29 @@ int call_num_channels(lua_State *L)
 
 }  // namespace
 
+int Nageru_set_channel_name(lua_State *L)
+{
+       // NOTE: m is already locked.
+       Theme *theme = get_theme_updata(L);
+       unsigned channel = luaL_checknumber(L, 1);
+       const string text = checkstdstring(L, 2);
+       theme->channel_names[channel] = text;
+       lua_pop(L, 2);
+       return 0;
+}
+
+int Nageru_set_num_channels(lua_State *L)
+{
+       // NOTE: m is already locked.
+       Theme *theme = get_theme_updata(L);
+       if (theme->startup_finished) {
+               luaL_error(L, "set_num_channels() can only be called at startup.");
+       }
+       theme->num_channels = luaL_checknumber(L, 1);
+       lua_pop(L, 1);
+       return 0;
+}
+
 Theme::Theme(const string &filename, const vector<string> &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)
 {
@@ -1229,7 +1317,7 @@ Theme::Theme(const string &filename, const vector<string> &search_dirs, Resource
        }
 
        // Set up the API we provide.
-       register_constants();
+       register_globals();
        register_class("Scene", Scene_funcs);
        register_class("Block", Block_funcs);
        register_class("EffectBlueprint", EffectBlueprint_funcs);
@@ -1260,8 +1348,11 @@ Theme::Theme(const string &filename, const vector<string> &search_dirs, Resource
        }
        assert(lua_gettop(L) == 0);
 
-       // Ask it for the number of channels.
-       num_channels = call_num_channels(L);
+       if (num_channels == -1) {
+               // Ask it for the number of channels.
+               num_channels = call_num_channels(L);
+       }
+       startup_finished = true;
 }
 
 Theme::~Theme()
@@ -1270,7 +1361,7 @@ Theme::~Theme()
        lua_close(L);
 }
 
-void Theme::register_constants()
+void Theme::register_globals()
 {
        // Set Nageru.VIDEO_FORMAT_BGRA = bmusb::PixelFormat_8BitBGRA, etc.
        const vector<pair<string, int>> num_constants = {
@@ -1294,6 +1385,14 @@ void Theme::register_constants()
                lua_settable(L, 1);  // t[key] = value
        }
 
+       const luaL_Reg Nageru_funcs[] = {
+               { "set_channel_name", Nageru_set_channel_name },
+               { "set_num_channels", Nageru_set_num_channels },
+               { NULL, NULL }
+       };
+       lua_pushlightuserdata(L, this);
+       luaL_setfuncs(L, Nageru_funcs, 1);        // for (name,f in funcs) { mt[name] = f, with upvalue {theme} }
+
        lua_setglobal(L, "Nageru");  // Nageru = t
        assert(lua_gettop(L) == 0);
 }
@@ -1420,7 +1519,17 @@ Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned he
 string Theme::get_channel_name(unsigned channel)
 {
        lock_guard<mutex> lock(m);
+
        lua_getglobal(L, "channel_name");
+       if (lua_isnil(L, -1)) {
+               lua_pop(L, 1);
+               if (channel_names.count(channel)) {
+                       return channel_names[channel];
+               } else {
+                       return "(no title)";
+               }
+       }
+
        lua_pushnumber(L, channel);
        if (lua_pcall(L, 1, 1, 0) != 0) {
                fprintf(stderr, "error running function `channel_name': %s\n", lua_tostring(L, -1));
@@ -1429,6 +1538,7 @@ string Theme::get_channel_name(unsigned channel)
        const char *ret = lua_tostring(L, -1);
        if (ret == nullptr) {
                fprintf(stderr, "function `channel_name' returned nil for channel %d\n", channel);
+               fprintf(stderr, "Try Nageru.set_channel_name(channel, name) at the start of the script instead.\n");
                abort();
        }