Atrinik Client  4.0
mplayer.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
32 #include <global.h>
33 #include <toolkit/path.h>
34 
38 #define FILE_MPLAYER_BLACKLIST "mplayer.blacklist"
39 
43 #define BLACKLIST_ALL_DELAY 1500
44 
45 enum {
46  BUTTON_PLAY,
47  BUTTON_SHUFFLE,
48  BUTTON_BLACKLIST,
49  BUTTON_CLOSE,
51 
52  BUTTON_NUM
53 } ;
54 
58 static uint8_t shuffle = 0;
62 static uint8_t *shuffle_blacklist = NULL;
66 static button_struct buttons[BUTTON_NUM];
78 static list_struct *list_mplayer = NULL;
79 
85 static void list_handle_enter(list_struct *list, SDL_Event *event)
86 {
89  shuffle = 0;
90 }
91 
93 static void list_text_color_hook(list_struct *list, uint32_t row, uint32_t col, const char **color, const char **color_shadow)
94 {
95  if (shuffle_blacklist[row]) {
96  *color = COLOR_RED;
97  }
98 }
99 
106 static void mplayer_do_shuffle(list_struct *list)
107 {
108  size_t i;
109  uint8_t found_num = 0;
110  uint32_t *row_ids, row_num, selected;
111 
112  /* Calculate whether there are enough non-blacklisted songs to
113  * shuffle through. */
114  for (i = 0; i < list->rows - 1 && found_num < 2; i++) {
115  if (!shuffle_blacklist[i]) {
116  found_num++;
117  }
118  }
119 
120  if (found_num < 2) {
121  return;
122  }
123 
124  /* Build a list containing non-blacklisted row IDs. */
125  row_num = 0;
126  row_ids = emalloc(sizeof(*row_ids) * (list->rows - 1));
127 
128  for (i = 0; i < list->rows - 1; i++) {
129  if (!shuffle_blacklist[i]) {
130  row_ids[row_num++] = i;
131  }
132  }
133 
134  /* Select a row ID at random. */
135  selected = row_ids[rndm(1, row_num) - 1];
136  efree(row_ids);
137 
138  list->row_selected = selected + 1;
139  list->row_offset = MIN(list->rows - list->max_rows, selected);
140  cur_widget[MPLAYER_ID]->redraw = 1;
141 
143 }
144 
148 static void mplayer_check_shuffle(void)
149 {
150  if (!sound_playing_music()) {
151  mplayer_do_shuffle(list_mplayer);
152  }
153 }
154 
164 {
165  if (list && shuffle_blacklist && shuffle_blacklist[list->row_selected - 1]) {
166  return 1;
167  }
168 
169  return 0;
170 }
171 
178 {
179  if (list && shuffle_blacklist) {
180  /* Clear blacklist status. */
181  if (shuffle_blacklist[list->row_selected - 1]) {
182  shuffle_blacklist[list->row_selected - 1] = 0;
183  } else {
184  /* Enable blacklist status. */
185 
186  shuffle_blacklist[list->row_selected - 1] = 1;
187 
188  /* Shuffle mode and we're playing the music we just
189  * blacklisted, so stop playing it. */
190  if (shuffle && !strcmp(sound_get_bg_music_basename(), list->text[list->row_selected - 1][0])) {
191  sound_start_bg_music("no_music", 0, 0);
192  }
193  }
194 
195  cur_widget[MPLAYER_ID]->redraw = 1;
196  }
197 }
198 
206 static void mplayer_blacklist_mass_toggle(list_struct *list, uint8_t state)
207 {
208  if (list && shuffle_blacklist) {
209  size_t row;
210 
211  for (row = 0; row < list->rows - 1; row++) {
212  shuffle_blacklist[row] = state;
213  }
214 
215  /* Shuffle mode and we're disabling all, turn off music. */
216  if (shuffle && state == 1) {
217  sound_start_bg_music("no_music", 0, 0);
218  }
219 
220  cur_widget[MPLAYER_ID]->redraw = 1;
221  }
222 }
223 
230 {
231  FILE *fp;
232  size_t row;
233 
234  fp = path_fopen(FILE_MPLAYER_BLACKLIST, "w");
235 
236  if (fp == NULL) {
237  LOG(ERROR, "Failed to open file: %s",
239  return;
240  }
241 
242  for (row = 0; row < list->rows; row++) {
243  if (shuffle_blacklist[row]) {
244  fprintf(fp, "%s\n", list->text[row][0]);
245  }
246  }
247 
248  fclose(fp);
249 }
250 
261 static void mplayer_list_init(list_struct *list, const char *path, uint8_t duplicates)
262 {
263  DIR *dir;
264  struct dirent *currentfile;
265  char buf[HUGE_BUF];
266 
267  /* Read the media directory and add the file names to the list. */
268  dir = opendir(path);
269 
270  if (!dir) {
271  return;
272  }
273 
274  while ((currentfile = readdir(dir))) {
275  /* Ignore hidden files and files without extension. */
276  if (currentfile->d_name[0] == '.' || !strchr(currentfile->d_name, '.')) {
277  continue;
278  }
279 
280  /* Check for duplicates. */
281  if (duplicates) {
282  uint8_t found = 0;
283  uint32_t row;
284 
285  for (row = 0; row < list->rows; row++) {
286  if (!strcmp(list->text[row][0], currentfile->d_name)) {
287  found = 1;
288  break;
289  }
290  }
291 
292  if (found) {
293  continue;
294  }
295  }
296 
297  snprintf(buf, sizeof(buf), "%s/%s", path, currentfile->d_name);
298 
299  /* Ignore files that cannot be accessed for reading; insufficient
300  * permissions, or broken symlinks, for example. */
301  if (access(buf, R_OK) == 0) {
302  list_add(list, list->rows, 0, currentfile->d_name);
303  }
304  }
305 
306  closedir(dir);
307 }
308 
310 static void widget_draw(widgetdata *widget)
311 {
312  SDL_Rect box;
313  char buf[HUGE_BUF];
314  size_t i;
315 
316  /* The list doesn't exist yet, create it. */
317  if (!list_mplayer) {
318  char version[MAX_BUF];
319 
320  /* Create the list and set up settings. */
321  list_mplayer = list_create(12, 1, 8);
322  list_mplayer->handle_enter_func = list_handle_enter;
323  list_mplayer->text_color_hook = list_text_color_hook;
324  list_mplayer->surface = widget->surface;
325  list_scrollbar_enable(list_mplayer);
326  list_set_column(list_mplayer, 0, 130, 7, NULL, -1);
327  list_set_font(list_mplayer, FONT_ARIAL10);
328 
329  /* Add default media directory songs. */
330  get_data_dir_file(buf, sizeof(buf), DIRECTORY_MEDIA);
331  mplayer_list_init(list_mplayer, buf, 0);
332 
333  /* Now add custom ones, but ignore duplicates. */
334  snprintf(buf, sizeof(buf), "%s/.atrinik/%s/"DIRECTORY_MEDIA, get_config_dir(), package_get_version_partial(version, sizeof(version)));
335  mplayer_list_init(list_mplayer, buf, 1);
336 
337  /* If we added any, sort the list alphabetically and add an entry
338  * to disable background music. */
339  if (list_mplayer->rows) {
340  FILE *fp;
341 
342  /* Allocate the blacklist. + 1 is for the last entry added
343  * further down. It is not actually used by the blacklist as
344  * it's not possible to toggle it on/off using the button, but
345  * it simplifies other logic checks. */
346  shuffle_blacklist = ecalloc(1, sizeof(*shuffle_blacklist) * (list_mplayer->rows + 1));
347 
348  /* Sort the list. */
349  list_sort(list_mplayer, LIST_SORT_ALPHA);
350 
351  /* Read the blacklist file contents. */
352  fp = path_fopen(FILE_MPLAYER_BLACKLIST, "r");
353 
354  if (fp) {
355  size_t row;
356 
357  while (fgets(buf, sizeof(buf) - 1, fp)) {
358  for (row = 0; row < list_mplayer->rows; row++) {
359  if (!strncmp(buf, list_mplayer->text[row][0], strlen(buf) - 1)) {
360  shuffle_blacklist[row] = 1;
361  break;
362  }
363  }
364  }
365 
366  fclose(fp);
367  }
368 
369  list_add(list_mplayer, list_mplayer->rows, 0, "Disable music");
370  }
371 
372  scrollbar_create(&scrollbar_progress, 130, 11, &scrollbar_progress_info.scroll_offset, &scrollbar_progress_info.num_lines, 1);
373  scrollbar_progress.redraw = &scrollbar_progress_info.redraw;
374  }
375 
376  if (widget->redraw) {
377  const char *bg_music;
378 
379  box.h = 0;
380  box.w = widget->w;
381  text_show(widget->surface, FONT_SERIF12, "Music Player", 0, 3, COLOR_HGOLD, TEXT_ALIGN_CENTER, &box);
382  list_set_parent(list_mplayer, widget->x, widget->y);
383  list_show(list_mplayer, 10, 2);
384  box.w /= 2;
385  text_show(widget->surface, FONT_SANS10, "Currently playing:", widget->w / 2, 22, COLOR_WHITE, TEXT_ALIGN_CENTER, &box);
386 
387  bg_music = sound_get_bg_music_basename();
388  box.h = 0;
389  box.w = widget->w / 2;
390 
391  /* Store the background music file name in temporary buffer and
392  * make sure it won't overflow by truncating it if necessary. */
393  if (bg_music) {
394  strncpy(buf, bg_music, sizeof(buf) - 1);
395  buf[sizeof(buf) - 1] = '\0';
396  text_truncate_overflow(FONT_SANS11, buf, 150);
397  }
398 
399  /* Show the music that is being played. */
400  text_show(widget->surface, FONT_SANS11, bg_music ? buf : "No music", widget->w / 2 - 5, 34, COLOR_HGOLD, TEXT_ALIGN_CENTER, &box);
401 
402  scrollbar_progress.px = widget->x;
403  scrollbar_progress.py = widget->y;
404  scrollbar_show(&scrollbar_progress, widget->surface, 170, 50);
405 
406  box.h = 120;
407  box.w -= 6 * 2;
408  text_show(widget->surface, FONT_ARIAL10, "You can use the music player to play your favorite tunes from the game, or play them all one-by-one in random order (shuffle).\n\nNote that if you use the music player, in-game areas won't change your music until you click [b]Stop[/b].", widget->w / 2 + 6, 62, COLOR_WHITE, TEXT_WORD_WRAP | TEXT_MARKUP, &box);
409 
410  for (i = 0; i < BUTTON_NUM; i++) {
411  buttons[i].surface = widget->surface;
412  button_set_parent(&buttons[i], widget->x, widget->y);
413  }
414 
415  buttons[BUTTON_PLAY].x = 10;
416  buttons[BUTTON_PLAY].y = widget->h - TEXTURE_CLIENT("button")->h - 4;
417  button_show(&buttons[BUTTON_PLAY], sound_map_background(-1) ? "Stop" : "Play");
418 
419  buttons[BUTTON_SHUFFLE].x = 10 + TEXTURE_CLIENT("button")->w + 5;
420  buttons[BUTTON_SHUFFLE].y = widget->h - TEXTURE_CLIENT("button")->h - 4;
421  buttons[BUTTON_SHUFFLE].pressed_forced = shuffle;
422  button_show(&buttons[BUTTON_SHUFFLE], "Shuffle");
423 
424  buttons[BUTTON_BLACKLIST].x = 10 + TEXTURE_CLIENT("button")->w * 2 + 5 * 2;
425  buttons[BUTTON_BLACKLIST].y = widget->h - TEXTURE_CLIENT("button_round")->h - 5;
426  buttons[BUTTON_BLACKLIST].disabled = list_mplayer->row_selected == list_mplayer->rows;
427  button_show(&buttons[BUTTON_BLACKLIST], mplayer_blacklisted(list_mplayer) ? "+" : "-");
428 
429  /* Show close button. */
430  buttons[BUTTON_CLOSE].x = widget->w - TEXTURE_CLIENT("button_round")->w - 4;
431  buttons[BUTTON_CLOSE].y = 4;
432  button_show(&buttons[BUTTON_CLOSE], "X");
433 
434  /* Show help button. */
435  buttons[BUTTON_HELP].x = widget->w - TEXTURE_CLIENT("button_round")->w * 2 - 4;
436  buttons[BUTTON_HELP].y = 4;
437  button_show(&buttons[BUTTON_HELP], "?");
438  }
439 }
440 
442 static void widget_background(widgetdata *widget, int draw)
443 {
444  uint32_t duration, num_lines;
445 
446  /* If shuffle is enabled, check whether we need to start playing
447  * another song. */
448  if (shuffle) {
450  }
451 
452  duration = sound_music_get_duration();
453  num_lines = duration + MAX(1, duration / 10);
454 
455  if (num_lines != scrollbar_progress_info.num_lines) {
456  scrollbar_progress_info.num_lines = num_lines;
457  scrollbar_progress.max_lines = MAX(1, duration / 10);
458  widget->redraw = 1;
459  }
460 
461  if (list_mplayer) {
462  if (scrollbar_progress_info.redraw && sound_music_can_seek()) {
463  sound_music_seek(scrollbar_progress_info.scroll_offset + 1);
464  scrollbar_progress_info.redraw = 0;
465  widget->redraw = 1;
466  } else {
467  uint32_t offset;
468 
469  offset = scrollbar_progress_info.scroll_offset;
470  scrollbar_progress.redraw = NULL;
471  scrollbar_scroll_to(&scrollbar_progress, sound_music_get_offset());
472  scrollbar_progress.redraw = &scrollbar_progress_info.redraw;
473 
474  if (offset != scrollbar_progress_info.scroll_offset) {
475  widget->redraw = 1;
476  }
477  }
478  }
479 
480  /* Do mass blacklist status change if the button has been held for
481  * some time. */
482  if (buttons[BUTTON_BLACKLIST].pressed && SDL_GetTicks() - buttons[BUTTON_BLACKLIST].pressed_ticks > BLACKLIST_ALL_DELAY) {
483  mplayer_blacklist_mass_toggle(list_mplayer, mplayer_blacklisted(list_mplayer));
484  mplayer_blacklist_save(list_mplayer);
485  buttons[BUTTON_BLACKLIST].pressed_ticks = SDL_GetTicks();
486  }
487 
488  if (!widget->redraw) {
489  widget->redraw = list_need_redraw(list_mplayer);
490  }
491 
492  if (!widget->redraw) {
493  size_t i;
494 
495  for (i = 0; i < BUTTON_NUM; i++) {
496  if (button_need_redraw(&buttons[i])) {
497  widget->redraw = 1;
498  break;
499  }
500  }
501  }
502 }
503 
505 static int widget_event(widgetdata *widget, SDL_Event *event)
506 {
507  size_t i;
508 
509  if (list_mplayer) {
510  if (list_handle_mouse(list_mplayer, event)) {
511  widget->redraw = 1;
512  return 1;
513  } else if (scrollbar_event(&scrollbar_progress, event)) {
514  widget->redraw = 1;
515  return 1;
516  }
517  }
518 
519  for (i = 0; i < BUTTON_NUM; i++) {
520  if (button_event(&buttons[i], event)) {
521  switch (i) {
522  case BUTTON_PLAY:
523 
524  if (sound_map_background(-1)) {
525  sound_start_bg_music("no_music", 0, 0);
527  shuffle = 0;
528  } else {
529  list_handle_enter(list_mplayer, event);
530  }
531 
532  break;
533 
534  case BUTTON_SHUFFLE:
535  shuffle = !shuffle;
536 
537  if (shuffle) {
538  mplayer_do_shuffle(list_mplayer);
540  } else {
541  sound_start_bg_music("no_music", 0, 0);
543  }
544 
545  break;
546 
547  case BUTTON_BLACKLIST:
548  /* Toggle the blacklist state of the selected row. */
549  mplayer_blacklist_toggle(list_mplayer);
550  mplayer_blacklist_save(list_mplayer);
551  break;
552 
553  case BUTTON_CLOSE:
554  widget->show = 0;
555  break;
556 
557  case BUTTON_HELP:
558  help_show("music player");
559  break;
560  }
561 
562  widget->redraw = 1;
563  return 1;
564  }
565 
566  if (buttons[i].redraw) {
567  widget->redraw = 1;
568  }
569  }
570 
571  return 0;
572 }
573 
575 static void widget_deinit(widgetdata *widget)
576 {
577  if (shuffle_blacklist) {
578  efree(shuffle_blacklist);
579  shuffle_blacklist = NULL;
580  }
581 
582  if (list_mplayer != NULL) {
583  list_remove(list_mplayer);
584  list_mplayer = NULL;
585  }
586 
587  for (size_t i = 0; i < BUTTON_NUM; i++) {
588  button_destroy(&buttons[i]);
589  }
590 }
591 
596 {
597  widget->draw_func = widget_draw;
598  widget->background_func = widget_background;
599  widget->event_func = widget_event;
600  widget->deinit_func = widget_deinit;
601 
602  for (size_t i = 0; i < BUTTON_NUM; i++) {
603  button_create(&buttons[i]);
604 
605  if (i == BUTTON_BLACKLIST || i == BUTTON_HELP || i == BUTTON_CLOSE) {
606  buttons[i].texture = texture_get(TEXTURE_TYPE_CLIENT,
607  "button_round");
608  buttons[i].texture_pressed = texture_get(TEXTURE_TYPE_CLIENT,
609  "button_round_down");
610  buttons[i].texture_over = texture_get(TEXTURE_TYPE_CLIENT,
611  "button_round_over");
612  }
613  }
614 }
const char * sound_get_bg_music_basename(void)
Definition: sound.c:587
void scrollbar_scroll_to(scrollbar_struct *scrollbar, int scroll)
Definition: scrollbar.c:397
uint8_t sound_map_background(int val)
Definition: sound.c:608
void sound_music_seek(uint32_t offset)
Definition: sound.c:669
list_struct * list_create(uint32_t max_rows, uint32_t cols, int spacing)
Definition: list.c:113
#define TEXT_WORD_WRAP
Definition: text.h:226
void sound_start_bg_music(const char *filename, int volume, int loop)
Definition: sound.c:412
void list_remove(list_struct *list)
Definition: list.c:522
static scrollbar_info_struct scrollbar_progress_info
Definition: mplayer.c:74
SDL_Surface * surface
Definition: widget.h:110
#define BLACKLIST_ALL_DELAY
Definition: mplayer.c:43
int button_event(button_struct *button, SDL_Event *event)
Definition: button.c:222
static void mplayer_list_init(list_struct *list, const char *path, uint8_t duplicates)
Definition: mplayer.c:261
void list_set_column(list_struct *list, uint32_t col, int width, int spacing, const char *name, int centered)
Definition: list.c:242
static void widget_deinit(widgetdata *widget)
Definition: mplayer.c:575
void button_destroy(button_struct *button)
Definition: button.c:94
#define LIST_SORT_ALPHA
Definition: list.h:285
int list_handle_mouse(list_struct *list, SDL_Event *event)
Definition: list.c:698
void text_show(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:1983
void list_add(list_struct *list, uint32_t row, uint32_t col, const char *str)
Definition: list.c:163
int sound_playing_music(void)
Definition: sound.c:903
static void mplayer_check_shuffle(void)
Definition: mplayer.c:148
uint32_t sound_music_get_duration()
Definition: sound.c:691
#define COLOR_HGOLD
Definition: text.h:319
static void mplayer_blacklist_mass_toggle(list_struct *list, uint8_t state)
Definition: mplayer.c:206
uint32_t row_offset
Definition: list.h:117
texture_struct * texture_get(texture_type_t type, const char *name)
Definition: texture.c:279
char *** text
Definition: list.h:85
static void list_handle_enter(list_struct *list, SDL_Event *event)
Definition: mplayer.c:85
static uint8_t shuffle
Definition: mplayer.c:58
#define TEXT_MARKUP
Definition: text.h:224
uint32_t max_lines
Definition: scrollbar.h:77
static scrollbar_struct scrollbar_progress
Definition: mplayer.c:70
#define TEXT_ALIGN_CENTER
Definition: text.h:230
char * package_get_version_partial(char *dst, size_t dstlen)
Definition: misc.c:89
static void widget_background(widgetdata *widget, int draw)
Definition: mplayer.c:442
#define COLOR_RED
Definition: text.h:295
void scrollbar_show(scrollbar_struct *scrollbar, SDL_Surface *surface, int x, int y)
Definition: scrollbar.c:443
static uint8_t * shuffle_blacklist
Definition: mplayer.c:62
void help_show(const char *name)
Definition: help.c:219
uint8_t show
Definition: widget.h:69
int list_need_redraw(list_struct *list)
Definition: list.c:315
void text_truncate_overflow(font_struct *font, char *text, int max_width)
Definition: text.c:2446
texture_struct * texture_over
Definition: button.h:67
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
widgetdata * cur_widget[TOTAL_SUBWIDGETS]
Definition: widget.c:75
uint8_t redraw
Definition: widget.h:72
void button_create(button_struct *button)
Definition: button.c:65
static list_struct * list_mplayer
Definition: mplayer.c:78
void(* text_color_hook)(struct list_struct *list, uint32_t row, uint32_t col, const char **color, const char **color_shadow)
Definition: list.h:228
static void mplayer_blacklist_toggle(list_struct *list)
Definition: mplayer.c:177
int x
Definition: widget.h:48
uint32_t max_rows
Definition: list.h:57
void scrollbar_create(scrollbar_struct *scrollbar, int w, int h, uint32_t *scroll_offset, uint32_t *num_lines, uint32_t max_lines)
Definition: scrollbar.c:350
texture_struct * texture
Definition: button.h:61
void list_sort(list_struct *list, int type)
Definition: list.c:832
static button_struct buttons[BUTTON_NUM]
Definition: mplayer.c:66
SDL_Surface * surface
Definition: list.h:138
uint32_t pressed_ticks
Definition: button.h:114
SDL_Surface * surface
Definition: button.h:46
int w
Definition: widget.h:54
uint8_t * redraw
Definition: scrollbar.h:87
int h
Definition: widget.h:57
int disabled
Definition: button.h:111
texture_struct * texture_pressed
Definition: button.h:73
void list_set_font(list_struct *list, font_struct *font)
Definition: list.c:284
void list_show(list_struct *list, int x, int y)
Definition: list.c:337
static void widget_draw(widgetdata *widget)
Definition: mplayer.c:310
int scrollbar_event(scrollbar_struct *scrollbar, SDL_Event *event)
Definition: scrollbar.c:529
static int mplayer_blacklisted(list_struct *list)
Definition: mplayer.c:163
static void mplayer_blacklist_save(list_struct *list)
Definition: mplayer.c:229
void list_set_parent(list_struct *list, int px, int py)
Definition: list.c:96
#define FILE_MPLAYER_BLACKLIST
Definition: mplayer.c:38
int sound_music_can_seek(void)
Definition: sound.c:642
void list_scrollbar_enable(list_struct *list)
Definition: list.c:302
static void list_text_color_hook(list_struct *list, uint32_t row, uint32_t col, const char **color, const char **color_shadow)
Definition: mplayer.c:93
void button_show(button_struct *button, const char *text)
Definition: button.c:161
uint32_t sound_music_get_offset(void)
Definition: sound.c:623
void get_data_dir_file(char *buf, size_t len, const char *fname)
Definition: wrapper.c:331
uint32_t rows
Definition: list.h:60
void(* handle_enter_func)(struct list_struct *list, SDL_Event *event)
Definition: list.h:201
const char * get_config_dir(void)
Definition: wrapper.c:303
uint32_t scroll_offset
Definition: scrollbar.h:135
void widget_mplayer_init(widgetdata *widget)
Definition: mplayer.c:595
uint32_t row_selected
Definition: list.h:109
static void mplayer_do_shuffle(list_struct *list)
Definition: mplayer.c:106
#define COLOR_WHITE
Definition: text.h:289
int pressed_forced
Definition: button.h:108
static int widget_event(widgetdata *widget, SDL_Event *event)
Definition: mplayer.c:505
int y
Definition: widget.h:51