Useful Hints¶
4.1 Getting started
4.2 Debugging
4.3 Best Practices¶
4.1 Getting started¶
If you plan to develop a skin with Skindesigner, for sure you have a rough idea how the skin should look like. First you should decide if your skin should only be usable for a fixed screen size like 1920 * 1080 or if it should scale dynamically to all screen resolutions. In the first case, you can work with hard coded positioning and sizing, in case of a scalable skin you have to work as much as possible with relative values of the available screen size.
In both cases i recommend to develop a "mockup" of your planned skin, each digital with a drawing tool, or just "old school" with a pencil and a piece of paper. Especially for scalable skins it is reasonable to define all "major" dimensions of the different views in percent of the screen. With that it is much more comfortable to define the positions and sizes of the different views and areas, in most cases you can just use convenient percentage definitions like x="10%", width="90%"...
As soon as your basic layout is fixed, you can start implementing your skin. First just copy the folder "skinskeleton" available in the source code folder of the Skindesigner plugin into your "skin" folder Skindesigner uses and rename this copied skinskeleton folder to the name you've choosen for your skin. The skinskeleton folder contains skeletons of all necessary XML files inside the correct folder structure. Also the folder structure for the different types of images is already prepared. On my (Gen2VDR) system this is done by:
cd /etc/vdr/plugins/skindesigner/skins/ cp -r /usr/local/src/VDR/PLUGINS/src/skindesigner/skinskeleton/ . mv skinskeleton myfirstskin
After a restart of VDR "myfirstskin" should be selectable in the OSD and you can select your new skin.
When you open now a VDR menu...for sure nothing happens. The screen keeps empty. Now you have to implement the different XML files step by step...and the screen also is filled step by step ;)
Skindesigner provides the SVDRP command "RELD". This command is very helpful during skin developmeng with Skindesigner. Issuing "svdrpsend plug skindesigner RELD" triggers Skindesigner to reload the complete currently used skin with all templates and cached images. Consider that the OSD has to be closed when performing this command.
Always check your log files while loading or reloading a skin. In the logs different status messages are shown during the plugin loads and parses the templates of a skin. In case of errors, these errors are also shown. If the XML syntax is not correct or the XML documents are not correctly validated against the DTD, Skindesigner tells you exactly why. In such a case LCARS as default skin is used as long as the errors are not corrected and the skin is reloaded.
So for me the easiest way to develop a skin is to use two ssh sessions, one with a "tail -f /var/log/messages | grep skindesigner" to check the log messages, and another one to reload the skin via svdrp and to do other necessary stuff. Additionally the XML Files can be edited in every text editor remotely if the XML files can be accessed via network, for instance via a samba share from a windows client.
4.2 Debugging¶
If you encounter problems during development, there are two possibilities for debugging:
First you can always instruct view elements to print the currently available tokens into the debug log. Just change your view element tag as follows:
<statusinfo debug="true">
....
</statusinfo>
This works in every view element tag and also in the listelement, currentelement and tab tag. With activated debugging all available tokens with their content are displayed in the debug log. For the example above the output looks as follows:
Oct 5 10:08:06 maschine vdr: [18953] skindesigner: ------------------------------ Tokens for StatusInfo: Oct 5 10:08:06 maschine vdr: [18953] skindesigner: string var "trackdesc" = "ohne Audiodeskription" Oct 5 10:08:06 maschine vdr: [18953] skindesigner: string var "tracklang" = "deu" Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "audiochannel" = 0 Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "hasVT" = 1 Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "isDolby" = 1 Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "isEncrypted" = 0 Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "isRadio" = 0 Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "isRecording" = 0 Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "isStereo" = 1 Oct 5 10:08:06 maschine vdr: [18953] skindesigner: int var "numaudiotracks" = 3
Secondly it is possible to debug particular functions by assigning the "debug" attribute to "true". If a function is not acting like expected, just do the following:
...
<drawimage debug="true" name="enc" condition="{isEncrypted}" imagetype="icon" path="ico_crypt_on" x="{areawidth} - {width(enc)}" valign="center" width="{areaheight}*0.8*1.76" height="{areaheight}*0.8"/>
...
Here the debug output of the function above (whith this function the encrypted icon is displayed in displaychannel):
Oct 5 09:52:09 maschine vdr: [18953] skindesigner: Debugging Function DrawImage, Container: x = 0, y = 0, Size: 1459x64 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: --- Native Parameters: Oct 5 09:52:09 maschine vdr: [18953] skindesigner: Condition {isEncrypted}, Type: single param, cond is false Oct 5 09:52:09 maschine vdr: [18953] skindesigner: cond token isEncrypted, type: 3, compareValue 0, negated: 0 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: --- Native Parameters: Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "Condition" = "{isEncrypted}" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "Name" = "enc" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "X" = "{areawidth} - {width(enc)}" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "Width" = "{areaheight}*0.8*1.76" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "Height" = "{areaheight}*0.8" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "Image Type" = "icon" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "Image Path" = "ico_crypt_on" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: "Vertical Align" = "center" Oct 5 09:52:09 maschine vdr: [18953] skindesigner: --- Integer Parameters: Oct 5 09:52:09 maschine vdr: [18953] skindesigner: X = 1369 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: Y = 6 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: Width = 90 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: Height = 51 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: Image Type = 4 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: Vertical Align = 1 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: --- Dynamic Integer Parameters: Oct 5 09:52:09 maschine vdr: [18953] skindesigner: X = 1459-90 Oct 5 09:52:09 maschine vdr: [18953] skindesigner: --- Image Path: "ico_crypt_on"
At the beginning of the output the position and size of the surrounding container is displayed. Furthermore you can check if a condition is correctly set and if this condition is valid or not. You can also check if the numeric attributes are calculated correctly. The "native parameters" are the attributes as provided in the XML files. The "Integer Parameters" are these attributes after all expressions are evaluated. For instance the "y" attribute in the example function is derived from valign="center". The "dynamic integer parameters" are used internally for buffering of values if a value is calculated stepwise. In the example above {width(enc)} which is necessary to calculate x is available first at runtime, so the calculation of x is done in two steps. If in "Dynamic Integer Parameters" a value appears which is not completely parsed so that still a token exists, this numeric parameter cannot be calculated completely and is invalid.
The debug attribute is also available analog in an area tag. If debugging is activated for a complete area, debugging information for the area itself and for all it's functions are printed in the debug log, so may be the output is a little bit confusing...
Hopefully you never need to debug ... but if needed, these tools could be helpful ;)
4.3 Best Practices¶
Here last but not least some best practices and tips from my experiences:
- "paint before coding"...get clear about your desired design and think first about general layout of the menus, the dimensions and especially the sizes of the different containers inside the views.
- the "displaychannel" view is a good view to start. With that view you can learn best how to use Skindesigner.
- just play around a little bit with the different functions in the beginning to get a feeling how they work and if they work as you expect.
- Especially the VDR menu itself should be planned carefully. Think about what space is needed in each subview and design your "container layout" accordingly (position and size of <displaymenu> and all subviews like <menumain>, <menuschedules>, ...). Think additionally about in which subviews it is reasonable to implement the common view elements <header>, <datetime>, ... and for which subviews you want to use the default implementation in <displaymenu>.
- Use only as many areas as needed! Especially in menu items you should not use too many areas, because the number is multiplied by the number of menu items displayed.
- But: Texts and Images should never be placed on "background areas" which are filled with a color or a background image.
- Use a delay of in minimum 100ms for displaying the "currentelement" in the list views. Otherwise fast scrolling through the lists could get lagged.
- to be continued...