summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/SUBDIRS1
-rw-r--r--apps/plugins/SUBDIRS.app_build1
-rw-r--r--apps/plugins/tagcache/SOURCES2
-rw-r--r--apps/plugins/tagcache/tagcache.c581
-rw-r--r--apps/plugins/tagcache/tagcache.h25
-rw-r--r--apps/plugins/tagcache/tagcache.make30
8 files changed, 642 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 9ec38ab76b..49016fef13 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -22,6 +22,7 @@ clock,apps
22codebuster,games 22codebuster,games
23credits,viewers 23credits,viewers
24cube,demos 24cube,demos
25db_commit,apps
25db_folder_select,viewers 26db_folder_select,viewers
26demystify,demos 27demystify,demos
27dice,games 28dice,games
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 7edc8a3c51..b857f26ba5 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -4,6 +4,7 @@ battery_bench.c
4#endif 4#endif
5#ifdef HAVE_TAGCACHE 5#ifdef HAVE_TAGCACHE
6db_folder_select.c 6db_folder_select.c
7tagcache/tagcache.c
7#endif 8#endif
8chessclock.c 9chessclock.c
9credits.c 10credits.c
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 4cb57edb1b..b9c18b5fd9 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -31,6 +31,7 @@ rockboy
31 31
32#if defined(HAVE_TAGCACHE) 32#if defined(HAVE_TAGCACHE)
33pictureflow 33pictureflow
34tagcache
34#endif 35#endif
35 36
36#if PLUGIN_BUFFER_SIZE > 0x20000 37#if PLUGIN_BUFFER_SIZE > 0x20000
diff --git a/apps/plugins/SUBDIRS.app_build b/apps/plugins/SUBDIRS.app_build
index 1fc283d6c4..fbf83f01da 100644
--- a/apps/plugins/SUBDIRS.app_build
+++ b/apps/plugins/SUBDIRS.app_build
@@ -17,6 +17,7 @@ reversi
17 17
18#ifdef HAVE_TAGCACHE 18#ifdef HAVE_TAGCACHE
19pictureflow 19pictureflow
20tagcache
20#endif 21#endif
21 22
22/* For all the swcodec targets */ 23/* For all the swcodec targets */
diff --git a/apps/plugins/tagcache/SOURCES b/apps/plugins/tagcache/SOURCES
new file mode 100644
index 0000000000..2541f3e87c
--- /dev/null
+++ b/apps/plugins/tagcache/SOURCES
@@ -0,0 +1,2 @@
1tagcache.c
2
diff --git a/apps/plugins/tagcache/tagcache.c b/apps/plugins/tagcache/tagcache.c
new file mode 100644
index 0000000000..25ba9bc301
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.c
@@ -0,0 +1,581 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2023 by William Wilgus
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/*Plugin Includes*/
23
24#include "plugin.h"
25/* Redefinitons of ANSI C functions. */
26#include "lib/wrappers.h"
27#include "lib/helper.h"
28
29static void thread_create(void);
30static void thread(void); /* the thread running it all */
31static void allocate_tempbuf(void);
32static void free_tempbuf(void);
33static bool do_timed_yield(void);
34static void _log(const char *fmt, ...);
35/*Aliases*/
36#if 0
37#ifdef ROCKBOX_HAS_LOGF
38 #define logf rb->logf
39#else
40 #define logf(...) {}
41#endif
42#endif
43
44
45#define logf _log
46#define sleep rb->sleep
47#define qsort rb->qsort
48
49#define write(x,y,z) rb->write(x,y,z)
50#define ftruncate rb->ftruncate
51#define remove rb->remove
52
53#define vsnprintf rb->vsnprintf
54#define mkdir rb->mkdir
55#define filesize rb->filesize
56
57#define strtok_r rb->strtok_r
58#define strncasecmp rb->strncasecmp
59#define strcasecmp rb->strcasecmp
60
61#define current_tick (*rb->current_tick)
62#define crc_32(x,y,z) rb->crc_32(x,y,z)
63
64
65#ifndef SIMULATOR
66#define errno (*rb->__errno())
67#endif
68#define ENOENT 2 /* No such file or directory */
69#define EEXIST 17 /* File exists */
70#define MAX_LOG_SIZE 16384
71
72#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
73#define EV_ACTION MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x02)
74#define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01)
75
76/* communication to the worker thread */
77static struct
78{
79 int user_index;
80 bool exiting; /* signal to the thread that we want to exit */
81 bool resume;
82 unsigned int id; /* worker thread id */
83 struct event_queue queue; /* thread event queue */
84 long last_useraction_tick;
85} gThread;
86
87
88/*Support Fns*/
89/* open but with a builtin printf for assembling the path */
90int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...)
91{
92 va_list ap;
93 va_start(ap, pathfmt);
94 vsnprintf(buf, size, pathfmt, ap);
95 va_end(ap);
96 if ((oflag & O_PATH) == O_PATH)
97 return -1;
98 int handle = open(buf, oflag, 0666);
99 //logf("Open: %s %d flag: %x", buf, handle, oflag);
100 return handle;
101}
102
103static void sleep_yield(void)
104{
105 rb->queue_remove_from_head(&gThread.queue, EV_ACTION);
106 rb->queue_post(&gThread.queue, EV_ACTION, 0);
107 sleep(1);
108 #undef yield
109 rb->yield();
110 #define yield sleep_yield
111}
112
113/* make sure tag can be displayed by font pf*/
114static bool text_is_displayable(struct font *pf, unsigned char *src)
115{
116 unsigned short code;
117 const unsigned char *ptr = src;
118 while(*ptr)
119 {
120 ptr = rb->utf8decode(ptr, &code);
121
122 if(!rb->font_get_bits(pf, code))
123 {
124 return false;
125 }
126 }
127 return true;
128}
129
130/* callback for each tag if false returned tag will not be added */
131bool user_check_tag(int index_type, char* build_idx_buf)
132{
133 static struct font *pf = NULL;
134 if (!pf)
135 pf = rb->font_get(FONT_UI);
136
137 if (index_type == tag_artist || index_type == tag_album ||
138 index_type == tag_genre || index_type == tag_title ||
139 index_type == tag_composer || index_type == tag_comment ||
140 index_type == tag_albumartist || index_type == tag_virt_canonicalartist)
141 {
142 /* this could be expanded with more rules / transformations */
143 if (rb->utf8length(build_idx_buf) != strlen(build_idx_buf))
144 {
145 if (!text_is_displayable(pf, build_idx_buf))
146 {
147 logf("Can't display (%d) %s", index_type, build_idx_buf);
148 }
149 }
150 }
151 return true;
152}
153
154/* undef features we don't need */
155#undef HAVE_DIRCACHE
156#undef HAVE_TC_RAMCACHE
157#undef HAVE_EEPROM_SETTINGS
158/* paste the whole tagcache.c file */
159#include "../tagcache.c"
160
161static bool logdump(bool append);
162static unsigned char logbuffer[MAX_LOG_SIZE + 1];
163static int logindex;
164static bool logwrap;
165static bool logenabled = true;
166
167static void check_logindex(void)
168{
169 if(logindex >= MAX_LOG_SIZE)
170 {
171 /* wrap */
172 logdump(true);
173 //logwrap = true;
174 logindex = 0;
175 }
176}
177
178static int log_push(void *userp, int c)
179{
180 (void)userp;
181
182 logbuffer[logindex++] = c;
183 check_logindex();
184
185 return 1;
186}
187
188/* our logf function */
189static void _log(const char *fmt, ...)
190{
191 if (!logenabled)
192 {
193 rb->splash(10, "log not enabled");
194 return;
195 }
196
197
198 #ifdef USB_ENABLE_SERIAL
199 int old_logindex = logindex;
200 #endif
201 va_list ap;
202
203 va_start(ap, fmt);
204
205#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
206 char buf[1024];
207 vsnprintf(buf, sizeof buf, fmt, ap);
208 DEBUGF("%s\n", buf);
209 /* restart va_list otherwise the result if undefined when vuprintf is called */
210 va_end(ap);
211 va_start(ap, fmt);
212#endif
213
214 rb->vuprintf(log_push, NULL, fmt, ap);
215 va_end(ap);
216
217 /* add trailing zero */
218 log_push(NULL, '\0');
219}
220
221
222int compute_nb_lines(int w, struct font* font)
223{
224 int i, nb_lines;
225 int cur_x, delta_x;
226
227 if(logindex>= MAX_LOG_SIZE || (logindex == 0 && !logwrap))
228 return 0;
229
230 if(logwrap)
231 i = logindex;
232 else
233 i = 0;
234
235 cur_x = 0;
236 nb_lines = 0;
237
238 do {
239 if(logbuffer[i] == '\0')
240 {
241 cur_x = 0;
242 nb_lines++;
243 }
244 else
245 {
246 /* does character fit on this line ? */
247 delta_x = rb->font_get_width(font, logbuffer[i]);
248
249 if(cur_x + delta_x > w)
250 {
251 cur_x = 0;
252 nb_lines++;
253 }
254
255 /* update pointer */
256 cur_x += delta_x;
257 }
258
259 i++;
260 if(i >= MAX_LOG_SIZE)
261 i = 0;
262 } while(i != logindex);
263
264 return nb_lines;
265}
266
267static bool logdisplay(void)
268{
269
270 int w, h, i, index;
271 int fontnr;
272 static int delta_y = -1;
273 int cur_x, cur_y, delta_x;
274 struct font* font;
275
276 char buf[2];
277
278 fontnr = FONT_FIRSTUSERFONT;
279 font = rb->font_get(fontnr);
280 buf[1] = '\0';
281 w = LCD_WIDTH;
282 h = LCD_HEIGHT;
283
284 if (delta_y < 0) /* init, get the horizontal size of each line */
285 {
286 rb->font_getstringsize("A", NULL, &delta_y, fontnr);
287 /* start at the end of the log */
288 gThread.user_index = compute_nb_lines(w, font) - h/delta_y -1;
289 /* user_index will be number of the first line to display (warning: line!=log entry) */
290 /* if negative, will be set 0 to zero later */
291 }
292
293
294 rb->lcd_clear_display();
295
296 if(gThread.user_index < 0 || gThread.user_index >= MAX_LOG_SIZE)
297 gThread.user_index = 0;
298
299
300 if(logwrap)
301 i = logindex;
302 else
303 i = 0;
304
305 index = 0;
306 cur_x = 0;
307 cur_y = 0;
308
309 /* nothing to print ? */
310 if(logindex == 0 && !logwrap)
311 goto end_print;
312
313 do {
314 if(logbuffer[i] == '\0')
315 {
316 /* should be display a newline ? */
317 if(index >= gThread.user_index)
318 cur_y += delta_y;
319 cur_x = 0;
320 index++;
321 }
322 else
323 {
324 /* does character fit on this line ? */
325 delta_x = rb->font_get_width(font, logbuffer[i]);
326
327 if(cur_x + delta_x > w)
328 {
329 /* should be display a newline ? */
330 if(index >= gThread.user_index)
331 cur_y += delta_y;
332 cur_x = 0;
333 index++;
334 }
335
336 /* should we print character ? */
337 if(index >= gThread.user_index)
338 {
339 buf[0] = logbuffer[i];
340 rb->lcd_putsxy(cur_x, cur_y, buf);
341 }
342
343 /* update pointer */
344 cur_x += delta_x;
345 }
346 i++;
347 /* did we fill the screen ? */
348 if(cur_y > h - delta_y)
349 {
350 if (TIME_AFTER(current_tick, gThread.last_useraction_tick + HZ))
351 gThread.user_index++;
352 break;
353 }
354
355 if(i >= MAX_LOG_SIZE)
356 i = 0;
357 } while(i != logindex);
358
359 end_print:
360 rb->lcd_update();
361
362 return false;
363}
364
365static bool logdump(bool append)
366{
367 int fd;
368 int flags = O_CREAT|O_WRONLY|O_TRUNC;
369 /* nothing to print ? */
370 if(!logenabled || (logindex == 0 && !logwrap))
371 {
372 /* nothing is logged just yet */
373 return false;
374 }
375 if (!append)
376 {
377 flags = O_CREAT|O_WRONLY|O_APPEND;
378 }
379
380 fd = open(ROCKBOX_DIR "/db_commit_log.txt", flags, 0666);
381 logenabled = false;
382 if(-1 != fd) {
383 int i;
384
385 if(logwrap)
386 i = logindex;
387 else
388 i = 0;
389
390 do {
391 if(logbuffer[i]=='\0')
392 rb->fdprintf(fd, "\n");
393 else
394 rb->fdprintf(fd, "%c", logbuffer[i]);
395
396 i++;
397 if(i >= MAX_LOG_SIZE)
398 i = 0;
399 } while(i != logindex);
400
401 close(fd);
402 }
403
404 logenabled = true;
405
406 return false;
407}
408
409static void allocate_tempbuf(void)
410{
411 tempbuf_size = 0;
412 tempbuf = rb->plugin_get_audio_buffer(&tempbuf_size);
413 tempbuf_size &= ~0x03;
414
415}
416
417static void free_tempbuf(void)
418{
419 if (tempbuf_size == 0)
420 return ;
421
422 rb->plugin_release_audio_buffer();
423 tempbuf = NULL;
424 tempbuf_size = 0;
425}
426
427static bool do_timed_yield(void)
428{
429 /* Sorting can lock up for quite a while, so yield occasionally */
430 static long wakeup_tick = 0;
431 if (TIME_AFTER(current_tick, wakeup_tick))
432 {
433
434 yield();
435
436 wakeup_tick = current_tick + (HZ/5);
437 return true;
438 }
439 return false;
440}
441
442/*-----------------------------------------------------------------------------*/
443/******* plugin_start ******* */
444/*-----------------------------------------------------------------------------*/
445
446enum plugin_status plugin_start(const void* parameter)
447{
448 (void) parameter;
449
450 /* Turn off backlight timeout */
451 backlight_ignore_timeout();
452
453 memset(&tc_stat, 0, sizeof(struct tagcache_stat));
454 memset(&current_tcmh, 0, sizeof(struct master_header));
455 filenametag_fd = -1;
456
457 strlcpy(tc_stat.db_path, rb->global_settings->tagcache_db_path, sizeof(tc_stat.db_path));
458 if (!rb->dir_exists(tc_stat.db_path)) /* on fail use default DB path */
459 strlcpy(tc_stat.db_path, ROCKBOX_DIR, sizeof(tc_stat.db_path));
460 tc_stat.initialized = true;
461
462 memset(&gThread, 0, sizeof(gThread));
463 logf("started");
464 logdump(false);
465
466 thread_create();
467 gThread.user_index = 0;
468 logdisplay(); /* get something on the screen while user waits */
469
470 allocate_tempbuf();
471
472 commit();
473
474 if (tc_stat.ready)
475 rb->tagcache_commit_finalize();
476
477 free_tempbuf();
478
479 logdump(true);
480 gThread.user_index++;
481 logdisplay();
482 rb->thread_wait(gThread.id);
483 rb->queue_delete(&gThread.queue);
484
485 /* Turn on backlight timeout (revert to settings) */
486 backlight_use_settings();
487 return PLUGIN_OK;
488}
489
490/****************** main thread + helper ******************/
491static void thread(void)
492{
493 struct queue_event ev;
494 while (!gThread.exiting)
495 {
496 rb->queue_wait_w_tmo(&gThread.queue, &ev, 1);
497 switch (ev.id)
498 {
499 case SYS_USB_CONNECTED:
500 rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
501 logenabled = false;
502 break;
503 case SYS_USB_DISCONNECTED:
504 logenabled = true;
505 /*fall through*/
506 case EV_STARTUP:
507 logf("Thread Started");
508 break;
509 case EV_EXIT:
510 return;
511 default:
512 break;
513 }
514 logdisplay();
515
516 int action = rb->get_action(CONTEXT_STD, HZ/10);
517
518 switch( action )
519 {
520 case ACTION_NONE:
521 break;
522 case ACTION_STD_NEXT:
523 case ACTION_STD_NEXTREPEAT:
524 gThread.last_useraction_tick = current_tick;
525 gThread.user_index++;
526 break;
527 case ACTION_STD_PREV:
528 case ACTION_STD_PREVREPEAT:
529 gThread.last_useraction_tick = current_tick;
530 gThread.user_index--;
531 break;
532 case ACTION_STD_OK:
533 gThread.last_useraction_tick = current_tick;
534 gThread.user_index = 0;
535 break;
536 case SYS_POWEROFF:
537 case ACTION_STD_CANCEL:
538 gThread.exiting = true;
539 return;
540#ifdef HAVE_TOUCHSCREEN
541 case ACTION_TOUCHSCREEN:
542 {
543 gThread.last_useraction_tick = current_tick;
544 short x, y;
545 static int prev_y;
546
547 action = action_get_touchscreen_press(&x, &y);
548
549 if(action & BUTTON_REL)
550 prev_y = 0;
551 else
552 {
553 if(prev_y != 0)
554 gThread.user_index += (prev_y - y) / delta_y;
555
556 prev_y = y;
557 }
558 }
559#endif
560 default:
561 break;
562 }
563
564
565 yield();
566 }
567}
568
569static void thread_create(void)
570{
571 /* put the thread's queue in the bcast list */
572 rb->queue_init(&gThread.queue, true);
573 /*Note: tagcache_stack is defined in apps/tagcache.c */
574 gThread.last_useraction_tick = current_tick;
575 gThread.id = rb->create_thread(thread, tagcache_stack, sizeof(tagcache_stack),
576 0, "db_commit"
577 IF_PRIO(, PRIORITY_USER_INTERFACE)
578 IF_COP(, CPU));
579 rb->queue_post(&gThread.queue, EV_STARTUP, 0);
580 yield();
581}
diff --git a/apps/plugins/tagcache/tagcache.h b/apps/plugins/tagcache/tagcache.h
new file mode 100644
index 0000000000..e416289741
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.h
@@ -0,0 +1,25 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C)
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#ifndef _TC_PLUGIN_
22#define _TC_PLUGIN_
23#endif /*_TC_PLUGIN_ */
24
25
diff --git a/apps/plugins/tagcache/tagcache.make b/apps/plugins/tagcache/tagcache.make
new file mode 100644
index 0000000000..5d6d65cb0e
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.make
@@ -0,0 +1,30 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10TCPLUG_SRCDIR := $(APPSDIR)/plugins/tagcache
11TCPLUG_BUILDDIR := $(BUILDDIR)/apps/plugins/tagcache
12
13ROCKS += $(TCPLUG_BUILDDIR)/db_commit.rock
14
15TCPLUG_FLAGS = $(PLUGINFLAGS) -fno-strict-aliasing -Wno-unused \
16 -I$(TCPLUG_SRCDIR) -ffunction-sections \
17 -fdata-sections -Wl,--gc-sections
18TCPLUG_SRC := $(call preprocess, $(TCPLUG_SRCDIR)/SOURCES)
19TCPLUG_OBJ := $(call c2obj, $(TCPLUG_SRC))
20
21# add source files to OTHER_SRC to get automatic dependencies
22OTHER_SRC += $(APPSDIR)/tagcache.c $(TCPLUG_SRC)
23
24$(TCPLUG_BUILDDIR)/db_commit.rock: $(TCPLUG_OBJ)
25
26# special pattern rule for compiling with extra flags
27$(TCPLUG_BUILDDIR)/%.o: $(TCPLUG_SRCDIR)/%.c $(TCPLUG_SRCDIR)/tagcache.make
28 $(SILENT)mkdir -p $(dir $@)
29 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(TCPLUG_FLAGS) -c $< -o $@
30