diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-09-28 15:26:29 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-09-28 15:26:29 +0200 |
commit | e7da86053b3df2882816b0df8089e1a51b61939f (patch) | |
tree | 8f7430f7d1e82ddb8449548886f57a3a7c646c05 /src | |
parent | 9b134ea457f91ba8ea6ae558c9192f58c09f62bc (diff) | |
download | osc-graphics-e7da86053b3df2882816b0df8089e1a51b61939f.tar.gz |
autotools based build system
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 9 | ||||
-rw-r--r-- | src/layer.cpp | 72 | ||||
-rw-r--r-- | src/layer.h | 125 | ||||
-rw-r--r-- | src/layer_box.cpp | 47 | ||||
-rw-r--r-- | src/layer_box.h | 56 | ||||
-rw-r--r-- | src/layer_image.cpp | 170 | ||||
-rw-r--r-- | src/layer_image.h | 58 | ||||
-rw-r--r-- | src/layer_video.cpp | 285 | ||||
-rw-r--r-- | src/layer_video.h | 92 | ||||
-rw-r--r-- | src/main.cpp | 231 | ||||
-rw-r--r-- | src/osc_graphics.h | 46 | ||||
-rw-r--r-- | src/osc_server.cpp | 198 | ||||
-rw-r--r-- | src/osc_server.h | 105 |
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 |