+
+
+.. _locking:
+
+Locking alternatives
+--------------------
+
+In some cases, Nageru may be building in alternatives to a scene that you
+don't really need, resulting in combinatorial explosion. (If the number of
+instances is getting high, you will get a warning when finalizing the scene.)
+For instance, in some cases, you know that a given transition scene will never
+be used for previews, just live. In this case, you can replace the call to
+scene:finalize() with::
+
+ scene:finalize(false)
+
+In this case, you guarantee that the scene will never be returned when
+get_scene() is called with the number 0. (Similarly, you can use true
+to *only* use it for the live channel.)
+
+Similarly, inputs can hold four different input types, but in some scenes,
+you may always use them with a specific one, e.g. an image “bg_img”. In this case,
+you may add the input with a specific type right away::
+
+ scene:add_input(bg_img)
+
+Similarly, for a live input, you can do::
+
+ scene:add_input(0)
+
+You can still use scene:display() to change the input, but it needs to be of
+the same *type* as the one you gave to add_input().
+
+Finally, you can specify that some effects only make sense together, reducing
+the number of possibilities further. For instance, you may have an optional
+crop effect followed by a resample, where the resample is only enabled if the
+crop is. If so, you can do this::
+
+ resample_effect:always_disable_if_disabled(crop_effect)
+
+Also, you can disable an optional effect if a given other
+effect is *enabled*::
+
+ overlay1_effect:promise_to_disable_if_enabled(overlay2_effect)
+ overlay2_effect:promise_to_disable_if_enabled(overlay1_effect)
+
+Note that the latter is a promise from the user, not automatic disabling; since
+it is mostly useful for mutual exclusions, Nageru wouldn't know which of the
+two to disable. (If you violate the promise, you will get an error message at
+runtime.) It can still be useful for reducing the number of alternatives, though.
+
+For more advanced exclusions, you may choose to split up the scenes into several
+distinct ones that you manage yourself. At some point, however, you may choose
+to simply accept the added startup time and a bit of extra RAM cost; ease of
+use and flexibility often trumps such concerns.
+
+
+.. _menus:
+
+Theme menus
+-----------
+
+Complicated themes, especially those dealing with :doc:`HTML inputs <html>`,
+may have needs for user control that go beyond those of transition buttons.
+(An obvious example may be “reload the HTML file”.) For this reason,
+themes can also set simple *theme menus*, which are always visible
+no matter what inputs are chosen.
+
+If a theme chooses to set a theme menu, it will be available on the
+main menu bar under “Theme”; if not, it will be hidden. You can set
+the menu at startup or at any other point, using a simple series of
+labels and function references::
+
+ function modify_aspect()
+ -- Your code goes here.
+ end
+
+ function reload_html()
+ html_input:reload()
+ end
+
+ ThemeMenu.set(
+ { "Change &aspect", modify_aspect },
+ { "&Reload overlay", reload_html }
+ )
+
+When the user chooses a menu entry, the given Lua function will
+automatically be called. There are no arguments nor return values.
+
+Menus can contain submenus, by giving an array instead of a function::
+
+ ThemeMenu.set(
+ { "Overlay", {
+ { "Version A", select_overlay_a },
+ { "Version B", select_overlay_b }
+ },
+ { "&Reload overlay", reload_html }
+ )
+
+They can also be checkable, or have checkboxes, by adding a third
+array element containing flags for that::
+
+ ThemeMenu.set(
+ { "Enable overlay", enable_overlay, Nageru.CHECKED }, -- Currently checked.
+ { "Enable crashing", make_unstable, Nageru.CHECKABLE } -- Can be checked, but isn't currently.
+ )
+
+When such an option is selected, you probably want to rebuild the menu to
+reflect the new state.
+
+There currently is no support for input boxes, sliders,
+or the likes. However, do note that since the theme is written in unrestricted
+Lua, so you can use e.g. `lua-http <https://github.com/daurnimator/lua-http>`_
+to listen for external connections and accept more complicated inputs
+from those.
+
+
+.. _signal-info:
+
+Signal information queries
+--------------------------
+
+As previously mentioned, get_scene() takes in a “signals” parameter
+that you can query for information about each signal (numbered from 0;
+live and preview are channels, not signals), like its current resolution
+or frame rate:
+
+ * get_frame_width(signal), get_frame_height(signal): Width and height of the last frame.
+ * get_width(signal), get_height(signal): Width and height of the last *field*
+ (the field height is half of the frame height for an interlaced signal).
+ * get_interlaced(signal): Whether the last frame was interlaced.
+ * get_has_signal(signal): Whether there is a valid input signal.
+ * get_is_connected(signal): Whether there is even a card connected
+ to this signal (USB cards can be swapped in or out); if not,
+ you will get a stream of single-colored frames.
+ * get_frame_rate_nom(signal), get_frame_rate_den(signal): The frame rate
+ of the last frame, as a rational (e.g. 60/1, or 60000/1001 for 59.94).
+ * get_last_subtitle(signal): See :ref:`subtitle-ingest`.
+ * get_human_readable_resolution(signal): The resolution and frame rate in
+ human-readable form (e.g. “1080i59.94”), suitable for e.g. stream titles.
+ Note that Nageru does not follow the EBU recommendation of using
+ frame rate even for interlaced signals (e.g. “1080i25” instead of “1080i50”),
+ since it is little-used and confusing to most users.
+
+You can use this either for display purposes, or for choosing the right
+effect alternatives. In particular, you may want to disable scaling if
+the frame is already of the correct resolution.
+
+
+Audio control
+-------------
+
+Before you attempt to control audio from the theme, be sure to have read
+the documentation about :doc:`audio`.
+
+The theme has a certain amount of control over the audio
+mix, assuming that you are in multichannel mode. This is useful in particular
+to be able to set defaults, if e.g. one channel should always be muted at
+startup, or to switch in/out certain channels depending on whether they are
+visible or not.
+
+In particular, these operations are available::
+
+ # Returns number of buses in the mapping.
+ local num_buses = Nageru.get_num_audio_buses()
+
+ # Gets the name from the mapping. All indexes start at zero,
+ # so valid indexes are 0..(num_buses-1), inclusive.
+ local name = Nageru.get_audio_bus_name(N)
+
+ # 0.0 is zero amplification, as in the UI. Valid range is
+ # -inf to +6.0, inclusive.
+ local level = Nageru.get_audio_bus_fader_level_db(N)
+ set_audio_bus_fader_level_db(N, level)
+
+ # Similar as the above, but valid range is -15.0..+15.0 (dB).
+ # Valid bands are Nageru.EQ_BAND_{BASS, MID, TREBLE}.
+ local eq_level = Nageru.get_audio_bus_eq_level_db(N, Nageru.EQ_BAND_BASS)
+ Nageru.set_audio_bus_eq_level_db(N, Nageru.EQ_BAND_BASS, level)
+
+ # A boolean. Does not affect the bus levels set/returned above.
+ local muted = Nageru_get_audio_bus_mute(N)
+ Nageru_set_audio_bus_mute(N, false)
+
+Note that any audio operation is inherently unsynchronized with the UI,
+so if the user reduces the number of audio buses while
+the theme tries to access one that is going away, you may get unpredictable
+behavior, up to and including crashes. Thus, you will need to be careful
+with such operations.
+
+Also, you cannot do anything with audio before the first *get_scene()* call,
+since the audio mixer is initialized only after the theme has been loaded and
+initialized. Thus, for things that should be done only the first frame, the
+recommended method is to put code into get_scene() and have a guard variable
+that makes sure it is only run
+once, ever.
+
+
+Overriding the status line
+--------------------------
+
+Some users may wish to override the status line, e.g. with recording time.
+If so, it is possible to declare a function **format_status_line**::
+
+ function format_status_line(disk_space_text, file_length_seconds)
+ if file_length_seconds > 86400.0 then
+ return "Time to make a new segment"
+ else
+ return "Disk space left: " .. disk_space_text
+ end
+ end
+
+As demonstrated, it is given the disk space text (that would normally
+be there), and the length of the current recording file in seconds.
+HTML is supported.