A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Copyright (C) 2010 Teruaki Kawashima
11*
12* This program is free software; you can redistribute it and/or
13* modify it under the terms of the GNU General Public License
14* as published by the Free Software Foundation; either version 2
15* of the License, or (at your option) any later version.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#include "config.h"
23#include "plugin.h"
24#include "pluginlib_actions.h"
25#include "simple_viewer.h"
26#include <ctype.h>
27
28
29struct view_info {
30 struct font* pf;
31 struct viewport scrollbar_vp; /* viewport for scrollbar */
32 struct viewport vp;
33 const char *title;
34 const char *text; /* displayed text */
35 int display_lines; /* number of lines can be displayed */
36 int line_count; /* number of lines */
37 int line; /* current first line */
38 int start; /* possition of first line in text */
39};
40
41static bool isbrchr(const unsigned char *str, int len)
42{
43 const unsigned char *p = "!,-.:;? 、。!,.:;?―";
44 if (isspace(*str))
45 return true;
46
47 while(*p)
48 {
49 int n = rb->utf8seek(p, 1);
50 if (len == n && !rb->strncmp(p, str, len))
51 return true;
52 p += n;
53 }
54 return false;
55}
56
57static const char* get_next_line(const char *text, struct view_info *info)
58{
59 const char *ptr = text;
60 const char *space = NULL;
61 int total, n, w;
62 total = 0;
63 while(*ptr)
64 {
65 ucschar_t ch;
66 n = ((intptr_t)rb->utf8decode(ptr, &ch) - (intptr_t)ptr);
67 if (rb->is_diacritic(ch, NULL))
68 w = 0;
69 else
70 w = rb->font_get_width(info->pf, ch);
71 if (isbrchr(ptr, n))
72 space = ptr+(isspace(*ptr) || total + w <= info->vp.width? n: 0);
73 if (*ptr == '\n')
74 {
75 ptr += n;
76 break;
77 }
78 if (total + w > info->vp.width)
79 break;
80 ptr += n;
81 total += w;
82 }
83 return *ptr && space? space: ptr;
84}
85
86static void calc_line_count(struct view_info *info)
87{
88 const char *ptr = info->text;
89 int i = 0;
90 bool scrollbar = false;
91
92 while (*ptr)
93 {
94 ptr = get_next_line(ptr, info);
95 i++;
96 if (!scrollbar && i > info->display_lines)
97 {
98 ptr = info->text;
99 i = 0;
100 info->scrollbar_vp = info->vp;
101 info->scrollbar_vp.width = rb->global_settings->scrollbar_width;
102 info->vp.width -= info->scrollbar_vp.width;
103 if (rb->global_settings->scrollbar != SCROLLBAR_RIGHT)
104 info->vp.x = info->scrollbar_vp.width;
105 else
106 info->scrollbar_vp.x = info->vp.width;
107 scrollbar = true;
108 }
109 }
110 info->line_count = i;
111}
112
113static void calc_first_line(struct view_info *info, int line)
114{
115 const char *ptr = info->text;
116 int i = 0;
117
118 if (line > info->line_count - info->display_lines)
119 line = info->line_count - info->display_lines;
120 if (line < 0)
121 line = 0;
122
123 if (info->line <= line)
124 {
125 ptr += info->start;
126 i = info->line;
127 }
128 while (*ptr && i < line)
129 {
130 ptr = get_next_line(ptr, info);
131 i++;
132 }
133 info->start = ptr - info->text;
134 info->line = i;
135}
136
137static int init_view(struct view_info *info,
138 const char *title, const char *text)
139{
140 rb->viewport_set_defaults(&info->vp, SCREEN_MAIN);
141 info->pf = rb->font_get(rb->screens[SCREEN_MAIN]->getuifont());
142 info->display_lines = info->vp.height / info->pf->height;
143
144 info->title = title;
145 info->text = text;
146 info->line_count = 0;
147 info->line = 0;
148 info->start = 0;
149
150 /* no title for small screens. */
151 if (info->display_lines < 4)
152 {
153 info->title = NULL;
154 }
155 else
156 {
157 info->display_lines--;
158 info->vp.y += info->pf->height;
159 info->vp.height -= info->pf->height;
160 }
161
162 calc_line_count(info);
163 return 0;
164}
165
166static void draw_text(struct view_info *info)
167{
168#define OUTPUT_SIZE LCD_WIDTH+1
169 static char output[OUTPUT_SIZE];
170 const char *text, *ptr;
171 int max_show, line;
172 struct screen* display = rb->screens[SCREEN_MAIN];
173
174 /* clear screen */
175 display->clear_display();
176
177 /* display title. */
178 if(info->title)
179 {
180 display->set_viewport(NULL);
181 display->puts(0, 0, info->title);
182 }
183
184 max_show = MIN(info->line_count - info->line, info->display_lines);
185 text = info->text + info->start;
186
187 display->set_viewport(&info->vp);
188 for (line = 0; line < max_show; line++)
189 {
190 int len;
191 ptr = get_next_line(text, info);
192 len = ptr-text;
193 while(len > 0 && isspace(text[len-1]))
194 len--;
195 rb->memcpy(output, text, len);
196 output[len] = 0;
197 display->puts(0, line, output);
198 text = ptr;
199 }
200 if (info->line_count > info->display_lines)
201 {
202 display->set_viewport(&info->scrollbar_vp);
203 rb->gui_scrollbar_draw(display, (info->scrollbar_vp.width? 0: 1), 0,
204 info->scrollbar_vp.width - 1, info->scrollbar_vp.height,
205 info->line_count, info->line, info->line + max_show,
206 VERTICAL);
207 }
208
209 display->set_viewport(NULL);
210 display->update();
211}
212
213static void scroll_up(struct view_info *info, int n)
214{
215 if (info->line <= 0)
216 return;
217
218 calc_first_line(info, info->line-n);
219 draw_text(info);
220 rb->yield();
221}
222
223static void scroll_down(struct view_info *info, int n)
224{
225 if (info->line + info->display_lines >= info->line_count)
226 return;
227
228 calc_first_line(info, info->line+n);
229 draw_text(info);
230 rb->yield();
231}
232
233static void scroll_to_top(struct view_info *info)
234{
235 if (info->line <= 0)
236 return;
237
238 calc_first_line(info, 0);
239 draw_text(info);
240}
241
242static void scroll_to_bottom(struct view_info *info)
243{
244 if (info->line + info->display_lines >= info->line_count)
245 return;
246
247 calc_first_line(info, info->line_count - info->display_lines);
248 draw_text(info);
249}
250
251int view_text(const char *title, const char *text)
252{
253 struct view_info info;
254 const struct button_mapping *view_contexts[] = {
255 pla_main_ctx,
256 };
257 int button;
258
259 init_view(&info, title, text);
260 draw_text(&info);
261
262 /* wait for keypress */
263 while(1)
264 {
265 button = pluginlib_getaction(TIMEOUT_BLOCK, view_contexts,
266 ARRAYLEN(view_contexts));
267 switch (button)
268 {
269 case PLA_UP:
270#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
271 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
272 || (CONFIG_KEYPAD == IPOD_4G_PAD)
273 return PLUGIN_OK;
274#endif
275 case PLA_UP_REPEAT:
276#ifdef HAVE_SCROLLWHEEL
277 case PLA_SCROLL_BACK:
278 case PLA_SCROLL_BACK_REPEAT:
279#endif
280 scroll_up(&info, 1);
281 break;
282 case PLA_DOWN:
283 case PLA_DOWN_REPEAT:
284#ifdef HAVE_SCROLLWHEEL
285 case PLA_SCROLL_FWD:
286 case PLA_SCROLL_FWD_REPEAT:
287#endif
288 scroll_down(&info, 1);
289 break;
290 case PLA_LEFT:
291#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
292 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
293 || (CONFIG_KEYPAD == IPOD_4G_PAD)
294 return PLUGIN_OK;
295#endif
296 scroll_up(&info, info.display_lines);
297 break;
298 case PLA_RIGHT:
299 scroll_down(&info, info.display_lines);
300 break;
301 case PLA_LEFT_REPEAT:
302 scroll_to_top(&info);
303 break;
304 case PLA_RIGHT_REPEAT:
305 scroll_to_bottom(&info);
306 break;
307 case PLA_SELECT:
308 case PLA_EXIT:
309 case PLA_CANCEL:
310 return PLUGIN_OK;
311 default:
312 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
313 return PLUGIN_USB_CONNECTED;
314 break;
315 }
316 }
317
318 return PLUGIN_OK;
319}