1
/***************************************************************************
4
* Tue Jul 20 15:49:24 2010
5
* Copyright 2010 Chris Debenham
6
* <chris@adebenham.com>
7
****************************************************************************/
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License as published by
12
* the Free Software Foundation; either version 2 of the License, or
13
* (at your option) any later version.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU Library General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
25
#include "lyricue_display.h"
27
extern MYSQL *lyricDb;
28
extern gchar *bible_name;
29
extern gfloat stage_width;
30
extern gfloat stage_height;
31
extern gint bg_is_video;
32
extern ClutterActor *background;
34
gint blanked_state = BLANK_NONE;
35
gchar *default_bg = NULL;
36
gchar *current_bg = NULL;
37
gchar *temp_bg = NULL;
40
GHashTable *config = NULL;
41
#define SERVER_PORT 2346
43
// Command line options
44
gboolean windowed = FALSE;
45
gboolean debugging = FALSE;
46
int server_port = SERVER_PORT;
47
gchar *dbhostname = "localhost";
48
gchar *geometry = NULL;
49
unsigned long windowid = 0;
51
static GOptionEntry entries[] = {
52
{"window", 'w', 0, G_OPTION_ARG_NONE, &windowed, "Run in a window", NULL},
53
{"remote", 'r', 0, G_OPTION_ARG_STRING, &dbhostname, "Database hostname", NULL},
54
{"geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry, "Window Geometry", NULL},
55
{"port", 'p', 0, G_OPTION_ARG_INT, &server_port, "Port to listen on", NULL},
56
{"miniview", 'm', 0, G_OPTION_ARG_INT, &windowid, "Embed in windowid", NULL},
57
{"debug", 'd', 0, G_OPTION_ARG_NONE, &debugging, "Enable debug output", NULL},
63
main (int argc, char *argv[])
65
unsetenv("LIBGL_ALWAYS_INDIRECT");
66
setenv("CLUTTER_DISABLE_MIPMAPPED_TEXT","1",0);
67
bindtextdomain (GETTEXT_PACKAGE, NULL);
68
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
69
textdomain (GETTEXT_PACKAGE);
73
GOptionContext *context;
75
context = g_option_context_new ("- Lyricue display");
76
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
77
g_option_context_set_ignore_unknown_options(context, TRUE);
78
if (!g_option_context_parse (context, &argc, &argv, &error)) {
79
g_print ("option parsing failed: %s\n", error->message);
82
int ret = db_select ();
84
// Really should handle this ;)
86
load_configuration (lyricDb);
87
bible_load ((gchar *) g_hash_table_lookup (config, "DefBible"));
91
GSocketService *service = g_socket_service_new ();
92
GInetAddress *address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
93
g_snprintf(argv[0],29,"Lyricue Display on port %04d",server_port);
94
l_debug("Process Name:%s",argv[0]);
96
if (!g_socket_listener_add_inet_port
97
(G_SOCKET_LISTENER (service), server_port, NULL, NULL)) {
98
l_debug ("Unable to listen on port %d", server_port);
101
g_object_unref (address);
102
g_socket_service_start (service);
103
g_signal_connect (service, "incoming", G_CALLBACK (new_connection), NULL);
105
// Setup tracker entry in DB
106
do_query(lyricDb, "DELETE FROM playlists WHERE id=-1");
107
do_query(lyricDb, "INSERT INTO playlists SET id=-1,ref=0,title=''");
109
ret = create_main_window (argc, argv);
116
ret = db_deselect ();
122
network_read (GIOChannel * source, GIOCondition cond, gpointer data)
124
GString *s = g_string_new (NULL);
125
GError *error = NULL;
126
GIOStatus ret = g_io_channel_read_line_string (source, s, NULL, &error);
128
if (ret == G_IO_STATUS_EOF) {
131
} else if (ret == G_IO_STATUS_ERROR) {
132
g_warning ("Error reading: %s\n", error->message);
133
// Drop last reference on connection
134
g_object_unref (data);
135
// Remove the event source
138
s->str = g_strstrip(s->str);
139
if (g_utf8_strlen (s->str, -1) > 0) {
140
handle_command (source, s->str);
142
g_io_channel_shutdown (source, TRUE, NULL);
143
g_object_unref (data);
151
new_connection (GSocketService * service,
152
GSocketConnection * connection,
153
GObject * source_object, gpointer user_data)
155
GSocketAddress *sockaddr =
156
g_socket_connection_get_remote_address (connection, NULL);
158
g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
160
g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (sockaddr));
162
l_debug ("New Connection from %s:%d", g_inet_address_to_string (addr),
165
g_object_ref (connection);
166
GSocket *socket = g_socket_connection_get_socket (connection);
168
gint fd = g_socket_get_fd (socket);
169
GIOChannel *channel = g_io_channel_unix_new (fd);
170
g_io_add_watch (channel, G_IO_IN, (GIOFunc) network_read, connection);
175
handle_command (GIOChannel * source, const char *command)
177
l_debug ("Received: %s", command);
178
update_miniview(command);
179
GString *returnstring = NULL;
180
gchar **line = g_strsplit (command, ":", 2);
181
if (line[1] != NULL) {
182
line[0] = g_utf8_strdown (line[0], -1);
183
if (g_strcmp0 (line[0], "preview") == 0) {
184
do_preview (line[1]);
185
} else if (g_strcmp0 (line[0], "status") == 0) {
186
returnstring = do_status ();
187
} else if (g_strcmp0 (line[0], "snapshot") == 0) {
189
} else if (g_strcmp0 (line[0], "reconfig") == 0) {
191
} else if (g_strcmp0 (line[0], "backdrop") == 0) {
192
do_backdrop (line[1]);
193
} else if (g_strcmp0 (line[0], "blank") == 0) {
195
} else if (g_strcmp0 (line[0], "change_to_db") == 0) {
196
do_change_to_db (line[1]);
197
} else if (g_strcmp0 (line[0], "next_point") == 0) {
198
do_next_point (line[1]);
199
} else if (g_strcmp0 (line[0], "get") == 0) {
201
} else if (g_strcmp0 (line[0], "display") == 0) {
202
do_display (line[1]);
203
} else if (g_strcmp0 (line[0], "osd") == 0) {
205
} else if (g_strcmp0 (line[0], "media") == 0) {
207
} else if (g_strcmp0 (line[0], "fade") == 0) {
209
} else if (g_strcmp0 (line[0], "blur") == 0) {
214
if (returnstring != NULL) {
215
l_debug("The status message sent is: %s",returnstring->str);
216
GIOStatus res = g_io_channel_write_chars (source, returnstring->str,
217
returnstring->len, NULL,
219
g_string_free (returnstring, TRUE);
220
if (res != G_IO_STATUS_NORMAL)
222
/* force flushing of the write buffer */
223
res = g_io_channel_flush (source, NULL);
229
do_media (const char *options)
232
gchar **line = g_strsplit (options, ":", 2);
233
if (g_ascii_strncasecmp (line[0], "pause", 5) == 0) {
235
} else if (g_ascii_strncasecmp (line[0], "skip", 4) == 0) {
236
media_skip (atoi(line[1]));
243
do_fade (const char *options)
245
fade_backdrop(atoi(options));
249
do_blur (const char *options)
251
blur_backdrop(atoi(options));
255
do_preview (const char *options)
257
gchar **line = g_strsplit (options, ":", 2);
258
gboolean wrap = TRUE;
260
if (g_strcmp0(line[0], "ignore") != 0 ) {
261
gchar **extras = g_strsplit(parse_special(line[0]), "\n", 4);
262
if ((g_strv_length(extras) == 6) && (g_strcmp0(extras[3], "nowrap") == 0)) {
265
set_headtext (parse_special(extras[0]), 0, 1);
267
if (g_strv_length(extras) >= 3) {
268
GString *footer = g_string_new (NULL);
270
if (g_utf8_strlen(extras[2],10) != 0) {
271
g_string_printf(footer, "%s %s - %s",
272
gettext("Written by "),
276
if (g_utf8_strlen(extras[1],10) != 0) {
277
g_string_printf(footer, "%s %s",
278
gettext("Written by "),
281
g_string_assign(footer,"");
284
set_foottext(footer->str,0,1);
285
g_string_free(footer, TRUE);
290
set_maintext (parse_special(line[1]), 0, wrap);
297
l_debug ("Return status");
298
GString *ret = g_string_new (NULL);
299
g_string_printf (ret, "Status,W:%.0f,H:%.0f,F:%s,B:%s\n", stage_width,
300
stage_height, (gchar *) g_hash_table_lookup (config,
309
l_debug ("do_snapshot not implemented");
315
l_debug ("do_reconfig");
316
load_configuration (lyricDb);
320
do_backdrop (const char *options)
322
l_debug ("do_backdrop: %s", options);
323
gchar **line = g_strsplit (options, ":", 2);
325
default_bg = parse_special(line[0]);
326
change_backdrop (default_bg, TRUE);
333
if (blanked_state == BLANK_BG) {
334
change_backdrop (temp_bg, TRUE);
336
blanked_state = BLANK_NONE;
340
do_blank (const char *options)
342
l_debug ("do_blank: %s", options);
343
gchar **line = g_strsplit (options, ":", 2);
344
if (strlen(options) <= 1) {
348
if (blanked_state == BLANK_BG) {
349
l_debug("Re-show text");
350
do_display("current");
351
} else if ((blanked_state == BLANK_TEXT) && options != NULL) {
352
l_debug("clear text and set BG");
353
temp_bg = current_bg;
354
change_backdrop (line[0], TRUE);
355
blanked_state = BLANK_BG;
356
} else if ((blanked_state == BLANK_TEXT) && options == NULL) {
357
l_debug("Re-show text - 2");
358
do_display("current");
359
} else if (options != NULL) {
360
l_debug("clear text and set BG - 2");
361
temp_bg = current_bg;
362
change_backdrop (line[0], TRUE);
363
set_maintext ("", 0, FALSE);
364
set_headtext ("", 0, FALSE);
365
set_foottext ("", 0, FALSE);
366
blanked_state = BLANK_BG;
368
l_debug("Clear text");
369
set_maintext ("", 0, FALSE);
370
set_headtext ("", 0, FALSE);
371
set_foottext ("", 0, FALSE);
372
blanked_state = BLANK_TEXT;
378
do_change_to_db (const char *options)
380
l_debug ("do_change_to_db: %s", options);
381
bible_load (options);
385
do_next_point (const char *options)
387
l_debug ("do_next_point not implemented");
391
do_get (const char *options)
393
l_debug ("do_get not implemented");
397
do_osd (const char *options)
400
if (options != NULL) {
401
gchar **line = g_strsplit (options, ":", 2);
403
if(g_strcmp0(line[0],"slow") == 0) {
405
} else if(g_strcmp0(line[0],"fast") == 0) {
407
} else if(g_strcmp0(line[0],"default") == 0) {
410
speed = atoi(line[0]);
412
gchar *text = parse_special(line[1]);
413
set_osd(speed, text);
418
do_display (const char *options)
420
l_debug ("do_display");
421
if (options != NULL) {
422
gchar **line = g_strsplit (options, ":", 2);
426
gboolean bg_changed = FALSE;
427
do_query (lyricDb, "SELECT playlist FROM playlist WHERE playorder=%d",
429
result = mysql_store_result (lyricDb);
430
row = mysql_fetch_row (result);
432
current_list = atoi (row[0]);
434
mysql_free_result (result);
436
gchar *command = g_utf8_strdown (line[0], -1);
438
if (g_strcmp0 (command, "playlist") == 0) {
439
current_list = atoi (line[1]);
441
} else if (g_strcmp0 (command, "current") == 0) {
442
// Clear text and then redisplay same page
443
set_maintext ("", 0, FALSE);
444
set_headtext ("", 0, FALSE);
445
set_foottext ("", 0, FALSE);
446
if (g_strcmp0(line[1], "nobg") == 0) {
449
} else if (g_strcmp0 (command, "next_page") == 0) {
451
"SELECT MIN(playorder) FROM playlist WHERE playlist=%d AND playorder > %d ORDER BY playorder",
452
current_list, current_item);
453
result = mysql_store_result (lyricDb);
454
row = mysql_fetch_row (result);
455
mysql_free_result (result);
458
current_item = atoi (row[0]);
460
// End of song reached
461
gchar **loop = g_strsplit (line[1], ";", 2);
462
if (g_strcmp0(loop[0], "loop") == 0) {
465
if (loop[1] != NULL) {
466
loop_parent = atoi(loop[1]);
468
if (loop_parent == 0) {
469
l_debug ("Looping a song, back to page 1");
471
"SELECT MIN(playorder) FROM playlist WHERE playlist=%d",
473
result = mysql_store_result (lyricDb);
474
row = mysql_fetch_row (result);
475
mysql_free_result (result);
476
if (row[0] != NULL) {
477
current_item = atoi (row[0]);
480
l_debug ("Looping a sublist");
482
"SELECT MIN(p1.playorder) FROM playlist AS p1, playlist AS p2 WHERE p1.playorder>p2.playorder AND p2.type='play' AND p2.data=%d AND p1.playlist=%d", current_list, loop_parent);
483
result = mysql_store_result (lyricDb);
484
row = mysql_fetch_row (result);
485
mysql_free_result (result);
486
if (row[0] != NULL) {
487
current_item = atoi (row[0]);
489
// Loop back to top of parent
491
"SELECT MIN(playorder) FROM playlist WHERE playlist=%d",
493
result = mysql_store_result (lyricDb);
494
row = mysql_fetch_row (result);
495
mysql_free_result (result);
496
if (row[0] != NULL) {
497
current_item = atoi (row[0]);
503
do_display ("next_song:0");
508
} else if (g_strcmp0 (command, "prev_page") == 0) {
510
"SELECT MAX(playorder) FROM playlist WHERE playlist=%d AND playorder < %d ORDER BY playorder",
511
current_list, current_item);
512
result = mysql_store_result (lyricDb);
513
row = mysql_fetch_row (result);
514
mysql_free_result (result);
516
current_item = atoi (row[0]);
518
if (g_strcmp0 (line[1], "loop")) {
520
"SELECT MAX(playorder) FROM playlist WHERE playlist=%d",
522
result = mysql_store_result (lyricDb);
523
row = mysql_fetch_row (result);
524
mysql_free_result (result);
525
if (row[0] != NULL) {
526
current_item = atoi (row[0]);
531
} else if (g_strcmp0 (command, "next_song") == 0) {
533
"SELECT a.playorder,a.playlist FROM playlist AS a, playlist AS b WHERE a.data=b.playlist AND a.type=\"play\" AND b.playorder=%d",
535
result = mysql_store_result (lyricDb);
536
row = mysql_fetch_row (result);
537
mysql_free_result (result);
539
if (row && (row[0] != NULL)) {
540
current_item = atoi (row[0]);
541
current_list = atoi (row[1]);
544
"SELECT MIN(playorder) FROM playlist WHERE playorder > %d AND playlist=%d",
545
current_item, current_list);
546
result = mysql_store_result (lyricDb);
547
row = mysql_fetch_row (result);
548
mysql_free_result (result);
549
if (row[0] != NULL) {
550
current_item = atoi (row[0]);
553
} else if (g_strcmp0 (command, "prev_song") == 0) {
555
"SELECT a.playorder,a.playlist FROM playlist AS a, playlist AS b WHERE a.data=b.playlist AND a.type=\"play\" AND b.playorder=%d",
557
result = mysql_store_result (lyricDb);
558
row = mysql_fetch_row (result);
559
mysql_free_result (result);
560
if (row && (row[0] != NULL)) {
561
current_item = atoi (row[0]);
562
current_list = atoi (row[1]);
565
"SELECT MAX(playorder) FROM playlist WHERE playorder < %d AND playlist=%d",
566
current_item, current_list);
567
result = mysql_store_result (lyricDb);
568
row = mysql_fetch_row (result);
569
mysql_free_result (result);
570
if (row[0] != NULL) {
571
current_item = atoi (row[0]);
574
} else if (g_strcmp0 (command, "page") == 0) {
576
"SELECT playorder FROM playlist WHERE playlist=%d",
578
result = mysql_store_result (lyricDb);
580
while ((count < atoi (line[1]))
581
&& (row = mysql_fetch_row (result))) {
584
if (row && (row[0] != NULL)) {
585
current_item = atoi (row[0]);
588
current_item = atoi (command);
592
"SELECT type,data,transition FROM playlist WHERE playorder=%d",
594
result = mysql_store_result (lyricDb);
595
row = mysql_fetch_row (result);
597
gchar *type = g_strdup (row[0]);
598
gchar *data = g_strdup (row[1]);
602
gboolean wrap = TRUE;
603
int transition = atoi (row[2]);
605
if (g_strcmp0 (type, "back") == 0) {
606
default_bg = g_strdup(data);
607
change_backdrop (default_bg, TRUE);
610
} else if (g_strcmp0 (type, "file") == 0) {
611
change_backdrop (data, FALSE);
614
} else if (g_strcmp0 (type, "imag") == 0) {
615
change_backdrop (data, FALSE);
617
} else if (g_strcmp0 (type, "vers") == 0) {
619
"SELECT title FROM playlist,playlists WHERE playlist.playlist=playlists.id AND playorder=%d",
621
result = mysql_store_result (lyricDb);
622
row = mysql_fetch_row (result);
623
gchar **selected = g_strsplit (data, ":", 2);
625
if (selected[1] == NULL) {
626
selected = g_strsplit (row[0], ":", 3);
627
gchar **datasplt = g_strsplit (data, "-", 2);
628
GString *verseref = g_string_new (NULL);
629
g_string_printf (verseref, "%s:%s:%s-%s:%s", selected[0],
630
selected[1], datasplt[0], selected[1],
632
verse = do_grab_verse (g_string_free (verseref, FALSE));
638
lyrics = g_strdup(verse);
640
header = g_strdup(row[0]);
641
footer = g_strdup(bible_name);
643
} else if ((g_strcmp0 (type, "play") == 0) ||
644
(g_strcmp0 (type, "sub") == 0)) {
646
"SELECT playorder FROM playlist WHERE playlist=%s ORDER BY playorder",
648
result = mysql_store_result (lyricDb);
649
row = mysql_fetch_row (result);
652
} else { // Song page
654
"SELECT title,artist,lyrics,copyright FROM lyricMain AS l, page AS pa WHERE pa.songid=l.id AND pa.pageid=%s",
656
result = mysql_store_result (lyricDb);
657
row = mysql_fetch_row (result);
658
mysql_free_result (result);
660
gchar *title = g_strdup (row[0]);
661
gchar *artist = g_strdup (row[1]);
662
gchar *lyrictmp = g_strdup (row[2]);
663
gchar *copyright = g_strdup (row[3]);
664
GString *foot = g_string_new (NULL);
665
if (g_utf8_strlen (artist, 10) != 0) {
666
g_string_printf (foot, "Written by %s", artist);
668
if (g_utf8_strlen (copyright, 10) != 0) {
669
g_string_append_printf (foot, " - %s", copyright);
671
lyrics = g_strdup(lyrictmp);
672
header = g_strdup(title);
673
footer = g_strdup(foot->str);
674
g_string_free (foot, TRUE);
678
// Look for associated background image
680
int res = do_query (lyricDb,
681
"SELECT imagename FROM associations WHERE playlist=%d",
683
int bg_changed = FALSE;
685
result = mysql_store_result (lyricDb);
686
row = mysql_fetch_row (result);
688
change_backdrop (row[0], TRUE);
691
mysql_free_result (result);
696
"SELECT a.imagename,q.data FROM associations as a, playlist AS p, playlist AS q WHERE p.type='play' AND p.data=q.playlist and a.playlist=p.playorder AND q.playorder=%d",
699
result = mysql_store_result (lyricDb);
700
row = mysql_fetch_row (result);
701
mysql_free_result (result);
703
change_backdrop (row[0], TRUE);
708
if (!bg_changed && (g_strcmp0(default_bg, current_bg) != 0)) {
709
l_debug("Reset bg to default");
710
change_backdrop(default_bg, TRUE);
714
set_maintext(parse_special(lyrics), transition, wrap);
715
set_headtext(parse_special(header), transition, wrap);
716
set_foottext(parse_special(footer), transition, wrap);
726
//l_debug ("Updating tracker");
728
// Only do if this is main server
729
if (server_port == SERVER_PORT) {
730
GString *title = g_string_new (NULL);
731
if (blanked_state == BLANK_BG) {
732
g_string_assign (title, "blank_bg");
733
} else if (blanked_state == BLANK_TEXT) {
734
g_string_assign (title, "blank_text");
737
g_string_append_printf (title, "%.0f;%.0f;%d",
738
clutter_media_get_progress (CLUTTER_MEDIA
741
clutter_media_get_duration (CLUTTER_MEDIA
743
clutter_media_get_duration (CLUTTER_MEDIA
745
clutter_media_get_playing (CLUTTER_MEDIA
748
g_string_append (title, "0;0;0");
751
"UPDATE playlists SET ref = %d, title = \"%s\" WHERE id=-1",
752
current_item, g_string_free (title, FALSE));
758
update_miniview (const char *command)
760
if (server_port == SERVER_PORT) {
761
l_debug("miniview time");
762
GSocketClient *client = g_socket_client_new();
763
GSocketConnection *conn = g_socket_client_connect_to_host ( client, dbhostname, 2348, NULL, NULL);
765
GOutputStream *out = g_io_stream_get_output_stream (G_IO_STREAM(conn));
766
g_output_stream_write(out, command, strlen(command), NULL, NULL);
767
g_object_unref(conn);
769
g_object_unref(client);