A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 464 lines 16 kB view raw
1--[[ Lua Print functions 2/*************************************************************************** 3 * __________ __ ___. 4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 * \/ \/ \/ \/ \/ 9 * $Id$ 10 * 11 * Copyright (C) 2017 William Wilgus 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 2 16 * of the License, or (at your option) any later version. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ****************************************************************************/ 22]] 23 24--[[ Exposed Functions 25 26 _print.clear 27 _print.f 28 _print.opt 29 _print.opt.area 30 _print.opt.autoupdate 31 _print.opt.color 32 _print.opt.column 33 _print.opt.defaults 34 _print.opt.get 35 _print.opt.justify 36 _print.opt.line 37 _print.opt.overflow 38 _print.opt.sel_line 39 _print.opt.set 40 41]] 42 43if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 44 45local _print = {} do 46 47 -- internal constants 48 local _clr = require("color") -- _clr functions required 49 50 local _NIL = nil -- _NIL placeholder 51 local _LCD = rb.lcd_framebuffer() 52 local WHITE = _clr.set(-1, 255, 255, 255) 53 local BLACK = _clr.set(0, 0, 0, 0) 54 local DRMODE_SOLID = 3 55 local col_buf, s_lines = {}, {} 56 local _p_opts = _NIL 57 local tabstring = string.rep(" ", 2) 58-- print internal helper functions 59-------------------------------------------------------------------------------- 60 -- clamps value to >= min and <= max 61 local function clamp(val, min, max) 62 -- Warning doesn't check if min < max 63 if val < min then 64 return min 65 elseif val < max then 66 return val 67 end 68 return max 69 end 70 71 -- Gets size of text 72 local function text_extent(msg, font) 73 -- res, w, h 74 return rb.font_getstringsize(msg, font or rb.FONT_UI) 75 end 76 77 -- Updates a single line on the screen 78 local function update_line(enabled, opts, line, h) 79 if enabled ~= true then return end 80 local o = opts or _p_opts 81 -- updates screen in specified rectangle 82 rb.lcd_update_rect(o.x - 1, o.y + line * h, 83 clamp(o.x + o.width, 1, rb.LCD_WIDTH) - 1, 84 clamp(o.y + line * h + 1 + h, 1, rb.LCD_HEIGHT) - 1) 85 end 86 87 -- Clears a single line on the screen 88 local function clear_line(opts, line, h) 89 local o = opts or _p_opts 90 _LCD:clear(o.bg_pattern, o.x, o.y + line * h + 1, 91 o.x + o.width, line * h + h + o.y) 92 end 93 94 -- Sets the maximum number of lines on the screen 95 local function max_lines(opts) 96 local h = opts.height 97 local _, _, th = text_extent("W", opts.font) 98 return h / th 99 end 100 101 --saves the items displayed for side to side scroll 102 local function col_buf_insert(msg, line, _p_opts) 103 --if _p_opts.line <= 1 then col_buf = {} end 104 if not col_buf[line] then 105 table.insert(col_buf, line, msg) 106 end 107 end 108 109 --replaces / strips tab characters 110 local function check_escapes(o, msg) 111--[[ --for replacing a variety of escapes 112 local tabsz = 2 113 local tabstr = string.rep(" ", tabsz) 114 local function repl(esc) 115 local ret = "" 116 if esc:sub(1,1) == "\t" then ret = string.rep(tabstr, esc:len()) end 117 return ret 118 end 119 msg = msg:gsub("(%c+)", repl) 120]] 121 msg = msg:gsub("\t", tabstring) 122 local res, w, h = text_extent(msg, o.font) 123 return w, h, msg 124 end 125-------------------------------------------------------------------------------- 126 local function set_linedesc(t_linedesc, opts) 127 local o = opts or _print.opt.get(true) 128 --local out = function() local t = {} for k, v in pairs(o) do t[#t + 1] = tostring(k) t[#t + 1] = tostring(v) end return table.concat(t, "\n") end 129 --rb.splash_scroller(1000, out()) 130 local linedesc ={ 131 --These are the defaults - changes will be made below if you supplied t_linedesc 132 indent = 0, -- internal indent text 133 line = 0, -- line index within group 134 nlines = 1, -- selection grouping 135 offset = 0, -- internal item offset 136 scroll = true, 137 selected = false, --internal 138 separator_height = 0, 139 line_separator = false, 140 show_cursor = false, 141 show_icons = false, 142 icon = -1, 143 icon_fn = function() return -1 end, 144 style = rb.STYLE_COLORBAR, 145 text_color = o.fg_pattern or WHITE, 146 line_color = o.bg_pattern or BLACK, 147 line_end_color= o.bg_pattern or BLACK, 148 } 149 if type(t_linedesc) == "table" then 150 151 if not o.linedesc then 152 o.linedesc = {} 153 for k, v in pairs(linedesc) do 154 o.linedesc[k] = v 155 end 156 end 157 158 for k, v in pairs(t_linedesc) do 159 o.linedesc[k] = v 160 end 161 if o.linedesc.separator_height > 0 then 162 o.linedesc.line_separator = true 163 end 164 return 165 end 166 o.linedesc = linedesc 167 return o.linedesc 168 end 169 170 -- set defaults for print view 171 local function set_defaults() 172 _p_opts = { x = 1, 173 y = 1, 174 width = rb.LCD_WIDTH - 1, 175 height = rb.LCD_HEIGHT - 1, 176 font = rb.FONT_UI, 177 drawmode = DRMODE_SOLID, 178 fg_pattern = WHITE, 179 bg_pattern = BLACK, 180 sel_pattern = WHITE, 181 line = 1, 182 max_line = _NIL, 183 col = 0, 184 header = false, --Internal use - treats first entry as header 185 ovfl = "auto", -- auto, manual, none 186 justify = "left", --left, center, right 187 autoupdate = true, --updates screen as items are added 188 drawsep = false, -- separator between items 189 } 190 set_linedesc(nil, _p_opts) -- default line display 191 _p_opts.max_line = max_lines(_p_opts) 192 193 s_lines, col_buf = {}, {} 194 return _p_opts 195 end 196 197 -- returns table with settings for print 198 -- if bByRef is _NIL or false then a copy is returned 199 local function get_settings(bByRef) 200 _p_opts = _p_opts or set_defaults() 201 if not bByRef then 202 -- shallow copy of table 203 local copy = {} 204 for k, v in pairs(_p_opts) do 205 copy[k] = v 206 end 207 return copy 208 end 209 210 return _p_opts 211 end 212 213 -- sets the settings for print with your passed table 214 local function set_settings(t_opts) 215 _p_opts = t_opts or set_defaults() 216 if t_opts then 217 _p_opts.max_line = max_lines(_p_opts) 218 col_buf = {} 219 end 220 end 221 222 -- sets colors for print 223 local function set_color(fgclr, bgclr, selclr) 224 local o = get_settings(true) 225 226 if fgclr ~= _NIL then 227 o.fg_pattern, o.sel_pattern = fgclr, fgclr 228 end 229 o.sel_pattern = selclr or o.sel_pattern 230 o.bg_pattern = bgclr or o.bg_pattern 231 end 232 233 -- helper function sets up colors/marker for selected items 234 local function show_selected(iLine, msg) 235 if rb.LCD_DEPTH == 2 then -- invert 2-bit screens 236 local o = get_settings() -- using a copy of opts so changes revert 237 if not o then rb.set_viewport() return end 238 o.fg_pattern = 3 - o.fg_pattern 239 o.bg_pattern = 3 - o.bg_pattern 240 rb.set_viewport(o) 241 o = _NIL 242 else 243 show_selected = function() end -- no need to check again 244 end 245 end 246 247 -- sets line explicitly or increments line if line is _NIL 248 local function set_line(iLine) 249 local o = get_settings(true) 250 251 o.line = iLine or o.line + 1 252 253 if(o.line < 1 or o.line > o.max_line) then 254 o.line = 1 255 end 256 end 257 258 -- clears the set print area 259 local function clear() 260 local o = get_settings(true) 261 _LCD:clear(o.bg_pattern, o.x, o.y, o.x + o.width, o.y + o.height) 262 if o.autoupdate == true then rb.lcd_update() end 263 rb.lcd_scroll_stop() 264 set_line(1) 265 for i=1, #col_buf do col_buf[i] = _NIL end 266 s_lines = {} 267 collectgarbage("collect") 268 end 269 270 -- screen update after each call to print.f 271 local function set_update(bAutoUpdate) 272 local o = get_settings(true) 273 o.autoupdate = bAutoUpdate or false 274 end 275 276 -- sets print area 277 local function set_area(x, y, w, h) 278 local o = get_settings(true) 279 o.x, o.y = clamp(x, 1, rb.LCD_WIDTH), clamp(y, 1, rb.LCD_HEIGHT) 280 o.width, o.height = clamp(w, 1, rb.LCD_WIDTH - o.x), clamp(h, 1, rb.LCD_HEIGHT - o.y) 281 o.max_line = max_lines(_p_opts) 282 283 clear() 284 return o.line, o.max_line 285 end 286 287 -- when string is longer than print width scroll -- "auto", "manual", "none" 288 local function set_overflow(str_mode) 289 -- "auto", "manual", "none" 290 local str_mode = str_mode or "auto" 291 local o = get_settings(true) 292 o.ovfl = str_mode:lower() 293 col_buf = {} 294 end 295 296 -- aligns text to: "left", "center", "right" 297 local function set_justify(str_mode) 298 -- "left", "center", "right" 299 local str_mode = str_mode or "left" 300 local o = get_settings(true) 301 o.justify = str_mode:lower() 302 end 303 304 -- selects line 305 local function select_line(iLine) 306 s_lines[iLine] = true 307 end 308 309 -- Internal print function 310 local function print_internal(t_opts, x, w, h, msg) 311 312 local linedesc 313 local line_separator = false 314 local o = t_opts 315 local ld = o.linedesc or set_linedesc() 316 local show_cursor = ld.show_cursor or 0 317 local line_indent = 0 318 319 local linestyle = ld.style or rb.STYLE_COLORBAR 320 321 if o.justify ~= "left" then 322 line_indent = (o.width - w) --"right" 323 if o.justify == "center" then 324 line_indent = line_indent / 2 325 end 326 end 327 328 local line = o.line - 1 -- rb is 0-based lua is 1-based 329 330 if o.ovfl == "manual" then --save msg for later side scroll 331 col_buf_insert(msg, o.line, o) 332 end 333 334 -- bit of a pain to set the fields this way but its much more efficient than iterating a table to set them 335 local function set_desc(tld, scroll, separator_height, selected, style, indent, text_color, line_color, line_end_color) 336 tld.scroll = scroll 337 tld.separator_height = separator_height 338 tld.selected = selected 339 tld.style = style 340 tld.indent = indent 341 tld.text_color = text_color 342 tld.line_color = line_color 343 tld.line_end_color = line_end_color 344 end 345 346 line_separator = ld.line_separator or o.drawsep 347 local indent = line_indent < 0 and 0 or line_indent --rb scroller doesn't like negative offset! 348 if o.line == 1 and o.header then 349 set_desc(ld, true, 1, false, rb.STYLE_DEFAULT, 350 indent, o.fg_pattern, o.bg_pattern, o.bg_pattern) 351 ld.show_cursor = false 352 elseif s_lines[o.line] then 353 --/* Display line selector */ 354 local style = show_cursor == true and rb.STYLE_DEFAULT or linestyle 355 local ovfl = (o.ovfl == "auto" and w >= o.width and x == 0) 356 set_desc(ld, ovfl, 0, true, style, indent, 357 o.bg_pattern, o.sel_pattern, o.sel_pattern) 358 else 359 set_desc(ld, false, 0, false, rb.STYLE_DEFAULT,line_indent, 360 o.fg_pattern, o.bg_pattern, o.bg_pattern) 361 end 362 363 if ld.show_icons then 364 ld.icon = ld.icon_fn(line, ld.icon or -1) 365 end 366 367 rb.lcd_put_line(x, line *h, msg, ld) 368 369 ld.show_cursor = show_cursor 370 ld.style = linestyle 371 if line_separator then 372 if ld.selected == true then 373 rb.set_viewport(o) -- revert drawmode if selected 374 end 375 if not o.header then 376 rb.lcd_drawline(0, line * h, o.width, line * h) 377 end 378 rb.lcd_drawline(0, line * h + h, o.width, line * h + h) --only to add the last line 379 -- but we don't have an idea which line is the last line here so every line is the last line! 380 end 381 382 --only update the line we changed 383 update_line(o.autoupdate, o, line, h) 384 385 set_line(_NIL) -- increments line counter 386 end 387 388 -- Helper function that acts mostly like a normal printf() would 389 local function printf(fmt, v1, ...) 390 local o = get_settings(true) 391 local w, h, msg, rep 392 local line = o.line - 1 -- rb is 0-based lua is 1-based 393 394 if not (fmt) or (fmt) == "\n" then -- handles blank line / single '\n' 395 local res, w, h = text_extent(" ", o.font) 396 397 clear_line(o, line, h) 398 update_line(o.autoupdate, o, line, h) 399 400 if (fmt) then set_line(_NIL) end 401 402 return o.line, o.max_line, o.width, h 403 end 404 405 fmt, rep = fmt.gsub(fmt or "", "%%h", "%%s") 406 o.header = (rep == 1) 407 408 msg = string.format(fmt, v1, ...) 409 410 show_selected(o.line, msg) 411 412 w, h, msg = check_escapes(o, msg) 413 414 print_internal(o, o.col, w, h, msg) 415 416 return o.line, o.max_line, w, h 417 end 418 419 -- x > 0 scrolls right x < 0 scrolls left 420 local function set_column(x) 421 local o = get_settings() 422 if o.ovfl ~= "manual" then return end -- no buffer stored to scroll 423 rb.lcd_scroll_stop() 424 425 local res, w, h, str, line 426 427 for key, value in pairs(col_buf) do 428 line = key - 1 -- rb is 0-based lua is 1-based 429 o.line = key 430 431 if value then 432 show_selected(key, value) 433 res, w, h = text_extent(value, o.font) 434 clear_line(o, line, h) 435 436 print_internal(o, x + o.col, w, h, value) 437 update_line(o.autoupdate, o, line, h) 438 end 439 end 440 o = _NIL 441 end 442 443 --expose functions to the outside through _print table 444 _print.opt = {} 445 _print.opt.column = set_column 446 _print.opt.color = set_color 447 _print.opt.area = set_area 448 _print.opt.set = set_settings 449 _print.opt.get = get_settings 450 _print.opt.defaults = set_defaults 451 _print.opt.overflow = set_overflow 452 _print.opt.justify = set_justify 453 _print.opt.sel_line = select_line 454 _print.opt.line = set_line 455 _print.opt.linedesc = set_linedesc 456 _print.opt.autoupdate = set_update 457 _print.selected = function() return s_lines end 458 _print.clear = clear 459 _print.f = printf 460 461end --_print functions 462 463return _print 464