From a55523d7f3758d1fa46b8f8d063c57229db27bf3 Mon Sep 17 00:00:00 2001 From: waker Date: Sun, 5 Jul 2009 21:36:02 +0200 Subject: [wip] multithreading --- Jamfile | 4 +- callbacks.c | 245 +++-------------------------------------------- cmp3.c | 62 +++++++----- codec.h | 2 + common.h | 7 ++ gtkplaylist.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gtkplaylist.h | 40 ++++++++ main.c | 35 ++++++- messagepump.c | 99 +++++++++++++++++++ messagepump.h | 12 +++ messages.h | 14 +++ psdl.c | 19 +++- 12 files changed, 579 insertions(+), 259 deletions(-) create mode 100644 common.h create mode 100644 gtkplaylist.c create mode 100644 gtkplaylist.h create mode 100644 messagepump.c create mode 100644 messagepump.h create mode 100644 messages.h diff --git a/Jamfile b/Jamfile index 005baef7..94b2f391 100644 --- a/Jamfile +++ b/Jamfile @@ -13,7 +13,7 @@ HDRS += /usr/include/pango-1.0 ; HDRS += /usr/include/cairo ; Main deadbeef : - cmod.c codec.c cvorbis.c cwav.c cmp3.c playlist.c psdl.c main.c support.c interface.c callbacks.c threading.c ; + cmod.c codec.c cvorbis.c cwav.c cmp3.c playlist.c psdl.c main.c support.c interface.c callbacks.c threading.c messagepump.c gtkplaylist.c ; -LINKLIBS on deadbeef = -lmikmod -lm -lvorbis -logg -lvorbisfile -lmikmod -lmad -lSDL -lsamplerate -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 ; +LINKLIBS on deadbeef = -lmikmod -lm -lvorbis -logg -lvorbisfile -lmad -lSDL -lsamplerate -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lgthread -lpthread -lglib ; diff --git a/callbacks.c b/callbacks.c index 9226cae5..f4b52d18 100644 --- a/callbacks.c +++ b/callbacks.c @@ -10,19 +10,16 @@ #include "interface.h" #include "support.h" +#include "common.h" + #include "psdl.h" #include "playlist.h" +#include "gtkplaylist.h" +#include "messagepump.h" +#include "messages.h" -#define min(x,y) ((x)<(y)?(x):(y)) -#define max(x,y) ((x)>(y)?(x):(y)) extern GtkWidget *mainwin; -static GdkPixmap *backbuf; -static int rowheight = 17; -int trackerscroll = 0; -static int playlist_row = -1; -static double playlist_clicktime = 0; -static double ps_lastpos[2]; static void addfile_func (gpointer data, gpointer userdata) { @@ -30,144 +27,6 @@ addfile_func (gpointer data, gpointer userdata) { g_free (data); } -static void -setup_ps_scrollbar (void) { - GtkWidget *playlist = lookup_widget (mainwin, "playlist"); - int h = playlist->allocation.height / rowheight; - int size = ps_getcount (); - if (h >= size) { - size = 0; - } - GtkWidget *scroll = lookup_widget (mainwin, "playscroll"); -// gtk_range_set_range (GTK_RANGE (scroll), 0, size); -// gtk_range_set_increments (GTK_RANGE (scroll), 1, h); - GtkAdjustment *adj = (GtkAdjustment*)gtk_adjustment_new (gtk_range_get_value (GTK_RANGE (scroll)), 0, size, 1, h, h); - gtk_range_set_adjustment (GTK_RANGE (scroll), adj); -} - -void -draw_ps_row_back (GdkDrawable *drawable, cairo_t *cr, int row) { - // draw background - float w; - int start, end; - int startx, endx; - int width, height; - gdk_drawable_get_size (drawable, &width, &height); - w = width; - if (row == playlist_row) { - cairo_set_source_rgb (cr, 0x7f/255.f, 0x7f/255.f, 0x7f/255.f); - cairo_rectangle (cr, 0, row * rowheight - trackerscroll * rowheight, width, rowheight); - cairo_fill (cr); - cairo_set_source_rgb (cr, 0xae/255.f, 0xa6/255.f, 0x9d/255.f); - cairo_rectangle (cr, 1, row * rowheight - trackerscroll * rowheight+1, width-2, rowheight-2); - cairo_fill (cr); - } - else { - if (row % 2) { - cairo_set_source_rgb (cr, 0x1d/255.f, 0x1f/255.f, 0x1b/255.f); - cairo_rectangle (cr, 0, row * rowheight - trackerscroll * rowheight, w, rowheight); - cairo_fill (cr); - } - else { - cairo_set_source_rgb (cr, 0x21/255.f, 0x23/255.f, 0x1f/255.f); - cairo_rectangle (cr, 0, row * rowheight - trackerscroll * rowheight, width, rowheight); - cairo_fill (cr); - } - } -// start = min (vbstart[1], vbend[1]); -// end = max (vbstart[1], vbend[1]); -// startx = min (vbstart[0], vbend[0]); -// endx = max (vbstart[0], vbend[0]); -// if (tracker_vbmode && row >= start && row <= end) { // hilight selected notes -// cairo_set_source_rgb (cr, 0.0, 0.44, 0.0); -// cairo_rectangle (cr, startx * colwidth * COL_NUM_CHARS + colwidth * 3, row * rowheight - trackerscroll * rowheight, (endx - startx + 1) * colwidth * COL_NUM_CHARS, rowheight); -// cairo_fill (cr); -// } -} - -static void -text_draw (cairo_t *cr, int x, int y, const char *text) { - cairo_move_to (cr, x, y+rowheight-3); - cairo_show_text (cr, text); -} - -void -draw_ps_row (GdkDrawable *drawable, cairo_t *cr, int row, playItem_t *it) { - int width, height; - gdk_drawable_get_size (drawable, &width, &height); - if (it == playlist_current) { - cairo_set_source_rgb (cr, 1, 1, 1); - cairo_rectangle (cr, 3, row * rowheight - trackerscroll * rowheight + 3, rowheight-6, rowheight-6); - cairo_fill (cr); - } - if (row == playlist_row) { - cairo_set_source_rgb (cr, 0, 0, 0); - } - else { - cairo_set_source_rgb (cr, 0xf4/255.f, 0x7e/255.f, 0x46/255.f); - } - text_draw (cr, rowheight, row * rowheight - trackerscroll * rowheight, it->displayname); -} - -void -redraw_ps_row (GtkWidget *widget, int row) { - int x, y, w, h; - - x = 0; - y = (row - trackerscroll) * rowheight; - w = widget->allocation.width; - h = rowheight; - - cairo_t *cr; - cr = gdk_cairo_create (backbuf); - if (!cr) { - return; - } - //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); - -// cairo_select_font_face (cr, "fixed", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); -// cairo_set_font_size (cr, rowheight); - - playItem_t *it = ps_get_for_idx (row); - if (it) { - draw_ps_row_back (backbuf, cr, row); - draw_ps_row (backbuf, cr, row, it); - } - cairo_destroy (cr); - gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, x, y, x, y, w, h); -} - - -void draw_playlist (GtkWidget *widget, int x, int y, int w, int h) { - cairo_t *cr; - cr = gdk_cairo_create (backbuf); - if (!cr) { - return; - } - //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); - -// cairo_select_font_face (cr, "fixed", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); -// cairo_set_font_size (cr, rowheight); - int row; - int row1; - int row2; - int row2_full; - row1 = max (0, y / rowheight + trackerscroll); - row2 = min (ps_getcount (), (y+h) / rowheight + trackerscroll + 1); - row2_full = (y+h) / rowheight + trackerscroll + 1; - // draw background - for (row = row1; row < row2_full; row++) { - draw_ps_row_back (backbuf, cr, row); - } - playItem_t *it = ps_get_for_idx (trackerscroll); - for (row = row1; row < row2; row++) { - draw_ps_row (backbuf, cr, row, it); - it = it->next; - } - - cairo_destroy (cr); -} - void on_volume_value_changed (GtkRange *range, @@ -191,13 +50,7 @@ on_playlist_configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data) { - setup_ps_scrollbar (); - if (backbuf) { - g_object_unref (backbuf); - } - backbuf = gdk_pixmap_new (widget->window, widget->allocation.width, widget->allocation.height, -1); - draw_playlist (widget, 0, 0, widget->allocation.width, widget->allocation.height); - + gtkps_reconf (widget); return FALSE; } @@ -208,7 +61,7 @@ on_playlist_expose_event (GtkWidget *widget, gpointer user_data) { // draw visible area of playlist - gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); + gtkps_expose (widget, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } @@ -228,55 +81,7 @@ on_playlist_button_press_event (GtkWidget *widget, gpointer user_data) { if (event->button == 1) { - // remember mouse coords for doubleclick detection - ps_lastpos[0] = event->x; - ps_lastpos[1] = event->y; - // select item - int y = event->y/rowheight + trackerscroll; - if (y < 0 || y >= ps_getcount ()) { - y = -1; - } - - if (playlist_row != y) { - int old = playlist_row; - playlist_row = y; - if (old != -1) { - redraw_ps_row (widget, old); - } - if (playlist_row != -1) { - redraw_ps_row (widget, playlist_row); - } - } - - if (event->time - playlist_clicktime < 0.5 - && fabs(ps_lastpos[0] - event->x) < 3 - && fabs(ps_lastpos[1] - event->y) < 3) { - // doubleclick - play this item - if (playlist_row != -1) { - playItem_t *it = ps_get_for_idx (playlist_row); - if (it) { - playItem_t *prev = playlist_current; - playlist_current = it; - if (playlist_current != prev) { - if (prev) { - redraw_ps_row (widget, ps_get_idx_of (prev)); - } - if (playlist_current) { - redraw_ps_row (widget, ps_get_idx_of (playlist_current)); - } - } - psdl_stop (); - psdl_play (it); - } - } - - - // prevent next click to trigger doubleclick - playlist_clicktime = event->time-1; - } - else { - playlist_clicktime = event->time; - } + gtkps_mouse1_clicked (widget, event->x, event->y, event->time); } return FALSE; } @@ -287,12 +92,7 @@ on_playscroll_value_changed (GtkRange *range, gpointer user_data) { int newscroll = gtk_range_get_value (GTK_RANGE (range)); - if (newscroll != trackerscroll) { - trackerscroll = newscroll; - GtkWidget *widget = lookup_widget (mainwin, "playlist"); - draw_playlist (widget, 0, 0, widget->allocation.width, widget->allocation.height); - gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height); - } + gtkps_scroll (newscroll); } @@ -331,10 +131,10 @@ on_add_files_activate (GtkMenuItem *menuitem, g_slist_free (lst); } gtk_widget_destroy (dlg); - setup_ps_scrollbar (); + gtkps_setup_scrollbar (); GtkWidget *widget = lookup_widget (mainwin, "playlist"); draw_playlist (widget, 0, 0, widget->allocation.width, widget->allocation.height); - gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height); + gtkps_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height); } @@ -354,10 +154,10 @@ on_add_folder1_activate (GtkMenuItem *menuitem, } } gtk_widget_destroy (dlg); - setup_ps_scrollbar (); + gtkps_setup_scrollbar (); GtkWidget *widget = lookup_widget (mainwin, "playlist"); draw_playlist (widget, 0, 0, widget->allocation.width, widget->allocation.height); - gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height); + gtkps_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height); } @@ -450,24 +250,7 @@ void on_playbtn_clicked (GtkButton *button, gpointer user_data) { - if (psdl_ispaused ()) - psdl_unpause (); - else if (playlist_current) { - psdl_stop (); - psdl_play (playlist_current); - GtkWidget *widget = lookup_widget (mainwin, "playlist"); - redraw_ps_row (widget, ps_get_idx_of (playlist_current)); - } - else if (playlist_row != -1) { - playItem_t *it = ps_get_for_idx (playlist_row); - if (it) { - psdl_stop (); - psdl_play (it); - playlist_current = it; - } - GtkWidget *widget = lookup_widget (mainwin, "playlist"); - redraw_ps_row (widget, playlist_row); - } + messagepump_push (M_PLAYSONG, 0, 0, 0); } diff --git a/cmp3.c b/cmp3.c index c6b61d90..5637bbc4 100644 --- a/cmp3.c +++ b/cmp3.c @@ -32,9 +32,9 @@ static struct mad_frame frame; static struct mad_synth synth; static mad_timer_t timer; static int frame_count; -unsigned char *GuardPtr = NULL; +static int endoffile; -static void cmp3_decode (void); +static int cmp3_decode (void); int cmp3_init (const char *fname) { @@ -43,22 +43,18 @@ cmp3_init (const char *fname) { printf ("failed to read %s\n", fname); return -1; } - cmp3.info.bitsPerSample = 16; - cmp3.info.dataSize = -1; - cmp3.info.channels = 2; - cmp3.info.samplesPerSecond = 44100; buffer.remaining = 0; buffer.output = NULL; buffer.readsize = 0; buffer.cachefill = 0; frame_count = 0; - GuardPtr = NULL; + endoffile = 0; mad_stream_init(&stream); mad_frame_init(&frame); mad_synth_init(&synth); mad_timer_reset(&timer); - cmp3_decode (); + cmp3_decode (); // this 1st run will read 1st frame, but will not decode anything except header return 0; } @@ -102,7 +98,7 @@ static signed short MadFixedToSshort(mad_fixed_t Fixed) #define MadErrorString(x) mad_stream_errorstr(x) -static void +static int cmp3_decode (void) { for (;;) { // read more MPEG data if needed @@ -113,19 +109,32 @@ cmp3_decode (void) { memmove (buffer.input, stream.next_frame, buffer.remaining); } int size = READBUFFER - buffer.remaining; + int bytesread = 0; char *bytes = buffer.input + buffer.remaining; - for (;;) { - int bytesread = fread (bytes, 1, size, buffer.file); - if (bytesread < size) { - fseek (buffer.file, 0, SEEK_SET); - size -= bytesread; - bytes += bytesread; - } - else { - break; + if (!endoffile) { + for (;;) { + bytesread = fread (bytes, 1, size, buffer.file); + if (bytesread < size) { + // end of file + endoffile = 1; + break; + /* + fseek (buffer.file, 0, SEEK_SET); + size -= bytesread; + bytes += bytesread; + */ + } + else { + break; + } } } - mad_stream_buffer(&stream,buffer.input,READBUFFER); + if (bytesread) { + mad_stream_buffer(&stream,buffer.input,bytesread); + } + else { + return -1; + } stream.error=0; } // decode next frame @@ -133,6 +142,7 @@ cmp3_decode (void) { { if(MAD_RECOVERABLE(stream.error)) { + /* if(stream.error!=MAD_ERROR_LOSTSYNC || stream.this_frame!=GuardPtr) { @@ -140,6 +150,7 @@ cmp3_decode (void) { MadErrorString(&stream)); fflush(stderr); } + */ continue; } else { @@ -160,6 +171,8 @@ cmp3_decode (void) { cmp3.info.dataSize = -1; cmp3.info.channels = MAD_NCHANNELS(&frame.header); cmp3.info.samplesPerSecond = frame.header.samplerate; + cmp3.info.duration = frame.header.duration.seconds; + cmp3.info.duration += (float)frame.header.duration.fraction/MAD_TIMER_RESOLUTION; frame_count++; break; } @@ -204,7 +217,13 @@ cmp3_decode (void) { if (buffer.readsize == 0) { break; } + if (buffer.readsize > 0 && endoffile) { + // fill rest with zeroes, and return -1 + memset (buffer.output, 0, buffer.readsize); + return -1; + } } + return 0; } void @@ -222,6 +241,7 @@ int cmp3_read (char *bytes, int size) { int result; int totalread = 0; + int ret = 0; if (buffer.cachefill > 0) { int sz = min (size, buffer.cachefill); memcpy (bytes, buffer.cache, sz); @@ -239,10 +259,10 @@ cmp3_read (char *bytes, int size) { if (size > 0) { buffer.output = bytes; buffer.readsize = size; - cmp3_decode ();//mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); + ret = cmp3_decode (); totalread += size; } - return 0; + return ret; } codec_t cmp3 = { diff --git a/codec.h b/codec.h index 731e3363..994ca53f 100644 --- a/codec.h +++ b/codec.h @@ -8,12 +8,14 @@ typedef struct { int channels; int dataSize; int samplesPerSecond; + float duration; } fileinfo_t; typedef struct codec_s { fileinfo_t info; int (*init) (const char *fname); void (*free) (void); + // player is responsible for starting next song if -1 is returned int (*read) (char *bytes, int size); } codec_t; diff --git a/common.h b/common.h new file mode 100644 index 00000000..63f0f010 --- /dev/null +++ b/common.h @@ -0,0 +1,7 @@ +#ifndef __COMMON_H +#define __COMMON_H + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +#endif // __COMMON_H diff --git a/gtkplaylist.c b/gtkplaylist.c new file mode 100644 index 00000000..85dc345d --- /dev/null +++ b/gtkplaylist.c @@ -0,0 +1,299 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include "gtkplaylist.h" +#include "callbacks.h" +#include "interface.h" +#include "support.h" +#include "playlist.h" +#include "psdl.h" +#include "common.h" + +extern GtkWidget *mainwin; +static GdkPixmap *backbuf; +static int rowheight = 17; +static int scrollpos = 0; +static int playlist_row = -1; +static double playlist_clicktime = 0; +static double ps_lastpos[2]; + +static void +text_draw (cairo_t *cr, int x, int y, const char *text) { + cairo_move_to (cr, x, y+rowheight-3); + cairo_show_text (cr, text); +} + +void +gtkps_setup_scrollbar (void) { + GtkWidget *playlist = lookup_widget (mainwin, "playlist"); + int h = playlist->allocation.height / rowheight; + int size = ps_getcount (); + if (h >= size) { + size = 0; + } + GtkWidget *scroll = lookup_widget (mainwin, "playscroll"); +// gtk_range_set_range (GTK_RANGE (scroll), 0, size); +// gtk_range_set_increments (GTK_RANGE (scroll), 1, h); + GtkAdjustment *adj = (GtkAdjustment*)gtk_adjustment_new (gtk_range_get_value (GTK_RANGE (scroll)), 0, size, 1, h, h); + gtk_range_set_adjustment (GTK_RANGE (scroll), adj); +} + +void +redraw_ps_row (GtkWidget *widget, int row) { + int x, y, w, h; + + x = 0; + y = (row - scrollpos) * rowheight; + w = widget->allocation.width; + h = rowheight; + + cairo_t *cr; + cr = gdk_cairo_create (backbuf); + if (!cr) { + return; + } + //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + +// cairo_select_font_face (cr, "fixed", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); +// cairo_set_font_size (cr, rowheight); + + playItem_t *it = ps_get_for_idx (row); + if (it) { + draw_ps_row_back (backbuf, cr, row); + draw_ps_row (backbuf, cr, row, it); + } + cairo_destroy (cr); + gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, x, y, x, y, w, h); +} + +void +gtkps_nextsong (void) { + GtkWidget *widget = lookup_widget (mainwin, "playlist"); + playItem_t *prev = playlist_current; + if (playlist_current) { + playlist_current = playlist_current->next; + } + if (!playlist_current) { + playlist_current = playlist_head; + } + if (playlist_current) { + psdl_stop (); + psdl_play (playlist_current); + } + if (playlist_current != prev) { + if (prev) { + redraw_ps_row (widget, ps_get_idx_of (prev)); + } + if (playlist_current) { + redraw_ps_row (widget, ps_get_idx_of (playlist_current)); + } + } +} + +void +draw_ps_row_back (GdkDrawable *drawable, cairo_t *cr, int row) { + // draw background + float w; + int start, end; + int startx, endx; + int width, height; + gdk_drawable_get_size (drawable, &width, &height); + w = width; + if (row == playlist_row) { + cairo_set_source_rgb (cr, 0x7f/255.f, 0x7f/255.f, 0x7f/255.f); + cairo_rectangle (cr, 0, row * rowheight - scrollpos * rowheight, width, rowheight); + cairo_fill (cr); + cairo_set_source_rgb (cr, 0xae/255.f, 0xa6/255.f, 0x9d/255.f); + cairo_rectangle (cr, 1, row * rowheight - scrollpos * rowheight+1, width-2, rowheight-2); + cairo_fill (cr); + } + else { + if (row % 2) { + cairo_set_source_rgb (cr, 0x1d/255.f, 0x1f/255.f, 0x1b/255.f); + cairo_rectangle (cr, 0, row * rowheight - scrollpos * rowheight, w, rowheight); + cairo_fill (cr); + } + else { + cairo_set_source_rgb (cr, 0x21/255.f, 0x23/255.f, 0x1f/255.f); + cairo_rectangle (cr, 0, row * rowheight - scrollpos * rowheight, width, rowheight); + cairo_fill (cr); + } + } +// start = min (vbstart[1], vbend[1]); +// end = max (vbstart[1], vbend[1]); +// startx = min (vbstart[0], vbend[0]); +// endx = max (vbstart[0], vbend[0]); +// if (tracker_vbmode && row >= start && row <= end) { // hilight selected notes +// cairo_set_source_rgb (cr, 0.0, 0.44, 0.0); +// cairo_rectangle (cr, startx * colwidth * COL_NUM_CHARS + colwidth * 3, row * rowheight - scrollpos * rowheight, (endx - startx + 1) * colwidth * COL_NUM_CHARS, rowheight); +// cairo_fill (cr); +// } +} + +void +draw_ps_row (GdkDrawable *drawable, cairo_t *cr, int row, playItem_t *it) { + int width, height; + gdk_drawable_get_size (drawable, &width, &height); + if (it == playlist_current) { + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_rectangle (cr, 3, row * rowheight - scrollpos * rowheight + 3, rowheight-6, rowheight-6); + cairo_fill (cr); + } + if (row == playlist_row) { + cairo_set_source_rgb (cr, 0, 0, 0); + } + else { + cairo_set_source_rgb (cr, 0xf4/255.f, 0x7e/255.f, 0x46/255.f); + } + text_draw (cr, rowheight, row * rowheight - scrollpos * rowheight, it->displayname); +} + + +void +draw_playlist (GtkWidget *widget, int x, int y, int w, int h) { + cairo_t *cr; + cr = gdk_cairo_create (backbuf); + if (!cr) { + return; + } + //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + +// cairo_select_font_face (cr, "fixed", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); +// cairo_set_font_size (cr, rowheight); + int row; + int row1; + int row2; + int row2_full; + row1 = max (0, y / rowheight + scrollpos); + row2 = min (ps_getcount (), (y+h) / rowheight + scrollpos + 1); + row2_full = (y+h) / rowheight + scrollpos + 1; + // draw background + for (row = row1; row < row2_full; row++) { + draw_ps_row_back (backbuf, cr, row); + } + playItem_t *it = ps_get_for_idx (scrollpos); + for (row = row1; row < row2; row++) { + draw_ps_row (backbuf, cr, row, it); + it = it->next; + } + + cairo_destroy (cr); +} + +void +gtkps_reconf (GtkWidget *widget) { + gtkps_setup_scrollbar (); + if (backbuf) { + g_object_unref (backbuf); + } + backbuf = gdk_pixmap_new (widget->window, widget->allocation.width, widget->allocation.height, -1); + draw_playlist (widget, 0, 0, widget->allocation.width, widget->allocation.height); +} + +void +gtkps_expose (GtkWidget *widget, int x, int y, int w, int h) { + gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, x, y, x, y, w, h); +} + +void +gtkps_mouse1_clicked (GtkWidget *widget, int ex, int ey, double time) { + // remember mouse coords for doubleclick detection + ps_lastpos[0] = ex; + ps_lastpos[1] = ey; + // select item + int y = ey/rowheight + scrollpos; + if (y < 0 || y >= ps_getcount ()) { + y = -1; + } + + if (playlist_row != y) { + int old = playlist_row; + playlist_row = y; + if (old != -1) { + redraw_ps_row (widget, old); + } + if (playlist_row != -1) { + redraw_ps_row (widget, playlist_row); + } + } + + if (time - playlist_clicktime < 0.5 + && fabs(ps_lastpos[0] - ex) < 3 + && fabs(ps_lastpos[1] - ey) < 3) { + // doubleclick - play this item + if (playlist_row != -1) { + playItem_t *it = ps_get_for_idx (playlist_row); + if (it) { + playItem_t *prev = playlist_current; + playlist_current = it; + if (playlist_current != prev) { + if (prev) { + redraw_ps_row (widget, ps_get_idx_of (prev)); + } + if (playlist_current) { + redraw_ps_row (widget, ps_get_idx_of (playlist_current)); + } + } + psdl_stop (); + psdl_play (it); + } + } + + + // prevent next click to trigger doubleclick + playlist_clicktime = time-1; + } + else { + playlist_clicktime = time; + } +} + +void +gtkps_scroll (int newscroll) { + if (newscroll != scrollpos) { + scrollpos = newscroll; + GtkWidget *widget = lookup_widget (mainwin, "playlist"); + draw_playlist (widget, 0, 0, widget->allocation.width, widget->allocation.height); + gdk_draw_drawable (widget->window, widget->style->black_gc, backbuf, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height); + } +} + +void +gtkps_playsong (void) { + if (psdl_ispaused ()) { + printf ("unpause\n"); + psdl_unpause (); + } + else if (playlist_current) { + printf ("restart\n"); + psdl_stop (); + psdl_play (playlist_current); + GtkWidget *widget = lookup_widget (mainwin, "playlist"); + redraw_ps_row (widget, ps_get_idx_of (playlist_current)); + } + else if (playlist_row != -1) { + printf ("start under cursor\n"); + playItem_t *it = ps_get_for_idx (playlist_row); + if (it) { + psdl_stop (); + psdl_play (it); + playlist_current = it; + } + GtkWidget *widget = lookup_widget (mainwin, "playlist"); + redraw_ps_row (widget, playlist_row); + } + else { + printf ("play 1st in list\n"); + playlist_current = playlist_head; + if (playlist_current) { + psdl_stop (); + psdl_play (playlist_current); + GtkWidget *widget = lookup_widget (mainwin, "playlist"); + redraw_ps_row (widget, ps_get_idx_of (playlist_current)); + } + } +} diff --git a/gtkplaylist.h b/gtkplaylist.h new file mode 100644 index 00000000..153707cc --- /dev/null +++ b/gtkplaylist.h @@ -0,0 +1,40 @@ +#ifndef __GTKPLAYLIST_H +#define __GTKPLAYLIST_H + +#include +#include "playlist.h" + +void +gtkps_nextsong (void); + +void +redraw_ps_row (GtkWidget *widget, int row); + +void +gtkps_setup_scrollbar (void); + +void +draw_ps_row_back (GdkDrawable *drawable, cairo_t *cr, int row); + +void +draw_ps_row (GdkDrawable *drawable, cairo_t *cr, int row, playItem_t *it); + +void +draw_playlist (GtkWidget *widget, int x, int y, int w, int h); + +void +gtkps_reconf (GtkWidget *widget); + +void +gtkps_expose (GtkWidget *widget, int x, int y, int w, int h); + +void +gtkps_mouse1_clicked (GtkWidget *widget, int ex, int ey, double time); + +void +gtkps_scroll (int newscroll); + +void +gtkps_playsong (void); + +#endif diff --git a/main.c b/main.c index 987f4365..4d13f071 100644 --- a/main.c +++ b/main.c @@ -7,6 +7,9 @@ #include "psdl.h" #include "unistd.h" #include "threading.h" +#include "messagepump.h" +#include "messages.h" +#include "gtkplaylist.h" GtkWidget *mainwin; @@ -16,7 +19,27 @@ void psdl_thread (uintptr_t ctx) { psdl_init (); while (!psdl_terminate) { - sleep(1); + uint32_t msg; + uintptr_t ctx; + uint32_t p1; + uint32_t p2; + while (messagepump_pop(&msg, &ctx, &p1, &p2) != -1) { + switch (msg) { + case M_SONGFINISHED: + // play next song in playlists + GDK_THREADS_ENTER(); + gtkps_nextsong (); + GDK_THREADS_LEAVE(); + break; + case M_PLAYSONG: + printf ("playsong!\n"); + GDK_THREADS_ENTER(); + gtkps_playsong (); + GDK_THREADS_LEAVE(); + break; + } + } + usleep(10); // handle message pump here } psdl_free (); @@ -25,9 +48,13 @@ psdl_thread (uintptr_t ctx) { int main (int argc, char *argv[]) { - thread_start (psdl_thread, 0); + messagepump_init (); +// thread_start (psdl_thread, 0); - gtk_set_locale (); + g_thread_init (NULL); + gdk_threads_init (); + gdk_threads_enter (); +// gtk_set_locale (); gtk_init (&argc, &argv); /* @@ -38,6 +65,8 @@ main (int argc, char *argv[]) { mainwin = create_mainwin (); gtk_widget_show (mainwin); gtk_main (); + gdk_threads_leave (); + messagepump_free (); psdl_terminate = 1; return 0; } diff --git a/messagepump.c b/messagepump.c new file mode 100644 index 00000000..9967a8dc --- /dev/null +++ b/messagepump.c @@ -0,0 +1,99 @@ +#include +#include +#include "messagepump.h" +#include "threading.h" + +typedef struct message_s { + uint32_t id; + uintptr_t ctx; + uint32_t p1; + uint32_t p2; + struct message_s *next; +} message_t; + +enum { MAX_MESSAGES = 100 }; +static message_t pool[MAX_MESSAGES]; +static message_t *mfree; +static message_t *mqueue; +static message_t *mqtail; +static uintptr_t mutex; + +static void +messagepump_reset (void); + +int +messagepump_init (void) { + messagepump_reset (); + mutex = mutex_create (); +} + +void +messagepump_free () { + mutex_lock (mutex); + messagepump_reset (); + mutex_free (mutex); +} + +static void +messagepump_reset (void) { + mqueue = NULL; + mfree = NULL; + mqtail = NULL; + memset (pool, 0, sizeof (pool)); + for (int i = 0; i < MAX_MESSAGES; i++) { + pool[i].next = mfree; + mfree = &pool[i]; + } +} + +int +messagepump_push (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + if (!mfree) { + printf ("WARNING: message queue is full! message ignored (%d %p %d %d)\n", id, ctx, p1, p2); + return -1; + } + mutex_lock (mutex); + message_t *msg = mfree; + mfree = mfree->next; + if (mqtail) { + mqtail->next = msg; + } + mqtail = msg; + if (!mqueue) { + mqueue = msg; + } + + msg->next = NULL; + msg->id = id; + msg->ctx = ctx; + msg->p1 = p1; + msg->p2 = p2; + mutex_unlock (mutex); + return 0; +} + +int +messagepump_pop (uint32_t *id, uintptr_t *ctx, uint32_t *p1, uint32_t *p2) { + if (!mqueue) { + return -1; + } + mutex_lock (mutex); + *id = mqueue->id; + *ctx = mqueue->ctx; + *p1 = mqueue->p1; + *p2 = mqueue->p2; + message_t *next = mqueue->next; + mqueue->next = mfree; + mfree = mqueue; + mqueue = next; + if (!mqueue) { + mqtail = NULL; + } + mutex_unlock (mutex); + return 0; +} + +int +messagepump_hasmessages (void) { + return mqueue ? 1 : 0; +} diff --git a/messagepump.h b/messagepump.h new file mode 100644 index 00000000..bf88d63d --- /dev/null +++ b/messagepump.h @@ -0,0 +1,12 @@ +#ifndef __MESSAGEPUMP_H +#define __MESSAGEPUMP_H + +#include + +int messagepump_init (void); +void messagepump_free (void); +int messagepump_push (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2); +int messagepump_pop (uint32_t *id, uintptr_t *ctx, uint32_t *p1, uint32_t *p2); +//int messagepump_hasmessages (void); + +#endif // __MESSAGEPUMP_H diff --git a/messages.h b/messages.h new file mode 100644 index 00000000..6a52e31d --- /dev/null +++ b/messages.h @@ -0,0 +1,14 @@ +#ifndef __MESSAGES_H +#define __MESSAGES_H + +enum { + M_SONGFINISHED, + M_NEXTSONG, + M_PREVSONG, + M_PLAYSONG, + M_STOPSONG, + M_PAUSESONG, + M_PLAYRANDOM, +}; + +#endif // __MESSAGES_H diff --git a/psdl.c b/psdl.c index 713eeaa3..3f4c92b9 100644 --- a/psdl.c +++ b/psdl.c @@ -3,6 +3,8 @@ #include "psdl.h" #include "codec.h" #include "playlist.h" +#include "messagepump.h" +#include "messages.h" static int sdl_player_numsamples = 2<<25; static int sdl_player_freq; @@ -18,6 +20,7 @@ static SRC_DATA srcdata; static int codecleft; static float sdl_volume = 1; +static float sdl_eof; int psdl_init (void) { @@ -81,6 +84,7 @@ psdl_play (struct playItem_s *it) { return -1; } codec = it->codec; + sdl_eof = 0; if (codec->init (it->fname)) { return -1; } @@ -99,6 +103,9 @@ psdl_stop (void) { int psdl_ispaused (void) { + if (!codec) { + return 0; + } return (SDL_GetAudioStatus () == SDL_AUDIO_PAUSED); } @@ -121,11 +128,18 @@ psdl_set_volume (float vol) { static void psdl_callback (void* userdata, Uint8 *stream, int len) { + if (sdl_eof) { + messagepump_push (M_SONGFINISHED, 0, 0, 0); + memset (stream, 0, len); + return; + } + + int codecret = 0; if (!codec) { memset (stream, 0, len); } else if (codec->info.samplesPerSecond == sdl_player_freq) { - codec->read (stream, len); + codecret = codec->read (stream, len); } else { int nsamples = len/4; @@ -133,7 +147,7 @@ psdl_callback (void* userdata, Uint8 *stream, int len) { // add 5 extra samples for SRC nsamples = nsamples * codec->info.samplesPerSecond / sdl_player_freq + 5; // read data at source samplerate (with some room for SRC) - codec->read (sdl_buffer, (nsamples - codecleft) * 2 * codec->info.channels); + codecret = codec->read (sdl_buffer, (nsamples - codecleft) * 2 * codec->info.channels); // convert to float, and apply soft volume int i; float *fbuffer = sdl_fbuffer + codecleft*2; @@ -165,5 +179,6 @@ psdl_callback (void* userdata, Uint8 *stream, int len) { // copy spare samples for next update memmove (sdl_fbuffer, &sdl_fbuffer[srcdata.input_frames_used*2], codecleft * 8); } + sdl_eof = codecret == -1 ? 1 : 0; } -- cgit v1.2.3