summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamfile4
-rw-r--r--callbacks.c245
-rw-r--r--cmp3.c62
-rw-r--r--codec.h2
-rw-r--r--common.h7
-rw-r--r--gtkplaylist.c299
-rw-r--r--gtkplaylist.h40
-rw-r--r--main.c35
-rw-r--r--messagepump.c99
-rw-r--r--messagepump.h12
-rw-r--r--messages.h14
-rw-r--r--psdl.c19
12 files changed, 579 insertions, 259 deletions
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 <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include <stdlib.h>
+#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 <gtk/gtk.h>
+#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 <stdio.h>
+#include <string.h>
+#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 <stdint.h>
+
+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;
}