rendered paste body#include <gtk/gtk.h> // For gtk related stuff
#include <curl/curl.h> // The engine for retriving http data
#include <string.h> // Generic string manipulation functions (str*)
#include <stdlib.h> // For malloc and some other useful functions
#include <pcre.h> // For the regexp to get the song names from the http data
#include <unistd.h> // for getenv (get environmental variable)
#include <xosd.h>
char *recData = NULL; // The storage of http data
xosd *osd = NULL;
char *font = "-adobe-helvetica-bold-r-normal-*-*-320-*-*-p-*-iso8859-1";
char *curSong = NULL;
typedef struct _config
{
int channel; // The current channel of interest
char update; // Whether we pop up a splash telling updates
}config;
typedef struct _channelWin
{
GtkWidget *window;
GtkWidget *channelSpin;
GtkWidget *ok;
}channelWin;
channelWin chWin;
config mainConfig;
// Curl launches this function when data is available
static int writer(char *data, size_t size, size_t nmemb, char *buffer)
{
strcat(recData,data);
return size*nmemb; // Return size read
}
void updateShow()
{
xosd_display (osd, 0, XOSD_string, "Song:");
xosd_display (osd, 1, XOSD_string, curSong);
xosd_set_timeout(osd, 3);
}
void findSongNames()
{
int offset;
const char *error = malloc(128);
pcre *reg = pcre_compile("Array\\(([0-9]+),\"(.+?)\",\"(.+?)\"",0,&error,&offset,NULL);
char currSong[255];
int ovector[30];
ovector[2] = 0;
while(pcre_exec(reg, NULL, recData, strlen(recData), ovector[2],0, ovector, 30)>1)
{
char *chan = strdup(recData+ovector[2]);
chan[ovector[3]-ovector[2]]='\0';
int chann = atoi(chan);
if(chann==mainConfig.channel)
{
int i = 4;
for(;i<8;i+=2)
{
int k;
for(k=ovector[i];k<ovector[i+1];k++)
{
sprintf(currSong,"%s%c",currSong,recData[k]);
}
if(i!=6)
sprintf(currSong,"%s - ",currSong);
}
printf("\n");
}
}
if(curSong==NULL)
{
curSong = malloc(255);
}
if(strcmp(curSong,currSong)!=0)
{
strcpy(curSong,currSong);
updateShow();
}
}
gboolean updateListing(gpointer user_data)
{
char url[] = "http://www.siriusbackstage.com:8999/itson/streams.php";
recData = malloc(10240); // 10 kbytes for html data
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_HEADER, 0);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
res = curl_easy_perform(curl);
}
findSongNames();
return 1;
}
gboolean closeConfigWindow(GtkWidget *win, gpointer user_data)
{
gtk_widget_hide(win);
return 1;
}
static void onMenu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
{
GtkMenu *pmenu = (GtkMenu*)gtk_menu_new();
GtkWidget *menuItem = gtk_menu_item_new_with_label("Change Channel");
gtk_menu_append(GTK_MENU(pmenu), menuItem);
g_signal_connect_swapped(G_OBJECT(menuItem),"activate",G_CALLBACK(gtk_widget_show_all),chWin.window);
menuItem = gtk_menu_item_new_with_label("Exit");
gtk_menu_append(GTK_MENU(pmenu), menuItem);
g_signal_connect_swapped(G_OBJECT(menuItem),"activate",G_CALLBACK(gtk_exit),0);
gtk_widget_show_all((GtkWidget*)pmenu);
gtk_menu_popup(pmenu,NULL,NULL,NULL,NULL,button,activate_time);
}
void saveConfig(GtkWidget *widg, gpointer user_data)
{
char confFileP[255];
strcpy(confFileP,getenv("HOME"));
strcat(confFileP,"/.sirius");
FILE *confFile = fopen(confFileP,"w");
fwrite(&mainConfig,1,sizeof(config),confFile);
fclose(confFile);
updateListing(NULL);
gtk_widget_hide(chWin.window);
}
void onSpin(GtkWidget *sbutton, gpointer user_data)
{
mainConfig.channel = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sbutton));
}
int main(int argc, char ** argv)
{
osd = xosd_create(2);
char confFileP[255];
xosd_set_font (osd, font);
xosd_set_shadow_offset (osd, 2);
xosd_set_colour (osd, "lawngreen");
xosd_set_pos (osd, XOSD_bottom);
xosd_set_vertical_offset (osd, 48);
xosd_set_align (osd, XOSD_left);
xosd_set_horizontal_offset (osd, 48);
strcpy(confFileP,getenv("HOME"));
strcat(confFileP,"/.sirius");
FILE *confFile = fopen(confFileP,"r");
if(confFile !=NULL)
{
fread(&mainConfig,1,sizeof(config),confFile); // Read config
fclose(confFile);
}else{
mainConfig.channel = 1;
mainConfig.update = 1;
// Set default config and write it's ass!
confFile = fopen(confFileP,"w");
fwrite(&mainConfig,1,sizeof(config),confFile);
fclose(confFile);
}
gtk_init(&argc, &argv);
chWin.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
chWin.channelSpin = gtk_spin_button_new_with_range(1,198,1);
GtkWidget *vbox = gtk_vbox_new(0,0);
GtkWidget *hbox = gtk_hbox_new(0,0);
GtkWidget *hbox2 = gtk_hbox_new(0,0);
chWin.ok = gtk_button_new_with_label("Ok");
gtk_container_add(GTK_CONTAINER(chWin.window),vbox);
gtk_box_pack_start(GTK_BOX(vbox),hbox,0,0,0);
gtk_box_pack_start(GTK_BOX(vbox),hbox2,0,0,0);
gtk_box_pack_start(GTK_BOX(hbox),gtk_label_new("Select a channel:"),0,0,0);
gtk_box_pack_start(GTK_BOX(hbox),chWin.channelSpin,0,0,0);
gtk_box_pack_start(GTK_BOX(hbox2),chWin.ok,0,0,0);
GtkStatusIcon *sysIcon; //Icon that will sit in the system tray
sysIcon = gtk_status_icon_new_from_file("radio.xpm");
g_signal_connect(G_OBJECT(sysIcon),"popup-menu",G_CALLBACK(onMenu),NULL);
g_signal_connect(G_OBJECT(chWin.channelSpin),"value-changed",G_CALLBACK(onSpin),NULL);
g_signal_connect(G_OBJECT(chWin.window),"delete-event",G_CALLBACK(closeConfigWindow),NULL);
g_signal_connect(G_OBJECT(chWin.ok),"clicked",G_CALLBACK(saveConfig),NULL);
g_timeout_add(10000,(GSourceFunc)updateListing,NULL); //Update list ever 2 minutes
updateListing(NULL);
gtk_main();
return 0;
}