568 lines
14 KiB
C
568 lines
14 KiB
C
/* blccc - Blinkenlights Chaos Control Center
|
|
*
|
|
* Copyright (c) 2001-2002 Sven Neumann <sven@gimp.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <blib/blib.h>
|
|
|
|
#include "bltypes.h"
|
|
|
|
#include "blmarshal.h"
|
|
#include "blconfig.h"
|
|
#include "bllogger.h"
|
|
#include "blplaylist.h"
|
|
#include "blplaylistitem.h"
|
|
#include "bltheater.h"
|
|
|
|
|
|
enum
|
|
{
|
|
ITEM_FINISHED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static void bl_theater_class_init (BlTheaterClass *klass);
|
|
static void bl_theater_init (BlTheater *view);
|
|
static void bl_theater_finalize (GObject *object);
|
|
|
|
static void bl_theater_set_item_internal (BlTheater *theater,
|
|
BlPlaylistItem *item);
|
|
static void bl_theater_set_frame_data_internal (BlTheater *theater,
|
|
const guchar *data);
|
|
static void bl_theater_item_finished (BlTheater *theater,
|
|
gboolean relax);
|
|
static void bl_theater_stop_callback (BModule *module,
|
|
BlTheater *theater);
|
|
|
|
|
|
static guint bl_theater_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
|
|
|
|
GType
|
|
bl_theater_get_type (void)
|
|
{
|
|
static GType theater_type = 0;
|
|
|
|
if (!theater_type)
|
|
{
|
|
static const GTypeInfo theater_info =
|
|
{
|
|
sizeof (BlTheaterClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) bl_theater_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (BlTheater),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) bl_theater_init,
|
|
};
|
|
|
|
theater_type = g_type_register_static (G_TYPE_OBJECT,
|
|
"BlTheater",
|
|
&theater_info, 0);
|
|
}
|
|
|
|
return theater_type;
|
|
}
|
|
|
|
static void
|
|
bl_theater_class_init (BlTheaterClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
bl_theater_signals[ITEM_FINISHED] =
|
|
g_signal_new ("item_finished",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (BlTheaterClass, item_finished),
|
|
NULL, NULL,
|
|
bl_marshal_VOID__POINTER_BOOLEAN,
|
|
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
|
|
|
|
object_class->finalize = bl_theater_finalize;
|
|
|
|
klass->item_finished = NULL;
|
|
}
|
|
|
|
static void
|
|
bl_theater_init (BlTheater *theater)
|
|
{
|
|
theater->width = 0;
|
|
theater->height = 0;
|
|
|
|
theater->stop_signal_id = 0;
|
|
|
|
theater->item = NULL;
|
|
theater->item_stack = NULL;
|
|
|
|
theater->effects = NULL;
|
|
theater->sender = b_sender_new ();
|
|
theater->item_buffer = NULL;
|
|
theater->frame_buffer = NULL;
|
|
}
|
|
|
|
static void
|
|
bl_theater_finalize (GObject *object)
|
|
{
|
|
BlTheater *theater;
|
|
|
|
theater = BL_THEATER (object);
|
|
|
|
bl_theater_pause (theater);
|
|
|
|
if (theater->item)
|
|
g_object_unref (theater->item);
|
|
|
|
if (theater->item_stack)
|
|
{
|
|
g_list_foreach (theater->item_stack, (GFunc) g_object_unref, NULL);
|
|
g_list_free (theater->item_stack);
|
|
}
|
|
|
|
if (theater->effects)
|
|
g_object_unref (theater->effects);
|
|
|
|
if (theater->sender)
|
|
g_object_unref (theater->sender);
|
|
|
|
if (theater->item_buffer)
|
|
g_free (theater->item_buffer);
|
|
|
|
if (theater->frame_buffer)
|
|
g_free (theater->frame_buffer);
|
|
|
|
if (theater->logger)
|
|
{
|
|
bl_logger_stop (theater->logger);
|
|
g_object_unref (theater->logger);
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
void
|
|
bl_theater_play (BlTheater *theater)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
|
|
if (! theater->stop_signal_id && theater->item)
|
|
{
|
|
theater->stop_signal_id =
|
|
g_signal_connect (G_OBJECT (theater->item->module), "stop",
|
|
G_CALLBACK (bl_theater_stop_callback),
|
|
theater);
|
|
|
|
b_module_start (theater->item->module);
|
|
|
|
if (theater->logger)
|
|
bl_logger_start_module (theater->logger, theater->item->module);
|
|
|
|
{
|
|
gchar *desc;
|
|
|
|
desc = bl_playlist_item_describe (theater->item);
|
|
g_print ("started item: %s\n", desc);
|
|
g_free (desc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
bl_theater_pause (BlTheater *theater)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
|
|
if (theater->stop_signal_id && theater->item)
|
|
{
|
|
g_signal_handler_disconnect (G_OBJECT (theater->item->module),
|
|
theater->stop_signal_id);
|
|
theater->stop_signal_id = 0;
|
|
|
|
b_module_stop (theater->item->module);
|
|
}
|
|
}
|
|
|
|
void
|
|
bl_theater_finish (BlTheater *theater)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
|
|
if (theater->item)
|
|
{
|
|
bl_theater_pause (theater);
|
|
bl_theater_item_finished (theater, TRUE);
|
|
}
|
|
}
|
|
|
|
void
|
|
bl_theater_kill (BlTheater *theater)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
|
|
bl_theater_pause (theater);
|
|
bl_theater_set_frame_data (theater, NULL);
|
|
|
|
if (theater->logger)
|
|
bl_logger_stop (theater->logger);
|
|
}
|
|
|
|
gboolean
|
|
bl_theater_is_playing (BlTheater *theater)
|
|
{
|
|
g_return_val_if_fail (BL_IS_THEATER (theater), FALSE);
|
|
|
|
return (theater->stop_signal_id > 0);
|
|
}
|
|
|
|
void
|
|
bl_theater_event (BlTheater *theater,
|
|
BModuleEvent *event)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
g_return_if_fail (event != NULL);
|
|
|
|
if (theater->item)
|
|
b_module_event (theater->item->module, event);
|
|
}
|
|
|
|
void
|
|
bl_theater_set_frame_data (BlTheater *theater,
|
|
const guchar *data)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
|
|
if (theater->stop_signal_id)
|
|
g_warning ("Someone tried to set frame data while a movie is running.");
|
|
else
|
|
bl_theater_set_frame_data_internal (theater, data);
|
|
}
|
|
|
|
BlTheater *
|
|
bl_theater_new (BlConfig *config)
|
|
{
|
|
BlTheater *theater;
|
|
GList *list;
|
|
GError *error = NULL;
|
|
gint added_recipients = 0;
|
|
|
|
g_return_val_if_fail (BL_IS_CONFIG (config), NULL);
|
|
|
|
theater = BL_THEATER (g_object_new (BL_TYPE_THEATER, NULL));
|
|
|
|
theater->width = config->width;
|
|
theater->height = config->height;
|
|
|
|
if (! b_sender_configure (theater->sender,
|
|
config->width, config->height,
|
|
config->channels, config->maxval))
|
|
{
|
|
g_object_unref (G_OBJECT (theater));
|
|
return NULL;
|
|
}
|
|
|
|
for (list = config->recipients; list; list = list->next)
|
|
{
|
|
if (! bl_theater_add_recipient (theater, list->data, &error))
|
|
{
|
|
g_printerr ("Failed to add recipient: %s\n", error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
else
|
|
{
|
|
added_recipients++;
|
|
}
|
|
}
|
|
|
|
if (added_recipients == 0)
|
|
g_printerr ("No initial recipients, starting anyway ...\n");
|
|
else
|
|
g_printerr ("Sending to %d recipients ...\n", added_recipients);
|
|
|
|
theater->item_buffer = g_new0 (guchar, theater->width * theater->height);
|
|
theater->frame_buffer = g_new0 (guchar, theater->width * theater->height);
|
|
|
|
if (config->logfile)
|
|
theater->logger = bl_logger_new_from_file (config->logfile, NULL);
|
|
|
|
return theater;
|
|
}
|
|
|
|
gboolean
|
|
bl_theater_add_recipient (BlTheater *theater,
|
|
const gchar *hostname,
|
|
GError **error)
|
|
{
|
|
gchar *host;
|
|
gchar *colon;
|
|
gint port = MCU_LISTENER_PORT;
|
|
gboolean retval;
|
|
|
|
g_return_val_if_fail (BL_IS_THEATER (theater), FALSE);
|
|
g_return_val_if_fail (hostname != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
host = g_strdup (hostname);
|
|
|
|
if ((colon = strrchr (host, ':')))
|
|
{
|
|
b_parse_int (colon + 1, &port);
|
|
*colon = '\0';
|
|
}
|
|
|
|
retval = b_sender_add_recipient (theater->sender, host, port, error);
|
|
|
|
g_free (host);
|
|
|
|
return retval;
|
|
}
|
|
|
|
gboolean
|
|
bl_theater_remove_recipient (BlTheater *theater,
|
|
const gchar *hostname,
|
|
GError **error)
|
|
{
|
|
gchar *host;
|
|
gchar *colon;
|
|
gint port = MCU_LISTENER_PORT;
|
|
gboolean retval;
|
|
|
|
g_return_val_if_fail (BL_IS_THEATER (theater), FALSE);
|
|
g_return_val_if_fail (hostname != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
host = g_strdup (hostname);
|
|
|
|
if ((colon = strrchr (host, ':')))
|
|
{
|
|
b_parse_int (colon + 1, &port);
|
|
*colon = '\0';
|
|
}
|
|
|
|
retval = b_sender_remove_recipient (theater->sender, host, port, error);
|
|
|
|
g_free (host);
|
|
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
bl_theater_set_effects (BlTheater *theater,
|
|
BEffects *effects)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
g_return_if_fail (! effects || B_IS_EFFECTS (effects));
|
|
|
|
if (theater->effects)
|
|
g_object_unref (theater->effects);
|
|
|
|
theater->effects = effects;
|
|
|
|
if (theater->effects)
|
|
g_object_ref (theater->effects);
|
|
}
|
|
|
|
void
|
|
bl_theater_set_item (BlTheater *theater,
|
|
BlPlaylistItem *item)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
|
|
if (item)
|
|
g_return_if_fail (B_IS_MODULE (item->module) &&
|
|
item->module->width == theater->width &&
|
|
item->module->height == theater->height);
|
|
|
|
if (theater->item)
|
|
{
|
|
bl_theater_pause (theater);
|
|
bl_theater_item_finished (theater, TRUE);
|
|
}
|
|
|
|
if (item)
|
|
bl_theater_set_item_internal (theater, item);
|
|
}
|
|
|
|
void
|
|
bl_theater_push_item (BlTheater *theater,
|
|
BlPlaylistItem *item)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
g_return_if_fail (BL_IS_PLAYLIST_ITEM (item));
|
|
g_return_if_fail (B_IS_MODULE (item->module) &&
|
|
item->module->width == theater->width &&
|
|
item->module->height == theater->height);
|
|
|
|
bl_theater_pause (theater);
|
|
|
|
if (theater->item)
|
|
theater->item_stack = g_list_prepend (theater->item_stack,
|
|
theater->item);
|
|
|
|
bl_theater_set_item_internal (theater, item);
|
|
}
|
|
|
|
void
|
|
bl_theater_pop_item (BlTheater *theater)
|
|
{
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
|
|
if (theater->item_stack)
|
|
{
|
|
bl_theater_pause (theater);
|
|
|
|
if (theater->item)
|
|
g_object_unref (G_OBJECT (theater->item));
|
|
|
|
theater->item = theater->item_stack->data;
|
|
|
|
theater->item_stack = g_list_remove (theater->item_stack,
|
|
theater->item);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
bl_theater_paint_callback (BModule *module,
|
|
guchar *buffer,
|
|
gpointer data)
|
|
{
|
|
BlTheater *theater;
|
|
|
|
g_return_val_if_fail (B_IS_MODULE (module), FALSE);
|
|
g_return_val_if_fail (BL_IS_THEATER (data), FALSE);
|
|
|
|
theater = BL_THEATER (data);
|
|
|
|
if (theater->item && theater->item->effects)
|
|
{
|
|
memcpy (theater->item_buffer, buffer, theater->width * theater->height);
|
|
|
|
b_effects_apply (theater->item->effects,
|
|
theater->item_buffer,
|
|
theater->width, theater->height, 1, 255);
|
|
|
|
buffer = theater->item_buffer;
|
|
}
|
|
|
|
bl_theater_set_frame_data_internal (BL_THEATER (data), buffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* private functions */
|
|
|
|
static void
|
|
bl_theater_set_item_internal (BlTheater *theater,
|
|
BlPlaylistItem *item)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
g_return_if_fail (BL_IS_THEATER (theater));
|
|
g_return_if_fail (BL_IS_PLAYLIST_ITEM (item));
|
|
g_return_if_fail (B_IS_MODULE (item->module) &&
|
|
item->module->width == theater->width &&
|
|
item->module->height == theater->height);
|
|
|
|
theater->item = g_object_ref (G_OBJECT (item));
|
|
|
|
if (!b_module_prepare (item->module, &error))
|
|
{
|
|
gchar *title;
|
|
|
|
if (!error)
|
|
{
|
|
g_warning ("b_module_prepare didn't return an error ??\n");
|
|
}
|
|
else
|
|
{
|
|
b_module_describe (item->module, &title, NULL, NULL);
|
|
g_printerr ("Couldn't prepare module '%s': %s\n", title, error->message);
|
|
g_free (title);
|
|
}
|
|
|
|
bl_theater_item_finished (theater, FALSE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
bl_theater_set_frame_data_internal (BlTheater *theater,
|
|
const guchar *data)
|
|
{
|
|
if (data && theater->effects)
|
|
{
|
|
memcpy (theater->frame_buffer, data, theater->width * theater->height);
|
|
|
|
b_effects_apply (theater->effects,
|
|
theater->frame_buffer,
|
|
theater->width, theater->height, 1, 255);
|
|
|
|
data = (const gchar *) theater->frame_buffer;
|
|
}
|
|
|
|
b_sender_send_frame (theater->sender, data);
|
|
}
|
|
|
|
static void
|
|
bl_theater_item_finished (BlTheater *theater,
|
|
gboolean relax)
|
|
{
|
|
BlPlaylistItem *item;
|
|
gboolean pushed;
|
|
|
|
if (! theater->item)
|
|
return;
|
|
|
|
item = theater->item;
|
|
|
|
if (relax)
|
|
b_module_relax (item->module);
|
|
|
|
theater->item = NULL;
|
|
|
|
pushed = (theater->item_stack != NULL);
|
|
|
|
g_signal_emit (G_OBJECT (theater),
|
|
bl_theater_signals[ITEM_FINISHED], 0,
|
|
item, pushed);
|
|
|
|
g_object_unref (G_OBJECT (item));
|
|
}
|
|
|
|
static void
|
|
bl_theater_stop_callback (BModule *module,
|
|
BlTheater *theater)
|
|
{
|
|
if (theater->stop_signal_id)
|
|
{
|
|
g_signal_handler_disconnect (G_OBJECT (theater->item->module),
|
|
theater->stop_signal_id);
|
|
theater->stop_signal_id = 0;
|
|
}
|
|
|
|
bl_theater_item_finished (theater, TRUE);
|
|
}
|