Rockbox for the HiBy R3 Pro II/R1

Original author Melissa Autumn (https://codeberg.org/oopsallnaps/rockbox-hibyos) with contributions from Marc Aarts.

Adaptation to Rockbox standards by Marc Aarts

Change-Id: I09e5af7ba0a75c648e4b9fd424badc2d3665c943
This commit is contained in:
Marc Aarts 2025-12-22 08:11:25 +01:00 committed by Solomon Peachy
parent eee6c31b4a
commit 1183b1ab1b
118 changed files with 2858 additions and 107 deletions

View file

@ -78,6 +78,10 @@ RSBS: no
<main>
# touch override
wps.480x720x(16|24|32): cabbiev2.480x720x16.touch.wps
# override implicit .wps filename
wps.800x480x(16|24|32): cabbiev2.800x480x16.wps
wps.480x800x(16|24|32): cabbiev2.480x800x16.wps
@ -110,6 +114,8 @@ fms.160x128x2: cabbiev2-160x128x2.fms
fms.128x128x2: cabbiev2-128x128x2.fms
# Preferred font (including .fnt extension - leave blank for player):
Font.480x720x(16|24|32): 35-Adobe-Helvetica.fnt
Font.800x480x(16|24|32): 35-Adobe-Helvetica.fnt
Font.480x800x(16|24|32): 35-Adobe-Helvetica.fnt
Font.400x240x(16|24|32): 15-Adobe-Helvetica.fnt
@ -145,6 +151,8 @@ line selector text color: 000000
filetype colours: -
#backdrop - remember this is the source file name in your SVN folder, not dest name!
backdrop.480x720x(16|24|32): backdrops/cabbiev2.480x720x16.bmp
backdrop.800x480x(16|24|32): backdrops/cabbiev2.800x480x16.bmp
backdrop.480x800x(16|24|32): backdrops/cabbiev2.480x800x16.bmp
backdrop.400x240x(16|24|32): backdrops/cabbiev2.400x240x16.bmp
@ -173,6 +181,8 @@ selector type..+x2: bar (inverse)
selector type..+x(16|24|32): bar (gradient)
#icons
iconset.480x720x(16|24|32): icons/tango_icons.32x32.bmp
iconset.800x480x(16|24|32): icons/tango_icons.32x32.bmp
iconset.480x800x(16|24|32): icons/tango_icons.32x32.bmp
iconset.400x240x(16|24|32): icons/tango_icons.16x16.bmp
@ -194,6 +204,8 @@ iconset.96x96x(16|24|32): icons/tango_icons.8x8.bmp
iconset..+x2: icons/tango_small_mono.bmp
#viewer icons
viewers iconset.480x720x(16|24|32): icons/tango_icons_viewers.32x32.bmp
viewers iconset.800x480x(16|24|32): icons/tango_icons_viewers.32x32.bmp
viewers iconset.480x800x(16|24|32): icons/tango_icons_viewers.32x32.bmp
viewers iconset.400x240x(16|24|32): icons/tango_icons_viewers.16x16.bmp

View file

@ -0,0 +1,119 @@
# Cabbie v2.0
# (C) 2007-2012 The Authors (see /rockbox/wps/AUTHORS)
# Derived from "cabbie" (C) Yohann Misquitta
#
# TODO:
# * images for battery, hold button
%wd
%X(wpsbackdrop-480x720x16.bmp)
#%xl(A,lock-240x320x16.bmp,11,0,2)
#%xl(B,battery-240x320x16.bmp,46,0,10)
%xl(C,volume-480x800x16.bmp,145,125,10)
%xl(D,shuffle-480x800x16.bmp,240,133)
%xl(E,repeat-480x800x16.bmp,343,121,4)
%xl(F,playmode-480x800x16.bmp,403,119,5)
%xl(G,popup-480x800x16.bmp,0,0)
%xl(H,rew-480x800x16.bmp,0,5)
%xl(I,ff-480x800x16.bmp,70,5)
%xl(vol,volumebar-480x800x16.bmp,0,0)
%xl(volbd,volumebar-backdrop-480x800x16.bmp,0,0)
%Cl(0,0,275,275,c,c)
%?C<%Vd(c)|%Vd(a)>
%?vg(show_vol)<%?C<%Vd(d)|%Vd(b)>|%Vd(t)|%Vd(vol)>
# now playing text bar
%V(0,28,-,42,-)
%ac%Sx(Now Playing)
# track & next track info - no AA
%Vl(a,0,102,-,180,-)
# tap on current title info for playlist (ie. where albumart would normally be)
%T(0,0,275,275,playlist)
%s%ac%?id<%id|%?d(1)<%d(1)|%(root%)>>
%s%ac%?it<%it|%fn>
%s%ac%?ia<%ia|%?iA<%iA|%?d(2)<%d(2)|%(root%)>>>
%ac%?iy<%iy|>
%Vl(b,0,320,-,180,-)
%ac%?ig<%ig|>
%ac%?fv<%(vbr%) |>%fb kbit/s %fc
%s%ac%?Ia<%Ia|%?IA<%IA|%?D(2)<%D(2)|%(root%)>>>
%ac%Sx(Next Track:)
%ac%s%?It<%It|%Fn>
#
# album art viewport
#
%Vl(c,102,75,275,275,-)
# tap on the cover art for playlist
%T(0,0,275,275,playlist)
%Cd
# current track info - AA
%Vl(d,0,370,-,-200,-)
%s%ac%?id<%id|%?d(1)<%d(1)|%(root%)>>
%s%ac%?it<%it|%fn>
%s%ac%?ia<%ia|%?iA<%iA|%?d(2)<%d(2)|%(root%)>>>
# next track info - AA
%Vl(d,0,470,-,-200,-)
%?C<%s%ac%Sx(Next:) %?Ia<%Ia|%?IA<%IA|%?D(2)<%D(2)|%(root%)>>> - %?It<%It|%Fn>|%s%ac%?Id<%Id|%?D(1)<%D(1)|%(root%)>>>
# playtime
%V(20,580,440,36,-)
%pc%ac%?Sr<%pe %Sx(of) %pp|%pp %Sx(of) %pe>%ar%pr
# progressbar and bottom icons
%V(0,520,-,-,-)
%pb(25,11,430,-,pb-480x800x16.bmp)
%T(25,0,430,50,progressbar)
#%?mh<%xd(Aa)|%xd(Ab)>
#%?bp<%?bc<%xd(Ba)|%xd(Bb)>|%?bl<|%xd(Bc)|%xd(Bd)|%xd(Be)|%xd(Bf)|%xd(Bg)|%xd(Bh)|%xd(Bi)|%xd(Bj)>>
# volume
%?pv<%xd(Ca)|%xd(Cb)|%xd(Cc)|%xd(Cd)|%xd(Ce)|%xd(Cf)|%xd(Cg)|%xd(Ch)|%xd(Ci)|%xd(Cj)>
%T(small_vol,145,125,88,60,none)
#
# shuffle
%?ps<%xd(D)>
%T(237,130,100,50,shuffle)
#
# repeat
%xd(E, %mm, -1)
%T(340,121,50,70,repmode)
#
# playmode
%?Tp<%?mp<%xd(F, 1)|%xd(F, 3)|%xd(F, 2)|%xd(F, 4)|%xd(F, 5)||||>|%xd(F, %mp)>
%T(400,119,70,75,play)
%T(400,119,70,75,stop, repeat_press)
#
# volume slider
#
%Vl(vol,14,295,452,205,-)
%pv(0,0,452,205,image,vol,backdrop,volbd)
%T(0,0,452,205,volume)
#
# popup osd menu
#
%Vl(t,14,295,452,205,-)
%xd(G)
%T(26,26,160,148,browse)
%T(186,26,144,148,quickscreen)
%T(304,26,123,148,contextmenu)
#
# ff/rewind button
#
%V(0,640,150,75,-)
%xd(H)%xd(I)
%T(0,0,70,75,rwd, repeat_press)
%T(0,0,70,75,prev)
%T(70,0,70,75,ffwd, repeat_press)
%T(70,0,70,75,next)
# needs to be at the end since touch regions need to be declared
# before %Tl can be used
%?Tl(small_vol,2.0)<%vs(show_vol,set,3)|%?mv(2.0)<%vs(show_vol,set,3)|%?Tl<%vs(show_vol,set,2)|%vs(show_vol,set,1)>>>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,013 KiB

View file

@ -0,0 +1,96 @@
# Classic Statusbar
# (C) 2007-2012 The Authors (see /rockbox/wps/AUTHORS)
#
# Classic statusbar adapted to skin engine
# Standardized to a height of 24px for larger devices
#
# Status bar position can be changed from top to bottom by changing the y positions from 0 to -24
#
# Specify the UI area viewport... everything but a bar 24 pixels high at the top
%Vi(-,0,24,-,-,1)
# Conditional for showing volume as number or graphic
%?if(%St(volume display), =, numeric)<%Vd(d)|%Vd(c)>
# Conditional for showing battery as number or graphic
%?if(%St(battery display), =, numeric)<%Vd(b)|%Vd(a)%Vd(e)>
# Load some bitmaps
%xl(B,battery.16.bmp,0,0,16)
%xl(V,volume.16.bmp,2,0,17)
%xl(S,status.16.bmp,0,0,15)
%xl(D,access_disk.16.bmp,0,0)
%xl(y,batter-y.16.bmp,0,0)
%xl(m,rec_mpegbitrates.16.bmp,2,0,18)
%xl(f,rec_frequencies.16.bmp,0,0,12)
%xl(e,rec_encoders.16.bmp,0,0,3)
%xl(c,rec_channels.16.bmp,26,0,2)
# Enable the correct viewports
%?cs<%Vd(c)%Vd(p)%Vd(r)%Vd(s)|%Vd(c)%Vd(p)%Vd(r)%Vd(s)|%Vd(z)|%Vd(c)%Vd(p)%Vd(r)%Vd(s)>
# Charging animation viewports
%t(2)%?bc<%Vd(x)|%Vd(a)>;%t(2)%Vd(a)
# Battery area, Icons
%Vl(a,4,6,34,14,0)
%?bl<%xd(Ba)|%xd(Bb)|%xd(Bc)|%xd(Bd)|%xd(Be)|%xd(Bf)|%xd(Bg)|%xd(Bh)|%xd(Bi)|%xd(Bj)|%xd(Bk)|%xd(Bl)|%xd(Bm)|%xd(Bn)|%xd(Bo)>
# Battery area, grey background viewport for charging animation
%Vl(x,4,6,34,14,0)%Vb(555555)
#%?bl<%xd(Ba)|%xd(Bb)|%xd(Bc)|%xd(Bd)|%xd(Be)|%xd(Bf)|%xd(Bg)|%xd(Bh)|%xd(Bi)|%xd(Bj)|%xd(Bk)|%xd(Bl)|%xd(Bm)|%xd(Bn)|%xd(Bo)>
# end of battery symbol (to exclude it from animation)
%Vl(e,38,6,6,14,0)
%xd(y)
# usb power or charger icon
%V(44,6,16,16,0)
%?bu<%xd(Sa)|%?bc<%xd(Sa)|%?bp<%xd(So)>>>
# battery area, no icons
%Vl(b,4,4,36,16,0)
%ar%bl
# Volume area
%Vl(c,60,6,58,16,0)
%?mv<%ac%?pv<%pv|%pv| %pv| %pv>|%?pv<%xd(Va)|%xd(Vb)|%xd(Vc)|%xd(Vd)|%xd(Ve)|%xd(Vf)|%xd(Vg)|%xd(Vh)|%xd(Vi)|%xd(Vj)|%xd(Vk)|%xd(Vl)|%xd(Vm)|%xd(Vn)|%xd(Vo)|%xd(Vp)|%xd(Vq)>>
%Vl(d,60,4,58,16,0)
%ac%?pv<%pv|%pv| %pv| %pv>
# Icons, all in the same bmp strip, so need to use multiple viewports
# Playback mode
%Vl(p,118,6,18,16,0)
%?mp<%xd(Sc)|%xd(Sb)|%xd(Sd)|%xd(Se)|%xd(Sf)|%xd(Sg)|%xd(Sh)|%xd(Si)|%xd(Sj)>
# Repeat mode
%Vl(r,136,6,18,16,0)
%?mm<|%xd(Sk)|%xd(Sl)|%xd(Sm)|%xd(Sn)>
# Shuffle mode
%Vl(s,154,6,-,16,0)
%?ps<%xd(Sm)|>
# Recording section
# encoder/mpeg bitrate
%Vl(z,60,4,38,16,0)
%?Rp<%?Re<%xd(ea)|%xd(eb)|%xd(ec)|%?Rb<%xd(ma)|%xd(mb)|%xd(mc)|%xd(md)|%xd(me)|%xd(mf)|%xd(mg)|%xd(mh)|%xd(mi)|%xd(mj)|%xd(mk)|%xd(ml)|%xd(mm)|%xd(mn)|%xd(mo)|%xd(mp)|%xd(mq)|%xd(mr)>>>
# status icon
%Vl(z,98,4,16,16,0)
%?mp<%xd(Sc)|%xd(Sb)|%xd(Sd)|%xd(Se)|%xd(Sf)|%xd(Sg)|%xd(Sh)|%xd(Si)|%xd(Sj)>
# frequency and channels
%Vl(z,114,4,-,16,0)
%?Rp<%?Rf<%xd(fa)|%xd(fb)|%xd(fc)|%xd(fd)|%xd(fe)|%xd(ff)|%xd(fg)|%xd(fh)|%xd(fi)|%xd(fj)|%xd(fk)|%xd(fl)>>
%?Rm<%xd(ca)|%xd(cb)>
# Clock on RTC able targets, and disk access
# align on the right with room for 5 SYSFONT digits
%V(-82,4,62,16,0)
%?cc<%?ca<%?St(time format)<%cH|%cI>:%cM|--:-->|>
# disk access icon
%V(-20,6,-,16,0)
%?lh<%xd(D)|>

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,014 B

288
wps/template_fill.py Normal file
View file

@ -0,0 +1,288 @@
"""
Rough script to scale up the existing classic_statusbar
It will scale up by scale_pixel_size_by, and add padding specificed by
padding_x/y.
The status bar size will be determined by main_viewport_size_by.
So if you want zero padding just make sure those two numbers are the same.
This is all based on the 8px tall classic_statusbar, but this script can
be adapted for different sbs or wps file. Just need to create a template
which is just jinja template syntax on top of a sbs or wps.
The images were hacked together, you specify their real image path
from the wps folder, but it will hack off the asset folder because
rockbox automatically adds it. Oops.
"""
from jinja2 import Environment, PackageLoader, select_autoescape
from PIL import Image
import os
env = Environment(
loader=PackageLoader("template_fill"),
autoescape=select_autoescape()
)
template = env.get_template("classic_statusbar.sbs.j2")
original_pixel_size = 8
padding_x = 4
padding_y = 4
scale_pixel_size_by = 2
main_viewport_size_by = 3
scaled_pixel_size = original_pixel_size * scale_pixel_size_by
main_viewport_size = original_pixel_size * main_viewport_size_by
font_id = 0
asset_folder = 'classic_statusbar'
def upscale_element(element: dict) -> dict:
if element.get('image'):
element_img: str = element['image']
# Split off the last .bmp (and recombine if the filename contains periods)
out_element_image = (
f'{'.'.join(element_img.split('.')[0:-1])}.{scaled_pixel_size}.bmp'
)
try:
if os.path.isfile(out_element_image):
im = Image.open(out_element_image)
else:
im = Image.open(element_img)
size = im.width * scale_pixel_size_by, im.height * scale_pixel_size_by
im = im.resize(size, Image.Resampling.NEAREST)
im.save(out_element_image, "BMP")
# Bit of a hack really
element['image'] = out_element_image.replace(
f'{asset_folder}/', '')
except IOError:
print(f"cannot resize for <{element_img}>")
if element.get('source_x') and element['source_x'] != '-':
element['source_x'] *= scale_pixel_size_by
if element.get('source_y') and element['source_y'] != '-':
element['source_x'] *= scale_pixel_size_by
if element.get('viewport'):
if element['viewport']['x'] != '-':
element['viewport']['x'] *= scale_pixel_size_by
element['viewport']['x'] += padding_x
if element['viewport']['y'] != '-':
element['viewport']['y'] *= scale_pixel_size_by
element['viewport']['y'] += padding_x
if element['viewport']['width'] != '-':
element['viewport']['width'] *= scale_pixel_size_by
if element['viewport']['height'] != '-':
element['viewport']['height'] *= scale_pixel_size_by
return element
# These are based on the original resolution, they're upscaled later
battery = upscale_element({
'image': 'classic_statusbar/battery.bmp',
'source_x': 0,
'source_y': 0,
'sub_image_count': 16,
'viewport': {
'x': 0,
'y': 0,
'width': 17,
'height': 7,
'font_id': font_id
}
})
battery_cap = upscale_element({
'image': 'classic_statusbar/batter-y.bmp',
'source_x': 0,
'source_y': 0,
'sub_image_count': 0,
'viewport': {
'x': 17,
'y': 0,
'width': 3,
'height': 7,
'font_id': font_id
}
})
battery_no_icon = upscale_element({
'viewport': {
'x': 0,
'y': 0,
'width': 18,
'height': 8,
'font_id': font_id
}
})
volume = upscale_element({
'image': 'classic_statusbar/volume.bmp',
'source_x': 1,
'source_y': 0,
'sub_image_count': 17,
'viewport': {
'x': 28,
'y': 0,
'width': 19,
'height': 8,
'font_id': font_id
}
})
status = upscale_element({
'image': 'classic_statusbar/status.bmp',
'source_x': 0,
'source_y': 0,
'sub_image_count': 15,
'viewport': {
'x': 47,
'y': 0,
'width': 8,
'height': 8,
'font_id': font_id
}
})
power = upscale_element({
'viewport': {
'x': 20,
'y': 0,
'width': 8,
'height': 8,
'font_id': font_id
}
})
playback = upscale_element({
'viewport': {
'x': 47,
'y': 0,
'width': 9,
'height': 8,
'font_id': font_id
}
})
repeat = upscale_element({
'viewport': {
'x': 56,
'y': 0,
'width': 9,
'height': 8,
'font_id': font_id
}
})
shuffle = upscale_element({
'viewport': {
'x': 65,
'y': 0,
'width': '-',
'height': 8,
'font_id': font_id
}
})
access_disk = upscale_element({
'image': 'classic_statusbar/access_disk.bmp',
'source_x': 0,
'source_y': 0,
'sub_image_count': 0,
'viewport': {
'x': -12,
'y': 0,
'width': '-',
'height': 8,
'font_id': font_id
}
})
rec_bitrate = upscale_element({
'image': 'classic_statusbar/rec_mpegbitrates.bmp',
'source_x': 1,
'source_y': 0,
'sub_image_count': 18,
'viewport': {
'x': 28,
'y': 0,
'width': 19,
'height': 8,
'font_id': font_id
}
})
rec_freq = upscale_element({
'image': 'classic_statusbar/rec_frequencies.bmp',
'source_x': 0,
'source_y': 0,
'sub_image_count': 12,
'viewport': {
'x': 55,
'y': 0,
'width': '-',
'height': 8,
'font_id': font_id
}
})
rec_encoders = upscale_element({
'image': 'classic_statusbar/rec_encoders.bmp',
'source_x': 0,
'source_y': 0,
'sub_image_count': 3,
})
rec_channels = upscale_element({
'image': 'classic_statusbar/rec_channels.bmp',
'source_x': 13,
'source_y': 0,
'sub_image_count': 2,
})
rtc = upscale_element({
'viewport': {
'x': -43,
'y': 0,
'width': 31,
'height': 8,
'font_id': font_id
}
})
rendered = template.render(
viewport={
'width': 0,
'height': main_viewport_size,
'font_id': 1
},
options={},
battery=battery,
battery_cap=battery_cap,
battery_no_icon=battery_no_icon,
volume=volume,
status=status,
access_disk=access_disk,
rec_bitrate=rec_bitrate,
rec_freq=rec_freq,
rec_encoders=rec_encoders,
rec_channels=rec_channels,
power=power,
playback=playback,
repeat=repeat,
shuffle=shuffle,
rtc=rtc,
)
print(rendered)
with open('./classic_statusbar.sbs', 'w') as fh:
fh.write(rendered)

View file

@ -0,0 +1,96 @@
# Classic Statusbar
# (C) 2007-2012 The Authors (see /rockbox/wps/AUTHORS)
#
# Classic statusbar adapted to skin engine
# Standardized to a height of {{ viewport.height }}px for larger devices
#
# Status bar position can be changed from top to bottom by changing the y positions from 0 to -{{ viewport.height }}
#
# Specify the UI area viewport... everything but a bar {{ viewport.height }} pixels high at the top
%Vi(-,{{ viewport.width }},{{ viewport.height }},-,-,{{ viewport.font_id }})
# Conditional for showing volume as number or graphic
%?if(%St(volume display), =, numeric)<%Vd(d)|%Vd(c)>
# Conditional for showing battery as number or graphic
%?if(%St(battery display), =, numeric)<%Vd(b)|%Vd(a)%Vd(e)>
# Load some bitmaps
%xl(B,{{ battery.image }},{{ battery.source_x }},{{ battery.source_y }},{{ battery.sub_image_count }})
%xl(V,{{ volume.image }},{{ volume.source_x }},{{ volume.source_y }},{{ volume.sub_image_count }})
%xl(S,{{ status.image }},{{ status.source_x }},{{ status.source_y }},{{ status.sub_image_count }})
%xl(D,{{ access_disk.image }},{{ access_disk.source_x }},{{ access_disk.source_y }})
%xl(y,{{ battery_cap.image }},{{ battery_cap.source_x }},{{ battery_cap.source_y }})
%xl(m,{{ rec_bitrate.image }},{{ rec_bitrate.source_x }},{{ rec_bitrate.source_y }},{{ rec_bitrate.sub_image_count }})
%xl(f,{{ rec_freq.image }},{{ rec_freq.source_x }},{{ rec_freq.source_y }},{{ rec_freq.sub_image_count }})
%xl(e,{{ rec_encoders.image }},{{ rec_encoders.source_x }},{{ rec_encoders.source_y }},{{ rec_encoders.sub_image_count }})
%xl(c,{{ rec_channels.image }},{{ rec_channels.source_x }},{{ rec_channels.source_y }},{{ rec_channels.sub_image_count }})
# Enable the correct viewports
%?cs<%Vd(c)%Vd(p)%Vd(r)%Vd(s)|%Vd(c)%Vd(p)%Vd(r)%Vd(s)|%Vd(z)|%Vd(c)%Vd(p)%Vd(r)%Vd(s)>
# Charging animation viewports
%t(2)%?bc<%Vd(x)|%Vd(a)>;%t(2)%Vd(a)
# Battery area, Icons
%Vl(a, {{ battery.viewport.x }},{{ battery.viewport.y }},{{ battery.viewport.width }},{{ battery.viewport.height }},{{ battery.viewport.font_id }})
%?bl<%xd(Ba)|%xd(Bb)|%xd(Bc)|%xd(Bd)|%xd(Be)|%xd(Bf)|%xd(Bg)|%xd(Bh)|%xd(Bi)|%xd(Bj)|%xd(Bk)|%xd(Bl)|%xd(Bm)|%xd(Bn)|%xd(Bo)>
# Battery area, grey background viewport for charging animation
%Vl(x, {{ battery.viewport.x }},{{ battery.viewport.y }},{{ battery.viewport.width }},{{ battery.viewport.height }},{{ battery.viewport.font_id }})%Vb(555555)
#%?bl<%xd(Ba)|%xd(Bb)|%xd(Bc)|%xd(Bd)|%xd(Be)|%xd(Bf)|%xd(Bg)|%xd(Bh)|%xd(Bi)|%xd(Bj)|%xd(Bk)|%xd(Bl)|%xd(Bm)|%xd(Bn)|%xd(Bo)>
# end of battery symbol (to exclude it from animation)
%Vl(e,{{ battery_cap.viewport.x }},{{ battery_cap.viewport.y }},{{ battery_cap.viewport.width }},{{ battery_cap.viewport.height }},{{ battery_cap.viewport.font_id }})
%xd(y)
# usb power or charger icon
%V({{ power.viewport.x }},{{ power.viewport.y }},{{ power.viewport.width }},{{ power.viewport.height }},{{ power.viewport.font_id }})
%?bu<%xd(Sa)|%?bc<%xd(Sa)|%?bp<%xd(So)>>>
# battery area, no icons
%Vl(b,{{ battery_no_icon.viewport.x }},{{ battery_no_icon.viewport.y }},{{ battery_no_icon.viewport.width }},{{ battery_no_icon.viewport.height }},{{ battery_no_icon.viewport.font_id }})
%ar%bl
# Volume area
%Vl(c,{{ volume.viewport.x }},{{ volume.viewport.y }},{{ volume.viewport.width }},{{ volume.viewport.height }},{{ volume.viewport.font_id }})
%?mv<%ac%?pv<%pv|%pv| %pv| %pv>|%?pv<%xd(Va)|%xd(Vb)|%xd(Vc)|%xd(Vd)|%xd(Ve)|%xd(Vf)|%xd(Vg)|%xd(Vh)|%xd(Vi)|%xd(Vj)|%xd(Vk)|%xd(Vl)|%xd(Vm)|%xd(Vn)|%xd(Vo)|%xd(Vp)|%xd(Vq)>>
%Vl(d,{{ volume.viewport.x }},{{ volume.viewport.y }},{{ volume.viewport.width }},{{ volume.viewport.height }},{{ volume.viewport.font_id }})
%ac%?pv<%pv|%pv| %pv| %pv>
# Icons, all in the same bmp strip, so need to use multiple viewports
# Playback mode
%Vl(p,{{ playback.viewport.x }},{{ playback.viewport.y }},{{ playback.viewport.width }},{{ playback.viewport.height }},{{ playback.viewport.font_id }})
%?mp<%xd(Sc)|%xd(Sb)|%xd(Sd)|%xd(Se)|%xd(Sf)|%xd(Sg)|%xd(Sh)|%xd(Si)|%xd(Sj)>
# Repeat mode
%Vl(r,{{ repeat.viewport.x }},{{ repeat.viewport.y }},{{ repeat.viewport.width }},{{ repeat.viewport.height }},{{ repeat.viewport.font_id }})
%?mm<|%xd(Sk)|%xd(Sl)|%xd(Sm)|%xd(Sn)>
# Shuffle mode
%Vl(s,{{ shuffle.viewport.x }},{{ shuffle.viewport.y }},{{ shuffle.viewport.width }},{{ shuffle.viewport.height }},{{ shuffle.viewport.font_id }})
%?ps<%xd(Sm)|>
# Recording section
# encoder/mpeg bitrate
%Vl(z,{{ rec_bitrate.viewport.x }},{{ rec_bitrate.viewport.y }},{{ rec_bitrate.viewport.width }},{{ rec_bitrate.viewport.height }},{{ rec_bitrate.viewport.font_id }})
%?Rp<%?Re<%xd(ea)|%xd(eb)|%xd(ec)|%?Rb<%xd(ma)|%xd(mb)|%xd(mc)|%xd(md)|%xd(me)|%xd(mf)|%xd(mg)|%xd(mh)|%xd(mi)|%xd(mj)|%xd(mk)|%xd(ml)|%xd(mm)|%xd(mn)|%xd(mo)|%xd(mp)|%xd(mq)|%xd(mr)>>>
# status icon
%Vl(z,{{ status.viewport.x }},{{ status.viewport.y }},{{ status.viewport.width }},{{ status.viewport.height }},{{ status.viewport.font_id }})
%?mp<%xd(Sc)|%xd(Sb)|%xd(Sd)|%xd(Se)|%xd(Sf)|%xd(Sg)|%xd(Sh)|%xd(Si)|%xd(Sj)>
# frequency and channels
%Vl(z,{{ rec_freq.viewport.x }},{{ rec_freq.viewport.y }},{{ rec_freq.viewport.width }},{{ rec_freq.viewport.height }},{{ rec_freq.viewport.font_id }})
%?Rp<%?Rf<%xd(fa)|%xd(fb)|%xd(fc)|%xd(fd)|%xd(fe)|%xd(ff)|%xd(fg)|%xd(fh)|%xd(fi)|%xd(fj)|%xd(fk)|%xd(fl)>>
%?Rm<%xd(ca)|%xd(cb)>
# Clock on RTC able targets, and disk access
# align on the right with room for 5 SYSFONT digits
%V({{ rtc.viewport.x }},{{ rtc.viewport.y }},{{ rtc.viewport.width }},{{ rtc.viewport.height }},{{ rtc.viewport.font_id }})
%?cc<%?ca<%?St(time format)<%cH|%cI>:%cM|--:-->|>
# disk access icon
%V({{ access_disk.viewport.x }},{{ access_disk.viewport.y }},{{ access_disk.viewport.width }},{{ access_disk.viewport.height }},{{ access_disk.viewport.font_id }})
%?lh<%xd(D)|>