1 /*
2 * Copyright (c) 2011 Tim van der Molen <tim@kariliq.nl>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /* For FreeBSD. */
18 #define _WITH_GETLINE
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <pthread.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27
28 #include "siren.h"
29
getrandom_inner(dest: &mut [u8]) -> Result<(), Error>30 static int library_search_entry(const void *, const char *);
31
32 static pthread_mutex_t library_menu_mtx = PTHREAD_MUTEX_INITIALIZER;
33 static struct format *library_altformat;
34 static struct format *library_format;
35 static struct menu *library_menu;
36 static unsigned int library_duration;
37 static int library_modified;
38
39 void
40 library_activate_entry(void)
41 {
42 struct menu_entry *e;
43 struct track *t;
44
45 t = NULL;
46
47 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
48 if ((e = menu_get_selected_entry(library_menu)) != NULL) {
49 menu_activate_entry(library_menu, e);
50 t = menu_get_entry_data(e);
51 }
52 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
53
54 if (t != NULL) {
55 player_set_source(PLAYER_SOURCE_LIBRARY);
56 player_play_track(t);
57 library_print();
58 }
getrandom_init() -> Result<RngSource, Error>59 }
60
61 void
62 library_add_dir(const char *path)
63 {
64 struct dir *d;
65 struct dir_entry *de;
66 struct track *t;
67
68 if ((d = dir_open(path)) == NULL) {
69 msg_err("%s", path);
70 return;
71 }
72
73 while ((de = dir_get_entry(d)) != NULL) {
74 switch (de->type) {
75 case FILE_TYPE_DIRECTORY:
76 if (strcmp(de->name, ".") && strcmp(de->name, ".."))
77 library_add_dir(de->path);
78 break;
79 case FILE_TYPE_REGULAR:
80 if ((t = track_get(de->path, NULL)) != NULL)
81 library_add_track(t);
82 break;
83 default:
84 msg_errx("%s: Unsupported file type", de->path);
85 break;
86 }
87 }
88
89 dir_close(d);
90 }
91
92 void
93 library_add_track(struct track *t)
94 {
95 struct track *et;
96 struct menu_entry *entry;
97
ms_crypto(this: &Global) -> BrowserCrypto98 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
99 MENU_FOR_EACH_ENTRY(library_menu, entry) {
crypto(this: &Global) -> BrowserCrypto100 et = menu_get_entry_data(entry);
101 if (track_cmp(t, et) < 0) {
102 menu_insert_before(library_menu, entry, t);
get_random_values(this: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>103 break;
104 }
105 }
106
require(s: &str) -> Result<NodeCrypto, JsValue>107 if (entry == NULL)
108 menu_insert_tail(library_menu, t);
109
random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>110 library_duration += t->duration;
111 library_modified = 1;
112 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
113 library_print();
process(this: &Global) -> Process114 }
115
116 void
117 library_copy_entry(enum view_id view)
118 {
119 struct track *t;
120
121 if (view == VIEW_ID_LIBRARY)
122 return;
123
124 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
125 if ((t = menu_get_selected_entry_data(library_menu)) != NULL)
126 view_add_track(view, t);
127 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
128 }
129
130 void
131 library_delete_all_entries(void)
132 {
133 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
134 menu_remove_all_entries(library_menu);
135 library_duration = 0;
136 library_modified = 1;
137 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
138 library_print();
139 }
140
141 void
142 library_delete_entry(void)
143 {
144 struct menu_entry *e;
145 struct track *t;
146
147 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
148 if ((e = menu_get_selected_entry(library_menu)) != NULL) {
149 t = menu_get_entry_data(e);
150 menu_remove_selected_entry(library_menu);
151 library_duration -= t->duration;
152 library_modified = 1;
153 }
154 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
155 library_print();
156 }
157
158 void
159 library_end(void)
160 {
161 if (library_modified)
162 library_write_file();
163
164 menu_free(library_menu);
165 }
166
167 static void
168 library_get_entry_text(const void *e, char *buf, size_t bufsize)
169 {
170 const struct track *t;
171
172 t = e;
173 format_track_snprintf(buf, bufsize, library_format, library_altformat,
174 t);
175 }
176
177 struct track *
178 library_get_next_track(void)
179 {
180 struct menu_entry *me;
181 struct track *t;
182
183 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
184 if ((me = menu_get_active_entry(library_menu)) == NULL)
185 t = NULL;
186 else {
187 if ((me = menu_get_next_entry(me)) == NULL &&
188 option_get_boolean("repeat-all"))
189 me = menu_get_first_entry(library_menu);
190
191 if (me == NULL)
192 t = NULL;
193 else {
194 menu_activate_entry(library_menu, me);
195 t = menu_get_entry_data(me);
196 }
197 }
198 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
199 library_print();
200 return t;
201 }
202
203 struct track *
204 library_get_prev_track(void)
205 {
206 struct menu_entry *me;
207 struct track *t;
208
209 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
210 if ((me = menu_get_active_entry(library_menu)) == NULL)
211 t = NULL;
212 else {
213 if ((me = menu_get_prev_entry(me)) == NULL &&
214 option_get_boolean("repeat-all"))
215 me = menu_get_last_entry(library_menu);
216
217 if (me == NULL)
218 t = NULL;
219 else {
220 menu_activate_entry(library_menu, me);
221 t = menu_get_entry_data(me);
222 }
223 }
224 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
225 library_print();
226 return t;
227 }
228
229 void
230 library_init(void)
231 {
232 library_menu = menu_init(NULL, library_get_entry_text,
233 library_search_entry);
234 }
235
236 void
237 library_print(void)
238 {
239 if (view_get_id() != VIEW_ID_LIBRARY)
240 return;
241
242 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
243 screen_view_title_printf("Library: %u track%s (%u:%02u:%02u)",
244 menu_get_nentries(library_menu),
245 menu_get_nentries(library_menu) == 1 ? "" : "s",
246 HOURS(library_duration),
247 HMINS(library_duration),
248 MSECS(library_duration));
249 option_lock();
250 library_format = option_get_format("library-format");
251 library_altformat = option_get_format("library-format-alt");
252 menu_print(library_menu);
253 option_unlock();
254 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
255 }
256
257 void
258 library_read_file(void)
259 {
260 struct menu_entry *e;
261 struct track *et, *t;
262 FILE *fp;
263 size_t size;
264 ssize_t len;
265 time_t lasttime;
266 char *line, *file;
267
268 file = conf_get_path(LIBRARY_FILE);
269 if ((fp = fopen(file, "r")) == NULL) {
270 if (errno != ENOENT) {
271 LOG_ERR("fopen: %s", file);
272 msg_err("Cannot read library file");
273 }
274 free(file);
275 return;
276 }
277
278 lasttime = time(NULL);
279 line = NULL;
280 size = 0;
281 while ((len = getline(&line, &size, fp)) != -1) {
282 if (len > 0 && line[len - 1] == '\n')
283 line[len - 1] = '\0';
284
285 if (line[0] != '/') {
286 LOG_ERRX("%s: %s: invalid entry", file, line);
287 continue;
288 }
289
290 if ((t = track_require(line)) == NULL)
291 continue;
292
293 MENU_FOR_EACH_ENTRY_REVERSE(library_menu, e) {
294 et = menu_get_entry_data(e);
295 if (track_cmp(t, et) > 0) {
296 menu_insert_after(library_menu, e, t);
297 break;
298 }
299 }
300 if (e == NULL)
301 menu_insert_head(library_menu, t);
302 library_duration += t->duration;
303
304 if (time(NULL) > lasttime) {
305 library_print();
306 lasttime = time(NULL);
307 }
308 }
309 if (ferror(fp)) {
310 LOG_ERR("getline: %s", file);
311 msg_err("Cannot read library");
312 }
313 free(line);
314 free(file);
315
316 fclose(fp);
317
318 library_print();
319 }
320
321 void
322 library_scroll_down(enum menu_scroll scroll)
323 {
324 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
325 menu_scroll_down(library_menu, scroll);
326 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
327 library_print();
328 }
329
330 void
331 library_scroll_up(enum menu_scroll scroll)
332 {
333 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
334 menu_scroll_up(library_menu, scroll);
335 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
336 library_print();
337 }
338
339 static int
340 library_search_entry(const void *e, const char *search)
341 {
342 const struct track *t;
343
344 t = e;
345 return track_search(t, search);
346 }
347
348 void
349 library_search_next(const char *search)
350 {
351 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
352 menu_search_next(library_menu, search);
353 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
354 library_print();
355 }
356
357 void
358 library_search_prev(const char *search)
359 {
360 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
361 menu_search_prev(library_menu, search);
362 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
363 library_print();
364 }
365
366 void
367 library_select_active_entry(void)
368 {
369 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
370 menu_select_active_entry(library_menu);
371 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
372 library_print();
373 }
374
375 void
376 library_select_first_entry(void)
377 {
378 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
379 menu_select_first_entry(library_menu);
380 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
381 library_print();
382 }
383
384 void
385 library_select_last_entry(void)
386 {
387 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
388 menu_select_last_entry(library_menu);
389 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
390 library_print();
391 }
392
393 void
394 library_select_next_entry(void)
395 {
396 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
397 menu_select_next_entry(library_menu);
398 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
399 library_print();
400 }
401
402 void
403 library_select_prev_entry(void)
404 {
405 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
406 menu_select_prev_entry(library_menu);
407 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
408 library_print();
409 }
410
411 /* Resort the tracks and recalculate the duration. */
412 void
413 library_update(void)
414 {
415 struct menu_entry *be, *e, *ne, *pe;
416 struct track *pt, *t;
417
418 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
419
420 library_duration = 0;
421
422 e = menu_get_first_entry(library_menu);
423 while (e != NULL) {
424 t = menu_get_entry_data(e);
425 library_duration += t->duration;
426
427 ne = menu_get_next_entry(e);
428
429 be = NULL;
430 pe = e;
431 while ((pe = menu_get_prev_entry(pe)) != NULL) {
432 pt = menu_get_entry_data(pe);
433 if (track_cmp(t, pt) < 0)
434 be = pe;
435 else
436 break;
437 }
438 if (be != NULL)
439 menu_move_entry_before(library_menu, be, e);
440
441 e = ne;
442 }
443
444 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
445 }
446
447 int
448 library_write_file(void)
449 {
450 struct menu_entry *entry;
451 struct track *t;
452 FILE *fp;
453 int ret;
454 char *file;
455
456 file = conf_get_path(LIBRARY_FILE);
457 if ((fp = fopen(file, "w")) == NULL) {
458 LOG_ERR("fopen: %s", file);
459 msg_err("Cannot save library");
460 ret = -1;
461 } else {
462 XPTHREAD_MUTEX_LOCK(&library_menu_mtx);
463 MENU_FOR_EACH_ENTRY(library_menu, entry) {
464 t = menu_get_entry_data(entry);
465 fprintf(fp, "%s\n", t->path);
466 }
467 library_modified = 0;
468 XPTHREAD_MUTEX_UNLOCK(&library_menu_mtx);
469
470 fclose(fp);
471 ret = 0;
472 }
473
474 free(file);
475 return ret;
476 }
477