aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2012-09-28 15:26:29 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2012-09-28 15:26:29 +0200
commite7da86053b3df2882816b0df8089e1a51b61939f (patch)
tree8f7430f7d1e82ddb8449548886f57a3a7c646c05 /src
parent9b134ea457f91ba8ea6ae558c9192f58c09f62bc (diff)
downloadosc-graphics-e7da86053b3df2882816b0df8089e1a51b61939f.tar.gz
autotools based build system
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am9
-rw-r--r--src/layer.cpp72
-rw-r--r--src/layer.h125
-rw-r--r--src/layer_box.cpp47
-rw-r--r--src/layer_box.h56
-rw-r--r--src/layer_image.cpp170
-rw-r--r--src/layer_image.h58
-rw-r--r--src/layer_video.cpp285
-rw-r--r--src/layer_video.h92
-rw-r--r--src/main.cpp231
-rw-r--r--src/osc_graphics.h46
-rw-r--r--src/osc_server.cpp198
-rw-r--r--src/osc_server.h105
13 files changed, 1494 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..cae6a7c
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,9 @@
+AC_CXXFLAGS = -Wall
+
+bin_PROGRAMS = osc-graphics
+osc_graphics_SOURCES = main.cpp osc_graphics.h \
+ osc_server.cpp osc_server.h \
+ layer.cpp layer.h \
+ layer_box.cpp layer_box.h \
+ layer_image.cpp layer_image.h \
+ layer_video.cpp layer_video.h
diff --git a/src/layer.cpp b/src/layer.cpp
new file mode 100644
index 0000000..fdb3a3d
--- /dev/null
+++ b/src/layer.cpp
@@ -0,0 +1,72 @@
+#include <string.h>
+#include <bsd/sys/queue.h>
+
+#include <SDL.h>
+
+#include "osc_graphics.h"
+#include "osc_server.h"
+#include "layer.h"
+
+Layer::Layer(const char *_name) : mutex(SDL_CreateMutex()), name(strdup(_name))
+{
+ geo_osc_id = register_method("geo", GEO_TYPES, geo_osc);
+ alpha_osc_id = register_method("alpha", "f", alpha_osc);
+}
+
+void
+LayerList::insert(int pos, Layer *layer)
+{
+ Layer *cur, *prev = NULL;
+
+ lock();
+
+ LIST_FOREACH(cur, &head, layers) {
+ if (!pos--)
+ break;
+ prev = cur;
+ }
+
+ if (prev)
+ LIST_INSERT_AFTER(prev, layer, layers);
+ else
+ LIST_INSERT_HEAD(&head, layer, layers);
+
+ unlock();
+}
+
+void
+LayerList::delete_layer(Layer *layer)
+{
+ lock();
+ LIST_REMOVE(layer, layers);
+ unlock();
+
+ /* layer is guaranteed not to be rendered */
+ delete layer;
+}
+
+void
+LayerList::render(SDL_Surface *target)
+{
+ SDL_FillRect(target, NULL, SDL_MapRGB(target->format, 0, 0, 0));
+
+ lock();
+
+ Layer *cur;
+ LIST_FOREACH(cur, &head, layers) {
+ cur->lock();
+ cur->frame(target);
+ cur->unlock();
+ }
+
+ unlock();
+}
+
+Layer::~Layer()
+{
+ unregister_method(alpha_osc_id);
+ unregister_method(geo_osc_id);
+
+ free(name);
+ SDL_DestroyMutex(mutex);
+}
diff --git a/src/layer.h b/src/layer.h
new file mode 100644
index 0000000..21fab50
--- /dev/null
+++ b/src/layer.h
@@ -0,0 +1,125 @@
+#ifndef __HAVE_LAYER_H
+#define __HAVE_LAYER_H
+
+#include <string.h>
+#include <bsd/sys/queue.h>
+
+#include <SDL.h>
+#include <SDL_thread.h>
+
+#include <lo/lo.h>
+
+#include "osc_graphics.h"
+#include "osc_server.h"
+
+extern OSCServer osc_server;
+
+class Layer {
+ SDL_mutex *mutex;
+
+public:
+ /*
+ * Every derived class must have a static CtorInfo struct "ctor_info"
+ * and a static "ctor_osc" method
+ */
+ struct CtorInfo {
+ const char *name;
+ const char *types;
+ };
+
+ LIST_ENTRY(Layer) layers;
+
+ char *name;
+
+ Layer(const char *name);
+ virtual ~Layer();
+
+ inline void
+ lock()
+ {
+ SDL_LockMutex(mutex);
+ }
+ inline void
+ unlock()
+ {
+ SDL_UnlockMutex(mutex);
+ }
+
+ /*
+ * Frame render method
+ */
+ virtual void frame(SDL_Surface *target) = 0;
+
+protected:
+ inline OSCServer::MethodHandlerId *
+ register_method(const char *method, const char *types,
+ OSCServer::MethodHandlerCb method_cb)
+ {
+ return osc_server.register_method(this, method, types, method_cb);
+ }
+ inline void
+ unregister_method(OSCServer::MethodHandlerId *hnd)
+ {
+ osc_server.unregister_method(hnd);
+ }
+
+ /*
+ * Default methods
+ */
+ virtual void geo(SDL_Rect geo) = 0;
+ virtual void alpha(float opacity) = 0;
+
+private:
+ /*
+ * OSC handler methods
+ */
+ OSCServer::MethodHandlerId *geo_osc_id;
+ static void
+ geo_osc(Layer *obj, lo_arg **argv)
+ {
+ SDL_Rect geo = {
+ (Sint16)argv[0]->i, (Sint16)argv[1]->i,
+ (Uint16)argv[2]->i, (Uint16)argv[3]->i
+ };
+ obj->geo(geo);
+ }
+ OSCServer::MethodHandlerId *alpha_osc_id;
+ static void
+ alpha_osc(Layer *obj, lo_arg **argv)
+ {
+ obj->alpha(argv[0]->f);
+ }
+};
+
+class LayerList {
+ LIST_HEAD(layers_head, Layer) head;
+
+ SDL_mutex *mutex;
+
+ inline void
+ lock()
+ {
+ SDL_LockMutex(mutex);
+ }
+ inline void
+ unlock()
+ {
+ SDL_UnlockMutex(mutex);
+ }
+
+public:
+ LayerList() : mutex(SDL_CreateMutex())
+ {
+ LIST_INIT(&head);
+ }
+ ~LayerList()
+ {
+ SDL_DestroyMutex(mutex);
+ }
+
+ void insert(int pos, Layer *layer);
+ void delete_layer(Layer *layer);
+ void render(SDL_Surface *target);
+};
+
+#endif \ No newline at end of file
diff --git a/src/layer_box.cpp b/src/layer_box.cpp
new file mode 100644
index 0000000..b720d5c
--- /dev/null
+++ b/src/layer_box.cpp
@@ -0,0 +1,47 @@
+#include <math.h>
+
+#include <SDL.h>
+#include <SDL_gfxPrimitives.h>
+
+#include "osc_graphics.h"
+#include "layer_box.h"
+
+Layer::CtorInfo LayerBox::ctor_info = {"box", COLOR_TYPES};
+
+LayerBox::LayerBox(const char *name, SDL_Rect geo, float opacity,
+ SDL_Color color) : Layer(name)
+{
+ color_osc_id = register_method("color", COLOR_TYPES,
+ (OSCServer::MethodHandlerCb)color_osc);
+
+ LayerBox::geo(geo);
+ LayerBox::color(color);
+ LayerBox::alpha(opacity);
+}
+
+void
+LayerBox::geo(SDL_Rect geo)
+{
+ x1 = geo.x;
+ y1 = geo.y;
+ x2 = geo.x + geo.w;
+ y2 = geo.y + geo.h;
+}
+
+void
+LayerBox::alpha(float opacity)
+{
+ a = (Uint8)ceilf(opacity*SDL_ALPHA_OPAQUE);
+}
+
+void
+LayerBox::frame(SDL_Surface *target)
+{
+ boxRGBA(target, x1, y1, x2 ? : target->w, y2 ? : target->h,
+ r, g, b, a);
+}
+
+LayerBox::~LayerBox()
+{
+ unregister_method(color_osc_id);
+}
diff --git a/src/layer_box.h b/src/layer_box.h
new file mode 100644
index 0000000..1e4e5e3
--- /dev/null
+++ b/src/layer_box.h
@@ -0,0 +1,56 @@
+#ifndef __LAYER_BOX_H
+#define __LAYER_BOX_H
+
+#include <SDL.h>
+
+#include "osc_graphics.h"
+#include "osc_server.h"
+#include "layer.h"
+
+#define COLOR_TYPES "iii" /* r, g, b */
+
+class LayerBox : public Layer {
+ Sint16 x1, y1, x2, y2;
+ Uint8 r, g, b, a;
+
+public:
+ LayerBox(const char *name, SDL_Rect geo, float opacity,
+ SDL_Color color);
+
+ static CtorInfo ctor_info;
+ static Layer *
+ ctor_osc(const char *name, SDL_Rect geo, float opacity, lo_arg **argv)
+ {
+ SDL_Color color = {
+ (Uint8)argv[0]->i, (Uint8)argv[1]->i, (Uint8)argv[2]->i
+ };
+ return new LayerBox(name, geo, opacity, color);
+ }
+
+ ~LayerBox();
+
+ void frame(SDL_Surface *target);
+
+private:
+ void geo(SDL_Rect geo);
+ void alpha(float opacity);
+
+ inline void
+ color(SDL_Color color)
+ {
+ r = color.r;
+ g = color.g;
+ b = color.b;
+ }
+ OSCServer::MethodHandlerId *color_osc_id;
+ static void
+ color_osc(LayerBox *obj, lo_arg **argv)
+ {
+ SDL_Color color = {
+ (Uint8)argv[0]->i, (Uint8)argv[1]->i, (Uint8)argv[2]->i
+ };
+ obj->color(color);
+ }
+};
+
+#endif \ No newline at end of file
diff --git a/src/layer_image.cpp b/src/layer_image.cpp
new file mode 100644
index 0000000..a0d6d55
--- /dev/null
+++ b/src/layer_image.cpp
@@ -0,0 +1,170 @@
+#include <math.h>
+
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_rotozoom.h>
+
+/* HACK: older SDL_gfx versions define GFX_ALPHA_ADJUST in the header */
+#define GFX_ALPHA_ADJUST \
+ static __attribute__((unused)) GFX_ALPHA_ADJUST
+#include <SDL_gfxBlitFunc.h>
+#undef GFX_ALPHA_ADJUST
+
+#include "osc_graphics.h"
+#include "layer_image.h"
+
+Layer::CtorInfo LayerImage::ctor_info = {"image", "s" /* file */};
+
+static inline void
+rgba_blit_with_alpha(SDL_Surface *src_surf, SDL_Surface *dst_surf, Uint8 alpha)
+{
+ Uint8 *src = (Uint8 *)src_surf->pixels;
+ Uint8 *dst = (Uint8 *)dst_surf->pixels;
+ SDL_PixelFormat *fmt = src_surf->format;
+
+ int inc = fmt->BytesPerPixel;
+ int len = src_surf->w * src_surf->h;
+
+ SDL_MAYBE_LOCK(src_surf);
+ SDL_MAYBE_LOCK(dst_surf);
+
+ GFX_DUFFS_LOOP4({
+ register Uint32 pixel;
+ register int a;
+
+ pixel = *(Uint32 *)src;
+ a = ((pixel & fmt->Amask) >> fmt->Ashift) << fmt->Aloss;
+ a = (a*alpha)/SDL_ALPHA_OPAQUE;
+ a = (a << fmt->Aloss) << fmt->Ashift;
+ pixel &= ~fmt->Amask;
+ pixel |= a;
+ *(Uint32 *)dst = pixel;
+
+ src += inc;
+ dst += inc;
+ }, len)
+
+ SDL_MAYBE_UNLOCK(dst_surf);
+ SDL_MAYBE_UNLOCK(src_surf);
+}
+
+LayerImage::LayerImage(const char *name, SDL_Rect geo, float opacity,
+ const char *file) :
+ Layer(name),
+ surf_alpha(NULL), surf_scaled(NULL), surf(NULL)
+{
+ file_osc_id = register_method("file", "s",
+ (OSCServer::MethodHandlerCb)file_osc);
+
+ LayerImage::alpha(opacity);
+ LayerImage::geo(geo);
+ LayerImage::file(file);
+}
+
+void
+LayerImage::geo(SDL_Rect geo)
+{
+ if (!geo.x && !geo.y && !geo.w && !geo.h)
+ geov = (SDL_Rect){0, 0, screen->w, screen->h};
+ else
+ geov = geo;
+
+ if (!surf)
+ return;
+
+ if (surf_scaled &&
+ surf_scaled->w == geov.w && surf_scaled->h == geov.h)
+ return;
+
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ SDL_FREESURFACE_SAFE(surf_scaled);
+
+ if (surf->w != geov.w || surf->h != geov.h) {
+ surf_scaled = zoomSurface(surf,
+ (double)geov.w/surf->w,
+ (double)geov.h/surf->h,
+ SMOOTHING_ON);
+ }
+
+ alpha(alphav);
+}
+
+void
+LayerImage::alpha(float opacity)
+{
+ SDL_Surface *use_surf = surf_scaled ? : surf;
+ Uint8 alpha = (Uint8)ceilf(opacity*SDL_ALPHA_OPAQUE);
+
+ alphav = opacity;
+
+ if (!use_surf)
+ return;
+
+ if (!use_surf->format->Amask) {
+ if (alpha == SDL_ALPHA_OPAQUE)
+ SDL_SetAlpha(use_surf, 0, 0);
+ else
+ SDL_SetAlpha(use_surf, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+
+ return;
+ }
+
+ if (alpha == SDL_ALPHA_OPAQUE) {
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ return;
+ }
+
+ if (!surf_alpha) {
+ surf_alpha = SDL_CreateRGBSurface(use_surf->flags,
+ use_surf->w, use_surf->h,
+ use_surf->format->BitsPerPixel,
+ use_surf->format->Rmask,
+ use_surf->format->Gmask,
+ use_surf->format->Bmask,
+ use_surf->format->Amask);
+ }
+
+ if (alpha == SDL_ALPHA_TRANSPARENT) {
+ SDL_FillRect(surf_alpha, NULL,
+ SDL_MapRGBA(surf_alpha->format,
+ 0, 0, 0, SDL_ALPHA_TRANSPARENT));
+ } else {
+ rgba_blit_with_alpha(use_surf, surf_alpha, alpha);
+ }
+}
+
+void
+LayerImage::file(const char *file)
+{
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ SDL_FREESURFACE_SAFE(surf_scaled);
+ SDL_FREESURFACE_SAFE(surf);
+
+ if (!file || !*file)
+ return;
+
+ surf = IMG_Load(file);
+ if (!surf) {
+ SDL_IMAGE_ERROR("IMG_Load");
+ exit(EXIT_FAILURE);
+ }
+
+ geo(geov);
+}
+
+void
+LayerImage::frame(SDL_Surface *target)
+{
+ if (surf)
+ SDL_BlitSurface(surf_alpha ? : surf_scaled ? : surf, NULL,
+ target, &geov);
+}
+
+LayerImage::~LayerImage()
+{
+ unregister_method(file_osc_id);
+
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ SDL_FREESURFACE_SAFE(surf_scaled);
+ SDL_FREESURFACE_SAFE(surf);
+}
diff --git a/src/layer_image.h b/src/layer_image.h
new file mode 100644
index 0000000..5f2271f
--- /dev/null
+++ b/src/layer_image.h
@@ -0,0 +1,58 @@
+#ifndef __LAYER_IMAGE_H
+#define __LAYER_IMAGE_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <SDL.h>
+
+#include "osc_graphics.h"
+#include "layer.h"
+
+class LayerImage : public Layer {
+ SDL_Surface *surf_alpha; /* with per-surface alpha */
+ SDL_Surface *surf_scaled; /* scaled image */
+ SDL_Surface *surf; /* original image */
+
+ SDL_Rect geov;
+ float alphav;
+
+public:
+ LayerImage(const char *name,
+ SDL_Rect geo = (SDL_Rect){0, 0, 0, 0},
+ float opacity = 1.,
+ const char *file = NULL);
+
+ static CtorInfo ctor_info;
+ static Layer *
+ ctor_osc(const char *name, SDL_Rect geo, float opacity, lo_arg **argv)
+ {
+ return new LayerImage(name, geo, opacity, &argv[0]->s);
+ }
+
+ ~LayerImage();
+
+ void frame(SDL_Surface *target);
+
+private:
+ void geo(SDL_Rect geo);
+ void alpha(float opacity);
+
+ void file(const char *file = NULL);
+ OSCServer::MethodHandlerId *file_osc_id;
+ static void
+ file_osc(LayerImage *obj, lo_arg **argv)
+ {
+ obj->file(&argv[0]->s);
+ }
+};
+
+/*
+ * Macros
+ */
+#define SDL_IMAGE_ERROR(FMT, ...) do { \
+ fprintf(stderr, "%s(%d): " FMT ": %s\n", \
+ __FILE__, __LINE__, ##__VA_ARGS__, IMG_GetError()); \
+} while (0)
+
+#endif
diff --git a/src/layer_video.cpp b/src/layer_video.cpp
new file mode 100644
index 0000000..8b3169a
--- /dev/null
+++ b/src/layer_video.cpp
@@ -0,0 +1,285 @@
+#include <assert.h>
+#include <math.h>
+
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <SDL_rotozoom.h>
+
+/* HACK: older SDL_gfx versions define GFX_ALPHA_ADJUST in the header */
+#define GFX_ALPHA_ADJUST \
+ static __attribute__((unused)) GFX_ALPHA_ADJUST
+#include <SDL_gfxBlitFunc.h>
+#undef GFX_ALPHA_ADJUST
+
+#include <vlc/vlc.h>
+#include <vlc/libvlc_version.h>
+
+#include "osc_graphics.h"
+#include "layer_video.h"
+
+Layer::CtorInfo LayerVideo::ctor_info = {"video", "s" /* url */};
+
+libvlc_instance_t *LayerVideo::vlcinst = NULL;
+
+/*
+ * libvlc callbacks
+ */
+extern "C" {
+
+static void *lock_cb(void *data, void **p_pixels);
+static void unlock_cb(void *data, void *id, void *const *p_pixels);
+static void display_cb(void *data, void *id);
+
+}
+
+LayerVideo::LayerVideo(const char *name, SDL_Rect geo, float opacity,
+ const char *url) :
+ Layer(name), mp(NULL), surf(NULL),
+ mutex(SDL_CreateMutex())
+{
+ /* static initialization */
+ if (!vlcinst) {
+ static char const *vlc_argv[] = {
+ "--no-audio", /* skip any audio track */
+ "--no-xlib", /* tell VLC to not use Xlib */
+ "--no-osd" /* no text on video */
+ };
+
+ vlcinst = libvlc_new(NARRAY(vlc_argv), vlc_argv);
+ }
+
+ url_osc_id = register_method("url", "s",
+ (OSCServer::MethodHandlerCb)url_osc);
+ rate_osc_id = register_method("rate", "f",
+ (OSCServer::MethodHandlerCb)rate_osc);
+ position_osc_id = register_method("position", "f",
+ (OSCServer::MethodHandlerCb)position_osc);
+ paused_osc_id = register_method("paused", "i",
+ (OSCServer::MethodHandlerCb)paused_osc);
+
+ LayerVideo::geo(geo);
+ LayerVideo::alpha(opacity);
+ LayerVideo::rate(1.);
+ LayerVideo::paused(true);
+
+ LayerVideo::url(url);
+}
+
+void
+LayerVideo::geo(SDL_Rect geo)
+{
+ if (!geo.x && !geo.y && !geo.w && !geo.h)
+ geov = (SDL_Rect){0, 0, screen->w, screen->h};
+ else
+ geov = geo;
+}
+
+#if LIBVLC_VERSION_INT < LIBVLC_VERSION(2,0,0,0)
+
+/*
+ * libvlc_video_get_size() cannot be used before playback has started and
+ * libvlc_media_get_tracks_info() is broken in libVLC v1.x.x so we just
+ * use the screen size on those versions (results in unnecessary scaling).
+ */
+static inline void
+media_get_video_size(libvlc_media_t *media __attribute__((unused)),
+ unsigned int &width, unsigned int &height)
+{
+ width = screen->w;
+ height = screen->h;
+}
+
+#else
+
+static inline void
+media_get_video_size(libvlc_media_t *media,
+ unsigned int &width, unsigned int &height)
+{
+ libvlc_media_track_info_t *tracks = NULL;
+ int num_tracks;
+
+ width = height = 0;
+
+ libvlc_media_parse(media);
+
+ num_tracks = libvlc_media_get_tracks_info(media, &tracks);
+ for (int i = 0; i < num_tracks; i++) {
+ if (tracks[i].i_type == libvlc_track_video) {
+ width = tracks[i].u.video.i_width;
+ height = tracks[i].u.video.i_height;
+ break;
+ }
+ }
+
+ free(tracks);
+
+ assert(width && height);
+}
+
+#endif
+
+static void *
+lock_cb(void *data, void **p_pixels)
+{
+ LayerVideo *video = (LayerVideo *)data;
+
+ *p_pixels = video->lock_surf();
+
+ return NULL; /* picture identifier, not needed here */
+}
+
+static void
+unlock_cb(void *data, void *id __attribute__((unused)),
+ void *const *p_pixels)
+{
+ LayerVideo *video = (LayerVideo *)data;
+
+ video->unlock_surf();
+}
+
+static void
+display_cb(void *data __attribute__((unused)),
+ void *id __attribute__((unused)))
+{
+ /* VLC wants to display the video */
+}
+
+void
+LayerVideo::url(const char *url)
+{
+ libvlc_media_t *m;
+ unsigned int width, height;
+
+ SDL_FREESURFACE_SAFE(surf);
+
+ if (mp) {
+ libvlc_media_player_release(mp);
+ mp = NULL;
+ }
+
+ if (!url || !*url)
+ return;
+
+#ifdef __WIN32__
+ /* URL handling somehow broken under Windows */
+ m = libvlc_media_new_path(vlcinst, url);
+#else
+ m = libvlc_media_new_location(vlcinst, url);
+#endif
+ mp = libvlc_media_player_new_from_media(m);
+ media_get_video_size(m, width, height);
+ libvlc_media_release(m);
+
+ /*
+ * Cannot change buffer dimensions on the fly, so we let
+ * libVLC render into a statically sized buffer and do the resizing
+ * on our own.
+ * We use the original video size so libVLC does not have to do
+ * unnecessary scaling.
+ */
+ surf = SDL_CreateRGBSurface(SDL_HWSURFACE, width, height,
+ 16, 0x001f, 0x07e0, 0xf800, 0);
+
+ libvlc_video_set_callbacks(mp, lock_cb, unlock_cb, display_cb, this);
+ libvlc_video_set_format(mp, "RV16", surf->w, surf->h, surf->pitch);
+
+ rate(ratev);
+ paused(pausedv);
+}
+
+void
+LayerVideo::alpha(float opacity)
+{
+ alphav = opacity;
+}
+
+void
+LayerVideo::rate(float rate)
+{
+ ratev = rate;
+
+ if (mp)
+ libvlc_media_player_set_rate(mp, rate);
+}
+
+void
+LayerVideo::position(float position)
+{
+ if (mp)
+ libvlc_media_player_set_position(mp, position);
+}
+
+void
+LayerVideo::paused(bool paused)
+{
+ pausedv = paused;
+
+ if (!mp)
+ return;
+
+#if 0
+ libvlc_media_player_set_pause(mp, paused);
+#else
+ int playing = libvlc_media_player_is_playing(mp);
+ if (playing && paused)
+ libvlc_media_player_pause(mp);
+ else if (!playing && !paused)
+ libvlc_media_player_play(mp);
+#endif
+}
+
+void
+LayerVideo::frame(SDL_Surface *target)
+{
+ Uint8 alpha = (Uint8)ceilf(alphav*SDL_ALPHA_OPAQUE);
+
+ if (!surf)
+ return;
+
+ if (surf->w != geov.w || surf->h != geov.h) {
+ SDL_Surface *surf_scaled;
+
+ SDL_LockMutex(mutex);
+ surf_scaled = zoomSurface(surf,
+ (double)geov.w/surf->w,
+ (double)geov.h/surf->h,
+ SMOOTHING_ON);
+ SDL_UnlockMutex(mutex);
+
+ if (alpha < SDL_ALPHA_OPAQUE) {
+ if (surf_scaled->format->Amask)
+ SDL_gfxSetAlpha(surf_scaled, alpha);
+ else
+ SDL_SetAlpha(surf_scaled,
+ SDL_SRCALPHA | SDL_RLEACCEL,
+ alpha);
+ }
+
+ SDL_BlitSurface(surf_scaled, NULL, target, &geov);
+ SDL_FreeSurface(surf_scaled);
+ } else {
+ if (alpha == SDL_ALPHA_OPAQUE)
+ SDL_SetAlpha(surf, 0, 0);
+ else
+ SDL_SetAlpha(surf, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+
+ SDL_LockMutex(mutex);
+ SDL_BlitSurface(surf, NULL, target, &geov);
+ SDL_UnlockMutex(mutex);
+ }
+}
+
+LayerVideo::~LayerVideo()
+{
+ unregister_method(url_osc_id);
+ unregister_method(rate_osc_id);
+ unregister_method(position_osc_id);
+ unregister_method(paused_osc_id);
+
+ if (mp)
+ libvlc_media_player_release(mp);
+ libvlc_release(vlcinst);
+ SDL_DestroyMutex(mutex);
+ if (surf)
+ SDL_FreeSurface(surf);
+}
diff --git a/src/layer_video.h b/src/layer_video.h
new file mode 100644
index 0000000..c3a4806
--- /dev/null
+++ b/src/layer_video.h
@@ -0,0 +1,92 @@
+#ifndef __LAYER_VIDEO_H
+#define __LAYER_VIDEO_H
+
+#include <SDL.h>
+#include <SDL_thread.h>
+
+#include <lo/lo.h>
+
+#include <vlc/vlc.h>
+
+#include "osc_graphics.h"
+#include "layer.h"
+
+class LayerVideo : public Layer {
+ static libvlc_instance_t *vlcinst;
+ libvlc_media_player_t *mp;
+
+ SDL_Surface *surf;
+ SDL_mutex *mutex;
+
+ SDL_Rect geov;
+ float alphav;
+
+ float ratev;
+ bool pausedv;
+
+public:
+ LayerVideo(const char *name,
+ SDL_Rect geo = (SDL_Rect){0, 0, 0, 0},
+ float opacity = 1.,
+ const char *url = NULL);
+
+ static CtorInfo ctor_info;
+ static Layer *
+ ctor_osc(const char *name, SDL_Rect geo, float opacity, lo_arg **argv)
+ {
+ return new LayerVideo(name, geo, opacity, &argv[0]->s);
+ }
+
+ ~LayerVideo();
+
+ inline void *
+ lock_surf()
+ {
+ SDL_LockMutex(mutex);
+ SDL_MAYBE_LOCK(surf);
+ return surf->pixels;
+ }
+ inline void
+ unlock_surf()
+ {
+ SDL_MAYBE_UNLOCK(surf);
+ SDL_UnlockMutex(mutex);
+ }
+
+ void frame(SDL_Surface *target);
+
+private:
+ void geo(SDL_Rect geo);
+ void alpha(float opacity);
+
+ void url(const char *url = NULL);
+ OSCServer::MethodHandlerId *url_osc_id;
+ static void
+ url_osc(LayerVideo *obj, lo_arg **argv)
+ {
+ obj->url(&argv[0]->s);
+ }
+ void rate(float rate);
+ OSCServer::MethodHandlerId *rate_osc_id;
+ static void
+ rate_osc(LayerVideo *obj, lo_arg **argv)
+ {
+ obj->rate(argv[0]->f);
+ }
+ void position(float position);
+ OSCServer::MethodHandlerId *position_osc_id;
+ static void
+ position_osc(LayerVideo *obj, lo_arg **argv)
+ {
+ obj->position(argv[0]->f);
+ }
+ void paused(bool paused);
+ OSCServer::MethodHandlerId *paused_osc_id;
+ static void
+ paused_osc(LayerVideo *obj, lo_arg **argv)
+ {
+ obj->paused(argv[0]->i);
+ }
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..d31f14f
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,231 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <SDL.h>
+#include <SDL_framerate.h>
+
+#include "osc_graphics.h"
+#include "osc_server.h"
+
+#include "layer.h"
+#include "layer_box.h"
+#include "layer_image.h"
+#include "layer_video.h"
+
+/*
+ * Default values
+ */
+#define DEFAULT_SCREEN_WIDTH 640 /* pixels */
+#define DEFAULT_SCREEN_HEIGHT 480 /* pixels */
+#define DEFAULT_SCREEN_BPP 32 /* bits */
+#define DEFAULT_SDL_FLAGS (SDL_HWSURFACE | SDL_DOUBLEBUF)
+#define DEFAULT_SHOW_CURSOR SDL_ENABLE
+#define DEFAULT_FRAMERATE 20 /* Hz */
+#define DEFAULT_PORT "7770" /* port number/service/UNIX socket */
+
+#define BOOL2STR(X) \
+ ((X) ? "on" : "off")
+
+/*
+ * Declarations
+ */
+SDL_Surface *screen;
+
+OSCServer osc_server;
+LayerList layers;
+
+int config_dump_osc = 0;
+
+static inline void
+sdl_process_events(void)
+{
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ switch (event.key.keysym.sym) {
+ case SDLK_F11:
+ if (!SDL_WM_ToggleFullScreen(screen)) {
+ SDL_ERROR("SDL_WM_ToggleFullScreen");
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case SDLK_F10:
+ SDL_ShowCursor(!SDL_ShowCursor(SDL_QUERY));
+ break;
+
+ case SDLK_F9:
+ config_dump_osc ^= 1;
+ break;
+
+ case SDLK_ESCAPE:
+ exit(EXIT_SUCCESS);
+
+ default:
+ break;
+ }
+ break;
+
+ case SDL_QUIT:
+ exit(EXIT_SUCCESS);
+
+ default:
+ break;
+ }
+ }
+}
+
+static void
+print_help(void)
+{
+ printf("osc-server [-h] [-p <port>] [-f] [-c] "
+ "[-W <width>] [-H <height>] "
+ "[-B <bpp>] [-F <framerate>]\n"
+ "Options:\n"
+ "\t-h Show this help\n"
+ "\t-p <port> Listen on port <port> (default: %s)\n"
+ "\t-f Toggle fullscreen (default: %s)\n"
+ "\t-c Toggle cursor displaying (default: %s)\n"
+ "\t-W <width> Set screen width (default: %d)\n"
+ "\t-H <height> Set screen height (default: %d)\n"
+ "\t-B <bpp> Set screen Bits per Pixel (default: %d)\n"
+ "\t-F <framerate> Set framerate in Hz (default: %d)\n",
+ DEFAULT_PORT,
+ BOOL2STR(DEFAULT_SDL_FLAGS & SDL_FULLSCREEN),
+ BOOL2STR(DEFAULT_SHOW_CURSOR),
+ DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT,
+ DEFAULT_SCREEN_BPP,
+ DEFAULT_FRAMERATE);
+}
+
+static inline void
+parse_options(int argc, char **argv,
+ const char *&port, Uint32 &flags, int &show_cursor,
+ int &width, int &height, int &bpp, int &framerate)
+{
+ for (int i = 1; i < argc; i++) {
+ if (strlen(argv[i]) != 2 || argv[i][0] != '-')
+ goto error;
+
+ switch (argv[i][1]) {
+ case 'h':
+ print_help();
+ exit(EXIT_SUCCESS);
+ case 'p':
+ if (++i == argc)
+ goto error;
+ port = argv[i];
+ break;
+ case 'f':
+ flags ^= SDL_FULLSCREEN;
+ break;
+ case 'c':
+ show_cursor = !show_cursor;
+ break;
+ case 'W':
+ if (++i == argc)
+ goto error;
+ width = atoi(argv[i]);
+ break;
+ case 'H':
+ if (++i == argc)
+ goto error;
+ height = atoi(argv[i]);
+ break;
+ case 'B':
+ if (++i == argc)
+ goto error;
+ bpp = atoi(argv[i]);
+ break;
+ case 'F':
+ if (++i == argc)
+ goto error;
+ framerate = atoi(argv[i]);
+ break;
+ default:
+ goto error;
+ }
+ }
+
+ return;
+
+error:
+ print_help();
+ exit(EXIT_FAILURE);
+}
+
+#define REGISTER_LAYER(CLASS) \
+ osc_server.register_layer(CLASS::ctor_info.name, \
+ CLASS::ctor_info.types, \
+ CLASS::ctor_osc)
+
+int
+main(int argc, char **argv)
+{
+ FPSmanager fpsm;
+
+ const char *port = DEFAULT_PORT;
+ Uint32 sdl_flags = DEFAULT_SDL_FLAGS;
+ int show_cursor = DEFAULT_SHOW_CURSOR;
+ int width = DEFAULT_SCREEN_WIDTH;
+ int height = DEFAULT_SCREEN_HEIGHT;
+ int bpp = DEFAULT_SCREEN_BPP;
+ int framerate = DEFAULT_FRAMERATE;
+
+ parse_options(argc, argv,
+ port, sdl_flags, show_cursor,
+ width, height, bpp, framerate);
+
+#ifdef __WIN32__
+ /* disable SDL's stdio redirect */
+ freopen("CON", "w", stdout);
+ freopen("CON", "w", stderr);
+#endif
+
+ if (SDL_Init(SDL_INIT_VIDEO)) {
+ SDL_ERROR("SDL_Init");
+ return EXIT_FAILURE;
+ }
+ atexit(SDL_Quit);
+
+ SDL_WM_SetCaption("OSC Graphics", NULL);
+
+ screen = SDL_SetVideoMode(width, height, bpp, sdl_flags);
+ if (screen == NULL) {
+ SDL_ERROR("SDL_SetVideoMode");
+ return EXIT_FAILURE;
+ }
+
+#if DEFAULT_SDL_FLAGS & SDL_HWSURFACE
+ if (!(screen->flags & SDL_HWSURFACE))
+ fprintf(stderr, "Warning: Hardware surfaces not available!\n");
+#endif
+
+ SDL_ShowCursor(show_cursor);
+
+ osc_server.open(port);
+
+ REGISTER_LAYER(LayerImage);
+ REGISTER_LAYER(LayerVideo);
+ REGISTER_LAYER(LayerBox);
+
+ osc_server.start();
+
+ SDL_initFramerate(&fpsm);
+ SDL_setFramerate(&fpsm, framerate);
+
+ for (;;) {
+ sdl_process_events();
+
+ layers.render(screen);
+
+ SDL_Flip(screen);
+ SDL_framerateDelay(&fpsm);
+ }
+
+ /* never reached */
+ return EXIT_FAILURE;
+}
diff --git a/src/osc_graphics.h b/src/osc_graphics.h
new file mode 100644
index 0000000..2a08678
--- /dev/null
+++ b/src/osc_graphics.h
@@ -0,0 +1,46 @@
+#ifndef __OSC_GRAPHICS_H
+#define __OSC_GRAPHICS_H
+
+#include <stdio.h>
+
+#include <SDL.h>
+
+#include "osc_server.h"
+#include "layer.h"
+
+/*
+ * Macros
+ */
+#define NARRAY(ARRAY) \
+ (sizeof(ARRAY) / sizeof((ARRAY)[0]))
+
+#define SDL_MAYBE_LOCK(SURFACE) do { \
+ if (SDL_MUSTLOCK(SURFACE)) \
+ SDL_LockSurface(SURFACE); \
+} while (0)
+
+#define SDL_MAYBE_UNLOCK(SURFACE) do { \
+ if (SDL_MUSTLOCK(SURFACE)) \
+ SDL_UnlockSurface(SURFACE); \
+} while (0)
+
+#define SDL_FREESURFACE_SAFE(SURFACE) do { \
+ if (SURFACE) { \
+ SDL_FreeSurface(SURFACE); \
+ SURFACE = NULL; \
+ } \
+} while (0)
+
+#define SDL_ERROR(FMT, ...) do { \
+ fprintf(stderr, "%s(%d): " FMT ": %s\n", \
+ __FILE__, __LINE__, ##__VA_ARGS__, SDL_GetError()); \
+} while (0)
+
+/*
+ * Declarations
+ */
+extern SDL_Surface *screen;
+
+extern int config_dump_osc;
+
+#endif
diff --git a/src/osc_server.cpp b/src/osc_server.cpp
new file mode 100644
index 0000000..c3eb328
--- /dev/null
+++ b/src/osc_server.cpp
@@ -0,0 +1,198 @@
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <SDL.h>
+
+#include <lo/lo.h>
+
+#include "osc_graphics.h"
+#include "layer.h"
+
+#include "osc_server.h"
+
+/*
+ * liblo callbacks
+ */
+extern "C" {
+
+static void error_handler(int num, const char *msg, const char *path);
+static int generic_handler(const char *path, const char *types, lo_arg **argv,
+ int argc, void *data, void *user_data);
+
+static int dtor_generic_handler(const char *path, const char *types,
+ lo_arg **argv, int argc,
+ void *data, void *user_data);
+static int ctor_generic_handler(const char *path, const char *types,
+ lo_arg **argv, int argc,
+ void *data, void *user_data);
+static int method_generic_handler(const char *path, const char *types,
+ lo_arg **argv, int argc,
+ void *data, void *user_data);
+
+}
+
+extern LayerList layers;
+
+static void
+error_handler(int num, const char *msg, const char *path)
+{
+ fprintf(stderr, "liblo server error %d in path %s: %s\n",
+ num, path, msg);
+}
+
+/* catch any incoming messages and display them. returning 1 means that the
+ * message has not been fully handled and the server should try other methods */
+static int
+generic_handler(const char *path, const char *types, lo_arg **argv,
+ int argc, void *data,
+ void *user_data __attribute__((unused)))
+{
+ if (!config_dump_osc)
+ return 1;
+
+ printf("path: <%s>\n", path);
+ for (int i = 0; i < argc; i++) {
+ printf("arg %d '%c' ", i, types[i]);
+ lo_arg_pp((lo_type)types[i], argv[i]);
+ printf("\n");
+ }
+ printf("\n");
+
+ return 1;
+}
+
+void
+OSCServer::open(const char *port)
+{
+ server = lo_server_thread_new(port, error_handler);
+
+ add_method(NULL, generic_handler, NULL, NULL);
+}
+
+void
+OSCServer::add_method_v(MethodHandlerId **hnd, const char *types,
+ lo_method_handler handler, void *data,
+ const char *fmt, va_list ap)
+{
+ char buf[255];
+
+ if (fmt)
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ lo_server_thread_add_method(server, fmt ? buf : NULL, types,
+ handler, data);
+
+ if (hnd)
+ *hnd = new MethodHandlerId(types, buf, data);
+}
+
+void
+OSCServer::del_method(const char *types, const char *fmt, ...)
+{
+ char buf[255];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ lo_server_thread_del_method(server, buf, types);
+ va_end(ap);
+}
+
+static int
+dtor_generic_handler(const char *path,
+ const char *types __attribute__((unused)),
+ lo_arg **argv __attribute__((unused)),
+ int argc __attribute__((unused)),
+ void *data __attribute__((unused)),
+ void *user_data)
+{
+ Layer *layer = (Layer *)user_data;
+
+ layers.delete_layer(layer);
+ osc_server.del_method("", "%s", path);
+
+ return 0;
+}
+
+static int
+ctor_generic_handler(const char *path __attribute__((unused)),
+ const char *types __attribute__((unused)),
+ lo_arg **argv, int argc,
+ void *data __attribute__((unused)),
+ void *user_data)
+{
+ OSCServer::CtorHandlerCb const_cb = (OSCServer::CtorHandlerCb)user_data;
+ Layer *layer;
+
+ SDL_Rect geo = {
+ (Sint16)argv[2]->i, (Sint16)argv[3]->i,
+ (Uint16)argv[4]->i, (Uint16)argv[5]->i
+ };
+
+ layer = const_cb(&argv[1]->s, geo, argv[6]->f, argv + 7);
+ layers.insert(argv[0]->i, layer);
+
+ osc_server.add_method("", dtor_generic_handler, layer,
+ "/layer/%s/delete", layer->name);
+
+ return 0;
+}
+
+void
+OSCServer::register_layer(const char *name, const char *types,
+ CtorHandlerCb ctor_cb)
+{
+ char buf[255];
+
+ snprintf(buf, sizeof(buf), "%s%s", NEW_LAYER_TYPES, types);
+ add_method(buf, ctor_generic_handler, (void *)ctor_cb,
+ "/layer/new/%s", name);
+}
+
+struct OscMethodDefaultCtx {
+ Layer *layer;
+ OSCServer::MethodHandlerCb method_cb;
+};
+
+static int
+method_generic_handler(const char *path __attribute__((unused)),
+ const char *types __attribute__((unused)),
+ lo_arg **argv, int argc,
+ void *data __attribute__((unused)),
+ void *user_data)
+{
+ OscMethodDefaultCtx *ctx = (OscMethodDefaultCtx *)user_data;
+
+ ctx->layer->lock();
+ ctx->method_cb(ctx->layer, argv);
+ ctx->layer->unlock();
+
+ return 0;
+}
+
+OSCServer::MethodHandlerId *
+OSCServer::register_method(Layer *layer, const char *method, const char *types,
+ MethodHandlerCb method_cb)
+{
+ MethodHandlerId *hnd;
+ OscMethodDefaultCtx *ctx = new OscMethodDefaultCtx;
+
+ ctx->layer = layer;
+ ctx->method_cb = method_cb;
+
+ add_method(&hnd, types, method_generic_handler, ctx,
+ "/layer/%s/%s", layer->name, method);
+
+ return hnd;
+}
+
+void
+OSCServer::unregister_method(MethodHandlerId *hnd)
+{
+ delete (OscMethodDefaultCtx *)hnd->data;
+ del_method(hnd);
+}
+
+OSCServer::~OSCServer()
+{
+ lo_server_thread_free(server);
+}
diff --git a/src/osc_server.h b/src/osc_server.h
new file mode 100644
index 0000000..f6d0c51
--- /dev/null
+++ b/src/osc_server.h
@@ -0,0 +1,105 @@
+#ifndef __OSC_SERVER_H
+#define __OSC_SERVER_H
+
+#include <string.h>
+#include <stdarg.h>
+
+#include <SDL.h>
+
+#include <lo/lo.h>
+
+#include "osc_graphics.h"
+
+class Layer;
+
+class OSCServer {
+ lo_server_thread server;
+
+public:
+ struct MethodHandlerId {
+ char *types;
+ char *path;
+ void *data;
+
+ MethodHandlerId(const char *_types, const char *_path,
+ void *_data = NULL) :
+ types(strdup(_types)), path(strdup(_path)),
+ data(_data) {}
+ ~MethodHandlerId()
+ {
+ free(types);
+ free(path);
+ }
+ };
+
+private:
+ void add_method_v(MethodHandlerId **hnd, const char *types,
+ lo_method_handler handler, void *data,
+ const char *fmt, va_list ap)
+ __attribute__((format(printf, 6, 0)));
+
+public:
+ typedef void (*MethodHandlerCb)(Layer *obj, lo_arg **argv);
+ typedef Layer *(*CtorHandlerCb)(const char *name, SDL_Rect geo,
+ float alpha, lo_arg **argv);
+
+ ~OSCServer();
+
+ void open(const char *port);
+
+ inline void
+ start()
+ {
+ lo_server_thread_start(server);
+ }
+ inline void
+ stop()
+ {
+ lo_server_thread_stop(server);
+ }
+
+ inline void
+ add_method(MethodHandlerId **hnd, const char *types,
+ lo_method_handler handler, void *data,
+ const char *fmt, ...)
+ __attribute__((format(printf, 6, 7)))
+ {
+ va_list ap;
+ va_start(ap, fmt);
+ add_method_v(hnd, types, handler, data, fmt, ap);
+ va_end(ap);
+ }
+ inline void
+ add_method(const char *types,
+ lo_method_handler handler, void *data,
+ const char *fmt, ...)
+ __attribute__((format(printf, 5, 6)))
+ {
+ va_list ap;
+ va_start(ap, fmt);
+ add_method_v(NULL, types, handler, data, fmt, ap);
+ va_end(ap);
+ }
+
+ void del_method(const char *types, const char *fmt, ...)
+ __attribute__((format(printf, 3, 4)));
+ inline void
+ del_method(MethodHandlerId *hnd)
+ {
+ del_method(hnd->types, "%s", hnd->path);
+ delete hnd;
+ }
+
+ void register_layer(const char *name, const char *types,
+ CtorHandlerCb ctor_cb);
+
+ MethodHandlerId *register_method(Layer *layer, const char *method,
+ const char *types,
+ MethodHandlerCb method_cb);
+ void unregister_method(MethodHandlerId *hnd);
+};
+
+#define GEO_TYPES "iiii" /* x, y, width, height */
+#define NEW_LAYER_TYPES "is" GEO_TYPES "f" /* position, name, GEO, alpha */
+
+#endif