summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile129
-rw-r--r--Pixel.h34
-rw-r--r--README.md20
-rw-r--r--ambiservice.c39
-rw-r--r--ambiservice.h50
-rw-r--r--ambithread.c311
-rw-r--r--ambithread.h68
-rw-r--r--boblightservice.c136
-rw-r--r--boblightservice.h49
-rw-r--r--common.c80
-rw-r--r--common.h45
-rw-r--r--config.c60
-rw-r--r--config.h71
-rw-r--r--lib/boblight-functions.h41
-rw-r--r--lib/boblight.cpp120
-rw-r--r--lib/boblight.h103
-rw-r--r--lib/boblight_client.cpp704
-rw-r--r--lib/boblight_client.h115
-rw-r--r--lib/options.h41
-rw-r--r--softhdservice.h38
-rw-r--r--vdrboblight.c412
22 files changed, 2669 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 9fa3b1b..da1e3d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,6 @@
*.exe
*.out
*.app
+
+# Other Files
+.dependencies
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5311afc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,129 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile 2.13 2012/12/21 11:36:15 kls Exp $
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+
+PLUGIN = vdrboblight
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' DVBAPI.h | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The directory environment:
+
+# Use package data if installed...otherwise assume we're under the VDR source directory:
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+LIBDIR = $(call PKGCFG,libdir)
+LOCDIR = $(call PKGCFG,locdir)
+PLGCFG = $(call PKGCFG,plgcfg)
+#
+TMPDIR ?= /tmp
+
+### The compiler options:
+
+export CFLAGS = $(call PKGCFG,cflags)
+export CXXFLAGS = $(call PKGCFG,cxxflags)
+
+### Allow user defined options to overwrite defaults:
+
+-include $(PLGCFG)
+
+### The version number of VDR's plugin API:
+
+APIVERSION = $(call PKGCFG,apiversion)
+ifeq ($(strip $(APIVERSION)),)
+APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+NOCONFIG := 1
+endif
+
+# backward compatibility with VDR version < 1.7.34
+API1733 := $(shell if [ "$(APIVERSION)" \< "1.7.34" ]; then echo true; fi; )
+
+ifdef API1733
+
+VDRSRC = $(VDRDIR)
+ifeq ($(strip $(VDRSRC)),)
+VDRSRC := ../../..
+endif
+LIBDIR = $(VDRSRC)/PLUGINS/lib
+
+ifndef NOCONFIG
+CXXFLAGS = $(call PKGCFG,cflags)
+CXXFLAGS += -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC
+else
+-include $(VDRSRC)/Make.global
+-include $(VDRSRC)/Make.config
+endif
+
+export CXXFLAGS
+endif
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### The name of the shared object file:
+
+SOFILE = libvdr-$(PLUGIN).so
+
+### Includes and Defines (add further entries here):
+
+ifdef API1733
+INCLUDES += -I$(VDRSRC)/include
+endif
+
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o ambithread.o ambiservice.o boblightservice.o common.o config.o
+
+### The main target:
+
+ifdef API1733
+all: install
+else
+all: $(SOFILE) $(DEVPLUGTARGETS)
+endif
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+$(SOFILE): $(OBJS) $(FFDECSA)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(DECSALIB) -o $@
+
+
+install-lib: $(SOFILE)
+ install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
+
+install: install-lib $(DEVPLUGINSTALL)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot
+ @-rm -f libvdr-$(PLUGIN).so libvdr-$(PLUGIN).so.$(APIVERSION)
diff --git a/Pixel.h b/Pixel.h
new file mode 100644
index 0000000..9d9adf6
--- /dev/null
+++ b/Pixel.h
@@ -0,0 +1,34 @@
+/*
+ * Pixel.h
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PIXEL_H
+#define __PIXEL_H
+
+//***************************************************************************
+// Pixel - format as provided by softhddevice
+//***************************************************************************
+
+struct Pixel
+{
+ unsigned char b;
+ unsigned char g;
+ unsigned char r;
+ unsigned char a;
+};
+#endif \ No newline at end of file
diff --git a/README.md b/README.md
index 6fed0ef..c70d00a 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,24 @@
vdr-plugin-boblight
===================
+This is a "plugin" for the Video Disk Recorder (VDR).
+
Boblight with data from softhddevice
+
+Needs libboblight.so and boblightd configured and running (https://code.google.com/p/boblight)
+
+
+Priority [128] Every boblight client has a priority, the higher the lower
+Updaterate [25] Updaterate in Hz, 25 => 25 Updates per second from softhddevice. Boblightd has it's own updaterate, so this could be lowered
+Detect cinema bars
+Show mainmenu
+Log level
+
+Threshold [20] 0-255 Blacklevel detection, Black => LED's off
+Gamma [10] 0-100 Gamma corretion (divided by 10, 10 = 1.0)
+Value [80] 0-200 Saturation and value are multipliers for HSV color space (divided by 10, 10 = 1.0)
+Saturation [30] 0-200 Saturation and value are multipliers for HSV color space (divided by 10, 10 = 1.0)
+Speed [60] 0-100 Speed is a factor for a first order lowpass filter, the higher you set it the faster the lights react.
+Autospeed [0] 0-100 Autospeed adjusts the speed on top of that based on how fast the colors are changing.
+Interpolation [true] Interpolation is a setting for a boblightd output device,
+ when it's on it will interpolate between the last two writes of a client. (Off might reduce load) \ No newline at end of file
diff --git a/ambiservice.c b/ambiservice.c
new file mode 100644
index 0000000..b3b6ba8
--- /dev/null
+++ b/ambiservice.c
@@ -0,0 +1,39 @@
+/*
+ * ambiservice.h
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ambiservice.h"
+
+//***************************************************************************
+// View Modes
+//***************************************************************************
+
+const char* cAmbiService::viewModes[] =
+{
+ "atmo",
+ "fixed color",
+ "black",
+ "detached",
+
+ 0
+};
+
+const char* cAmbiService::toName(ViewMode vm)
+{
+ return viewModes[vm];
+}
diff --git a/ambiservice.h b/ambiservice.h
new file mode 100644
index 0000000..7ca41c6
--- /dev/null
+++ b/ambiservice.h
@@ -0,0 +1,50 @@
+/*
+ * ambiservice.c
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AMBI_SERVICE_H
+#define __AMBI_SERVICE_H
+
+class cAmbiService
+{
+ public:
+
+ enum Cinebars
+ {
+ cbHorizontal,
+ cbVertical,
+ cbBoth,
+ cbCount
+ };
+
+ enum ViewMode
+ {
+ vmAtmo,
+ vmFixedCol,
+ vmBlack,
+ vmDetached,
+ vmCount
+ };
+
+ // static
+
+ static const char* toName(ViewMode vm);
+ static const char* viewModes[];
+};
+
+#endif // __SEDU_SERVICE_H
diff --git a/ambithread.c b/ambithread.c
new file mode 100644
index 0000000..3ee66e7
--- /dev/null
+++ b/ambithread.c
@@ -0,0 +1,311 @@
+/*
+ * ambithread.c
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <vdr/plugin.h>
+
+#include "softhdservice.h"
+#include "ambithread.h"
+#include "config.h"
+
+//***************************************************************************
+// Class cAmbiThread
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cAmbiThread::cAmbiThread()
+{
+ loopActive = false;
+ image = 0;
+ imageSize = 0;
+ imageWidth = 0;
+ imageHeight = 0;
+ cineBarsHor = 0;
+ cineBarsVer = 0;
+}
+
+cAmbiThread::~cAmbiThread()
+{
+ bob.close();
+}
+
+//***************************************************************************
+// Stop Thread
+//***************************************************************************
+
+void cAmbiThread::Stop()
+{
+ loopActive = false;
+ waitCondition.Broadcast(); // wakeup the thread
+
+ Cancel(3); // wait up to 3 seconds for thread was stopping
+}
+
+//***************************************************************************
+// Action
+//***************************************************************************
+
+void cAmbiThread::Action()
+{
+ MsTime wait = 0;
+ cMutexLock lock(&mutex);
+
+ tell(0, "boblight Thread started (pid=%d)", getpid());
+
+ loopActive = true;
+ bob.open();
+
+ while (loopActive && Running())
+ {
+ MsTime start = msNow();
+
+ // work ...
+ if(bob.ping() == success) {
+
+ if(cfg.dirty > 0) {
+ cfg.dirty = 0;
+ bob.sendOptions();
+ }
+
+ if (cfg.viewMode == vmAtmo)
+ {
+ if (grabImage() == success)
+ {
+ detectCineBars();
+ putData();
+
+ MsTime elapsed = msNow() - start;
+ wait = 1000 / cfg.frequence - elapsed;
+ tell(3, "sleeping %ldms (%d Hz)", wait, cfg.frequence);
+ }
+ else
+ {
+ wait = 10000; // retry softhd grab every 10 seconds
+ }
+ }
+ else
+ {
+ putData();
+ wait = 500; // less load on fixed color or black
+ }
+ }
+ else { // Connection lost, reconnect
+ bob.close();
+ bob.open();
+ wait = 10000;
+ }
+
+ waitCondition.TimedWait(mutex, wait); // wait time in ms
+ }
+
+ bob.close();
+ loopActive = false;
+
+ tell(0, "boblight thread ended (pid=%d)", getpid());
+}
+
+//***************************************************************************
+// Grab Image
+//***************************************************************************
+
+int cAmbiThread::grabImage()
+{
+ SoftHDDevice_AtmoGrabService_v1_1_t req;
+
+ free(image);
+ image = 0;
+
+ cPlugin* softHdPlugin = cPluginManager::GetPlugin("softhddevice");
+ int softHdGrabService = (softHdPlugin && softHdPlugin->Service(ATMO1_GRAB_SERVICE, 0));
+
+ if (!softHdGrabService)
+ return error("Can't find softhddevice %s, aborting grab, retrying in 10 seconds!",
+ softHdPlugin ? "service" : "plugin");
+
+ // grab image at sofhddevice
+ req.width = -64; //warum? steht hier => http://projects.vdr-developer.org/projects/plg-softhddevice/repository/revisions/master/entry/video.c#L7372
+ req.height = 64;
+ req.img = 0;
+
+ if (!softHdPlugin->Service(ATMO1_GRAB_SERVICE, &req) || !req.img)
+ return fail;
+
+ tell(3, "Got image with %dx%d pixel; %d bytes", req.width, req.height, req.size);
+
+ image = (Pixel*)req.img;
+ imageSize = req.size;
+ imageWidth = req.width;
+ imageHeight = req.height;
+
+ return success;
+}
+
+//***************************************************************************
+// Detect Cine Bars
+//***************************************************************************
+
+int cAmbiThread::detectCineBars()
+{
+ const int threshold = 3; // threshold for black level of cine bars
+ Pixel* p;
+ int off;
+
+ // check horizontal bars
+
+ if (cfg.detectCineBars == cbHorizontal || cfg.detectCineBars == cbBoth)
+ {
+ for (off = 0; off < imageHeight/5; off++) // cinebar height max 1/5 of the screen height
+ {
+ int above = 0;
+
+ for (int x = 0; x < imageWidth; x++)
+ {
+ p = &image[off*imageWidth + x];
+
+ if (p->r > threshold || p->g > threshold || p->b > threshold)
+ above++;
+
+ p = &image[((imageHeight-1)-off)*imageWidth + x];
+
+ if (p->r > threshold || p->g > threshold || p->b > threshold)
+ above++;
+ }
+
+ if (above > imageWidth/8) // max 1/8 failed pixel
+ break;
+ }
+
+ if (cineBarsHor != off)
+ {
+ static int last = 0;
+ static int count = 0;
+
+ if (off != last)
+ {
+ last = off;
+ count = 0;
+ }
+
+ if (count++ >= cfg.frequence)
+ {
+ count = 0;
+ cineBarsHor = off;
+ tell(0, "Switch horizontal cine bars to %d", cineBarsHor);
+ }
+ }
+ }
+
+ // check vertical bars
+
+ if (cfg.detectCineBars == cbVertical || cfg.detectCineBars == cbBoth)
+ {
+ for (off = 0; off < imageWidth/5; off++) // cinebar height max 1/5 of the screen width
+ {
+ int above = 0;
+
+ for (int y = 0; y < imageHeight; y++)
+ {
+ p = &image[y*imageWidth + off];
+
+ if (p->r > threshold || p->g > threshold || p->b > threshold)
+ above++;
+
+ p = &image[y*imageWidth + ((imageWidth-1)-off)];
+
+ if (p->r > threshold || p->g > threshold || p->b > threshold)
+ above++;
+ }
+
+ if (above > imageHeight/6) // max 1/6 failed pixel
+ break;
+ }
+
+ if (cineBarsVer != off)
+ {
+ static int last = 0;
+ static int count = 0;
+
+ if (off != last)
+ {
+ last = off;
+ count = 0;
+ }
+
+ if (count++ >= cfg.frequence)
+ {
+ count = 0;
+
+ cineBarsVer = off;
+ tell(0, "Switch vertical cine bars to %d", cineBarsVer);
+ }
+ }
+ }
+
+ return done;
+}
+
+//***************************************************************************
+// Put Data
+//***************************************************************************
+
+int cAmbiThread::putData()
+{
+
+ if (cfg.viewMode == vmFixedCol)
+ {
+ int pFixedCol[3] = {cfg.fixedR, cfg.fixedG, cfg.fixedB};
+ bob.writePix(pFixedCol);
+ }
+ else if (cfg.viewMode == vmBlack) {
+ int pFixedCol[3] = {0,0,0};
+ bob.writePix(pFixedCol);
+ }
+ else if(cfg.viewMode == vmAtmo) {
+
+ int row = 0;
+ Pixel pixel = {0,0,0,0};
+ Pixel* p = &pixel;
+
+ for (int y = 0; y < imageHeight; y++) {
+ // skip horizontal cinebars
+ if(y < cineBarsHor) continue;
+ if(y > imageHeight - cineBarsHor) continue;
+
+ int rgb[3];
+ row = imageWidth * y;
+ for (int x = 0; x < imageWidth; x++) {
+ // skip vertical cinebars
+ if(x < cineBarsVer) continue;
+ if(x > imageWidth - cineBarsVer) continue;
+
+ p = &image[row + x];
+ rgb[0] = p->r;
+ rgb[1] = p->g;
+ rgb[2] = p->b;
+ bob.writeColor(rgb, x - cineBarsVer, y - cineBarsHor);
+ }
+ }
+ bob.setScanRange(imageWidth - (2*cineBarsVer), imageHeight - (2*cineBarsHor));
+ }
+
+ bob.send();
+
+ return success;
+}
diff --git a/ambithread.h b/ambithread.h
new file mode 100644
index 0000000..d4fe822
--- /dev/null
+++ b/ambithread.h
@@ -0,0 +1,68 @@
+/*
+ * ambithread.h
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <queue>
+
+#include <vdr/thread.h>
+
+#include "common.h"
+#include "config.h"
+#include "Pixel.h"
+#include "boblightservice.h"
+
+
+//***************************************************************************
+// SEDU Thread
+//***************************************************************************
+
+class cAmbiThread : public cThread, public cAmbiService
+{
+ public:
+
+ cAmbiThread();
+ ~cAmbiThread();
+
+ int isRunning() { return Running(); }
+
+ // interface
+
+ void Stop();
+
+ private:
+
+ void Action(void);
+
+ int grabImage();
+ int detectCineBars();
+ int putData();
+
+ // data
+ cBoblight bob;
+
+ cMutex mutex;
+ cCondVar waitCondition;
+ int loopActive;
+
+ Pixel* image;
+ int cineBarsHor;
+ int cineBarsVer;
+ int imageSize;
+ int imageWidth;
+ int imageHeight;
+}; \ No newline at end of file
diff --git a/boblightservice.c b/boblightservice.c
new file mode 100644
index 0000000..a6f5692
--- /dev/null
+++ b/boblightservice.c
@@ -0,0 +1,136 @@
+/*
+ * boblightservice.c
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#define BOBLIGHT_DLOPEN
+#include "lib/boblight.h"
+#include "boblightservice.h"
+
+cBoblight::cBoblight()
+{
+
+ char* boblight_error = boblight_loadlibrary(NULL);
+ if (boblight_error)
+ {
+ error("Couldn't load libboblight");
+ }
+}
+
+//***************************************************************************
+// Open/Close
+//***************************************************************************
+
+int cBoblight::open()
+{
+ //init boblight
+ m_boblight = boblight_init();
+ tell(1, "Successfully loaded and initalized libboblight");
+
+ if (!boblight_connect(m_boblight, NULL, -1, 1000000) || !boblight_setpriority(m_boblight, cfg.priority))
+ {
+ tell(0, "Error connecting to boblight %s", boblight_geterror(m_boblight));
+ close();
+ return fail;
+ }
+ tell(0, "Connected to boblight");
+ sendOptions();
+ return success;
+}
+
+int cBoblight::close()
+{
+ tell(1, "Destroying boblight");
+ boblight_destroy(m_boblight); // calls delete *void
+ m_boblight = 0; // set pointer to 0
+ return success;
+}
+
+int cBoblight::ping() {
+ if(m_boblight == 0) {
+ tell(1, "boblight not initalized");
+ return fail;
+ }
+
+ if (!boblight_ping(m_boblight, NULL)) {
+ tell(0, "Connecting to boblight lost: %s", boblight_geterror(m_boblight));
+ return fail;
+ }
+ return success;
+}
+
+//***************************************************************************
+// Write Pixel
+//***************************************************************************
+
+int cBoblight::writePix(int *rgb)
+{
+ boblight_addpixel(m_boblight, -1, rgb);
+
+ return success;
+}
+
+int cBoblight::writeColor(int *rgb, int x, int y)
+{
+ boblight_addpixelxy(m_boblight, x, y, rgb);
+
+ return success;
+}
+
+int cBoblight::send() {
+ if (!boblight_sendrgb(m_boblight, 0, NULL))
+ {
+ //m_error = boblight_geterror(m_boblight);
+ return fail;
+ }
+ return success;
+}
+
+int cBoblight::setScanRange(int width, int height) {
+ boblight_setscanrange(m_boblight, width, height);
+ return success;
+}
+
+int cBoblight::sendOptions() {
+ if (m_boblight == 0) return fail;
+ char buf[32];
+
+ sprintf(buf, "%s %1f", "value", cfg.value * 0.01);
+ boblight_setoption(m_boblight, -1, buf);
+
+ sprintf(buf, "%s %d", "threshold", cfg.threshold);
+ boblight_setoption(m_boblight, -1, buf);
+
+ sprintf(buf, "%s %.1f", "gamma", cfg.gamma * 0.01);
+ boblight_setoption(m_boblight, -1, buf);
+
+ sprintf(buf, "%s %1f", "saturation", cfg.saturation * 0.01);
+ boblight_setoption(m_boblight, -1, buf);
+
+ sprintf(buf, "%s %d", "speed", cfg.speed);
+ boblight_setoption(m_boblight, -1, buf);
+
+ sprintf(buf, "%s %d", "autospeed", cfg.autospeed);
+ boblight_setoption(m_boblight, -1, buf);
+
+ sprintf(buf, "%s %s", "interpolation", cfg.interpolation ? "true" : "false");
+ boblight_setoption(m_boblight, -1, buf);
+
+ return success;
+} \ No newline at end of file
diff --git a/boblightservice.h b/boblightservice.h
new file mode 100644
index 0000000..c34d9f3
--- /dev/null
+++ b/boblightservice.h
@@ -0,0 +1,49 @@
+/*
+ * boblightservice.h
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BOBLIGHT_H
+#define __BOBLIGHT_H
+
+#include "common.h"
+#include "config.h"
+#include "ambiservice.h"
+
+class cBoblight : public cAmbiService
+{
+ public:
+
+ cBoblight();
+ ~cBoblight() { close(); }
+
+ int open();
+ int close();
+
+ int writePix(int *rgb);
+ int writeColor(int *rgb, int x, int y);
+ int setScanRange(int width, int height);
+ int ping();
+ int send();
+ int sendOptions();
+
+ private:
+ void* m_boblight;
+
+};
+
+#endif \ No newline at end of file
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..2351300
--- /dev/null
+++ b/common.c
@@ -0,0 +1,80 @@
+/*
+ * common.c: EPG2VDR plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <vdr/thread.h>
+
+#include "common.h"
+#include "config.h"
+
+cMutex logMutex;
+
+//***************************************************************************
+// Tell
+//***************************************************************************
+
+void tell(int eloquence, const char* format, ...)
+{
+ if (cfg.loglevel < eloquence)
+ return ;
+
+ const int sizeBuffer = 100000;
+ char t[sizeBuffer+100]; *t = 0;
+ va_list ap;
+
+ cMutexLock lock(&logMutex);
+
+ va_start(ap, format);
+
+ snprintf(t, sizeBuffer, "BOBLIGHT: ");
+ vsnprintf(t+strlen(t), sizeBuffer-strlen(t), format, ap);
+
+ syslog(LOG_ERR, "%s", t);
+
+ va_end(ap);
+}
+
+//***************************************************************************
+// Error
+//***************************************************************************
+
+int error(const char* format, ...)
+{
+ const int sizeBuffer = 100000;
+ char t[sizeBuffer+100]; *t = 0;
+ va_list ap;
+
+ cMutexLock lock(&logMutex);
+
+ va_start(ap, format);
+
+ snprintf(t, sizeBuffer, "BOBLIGHT: ");
+ vsnprintf(t+strlen(t), sizeBuffer-strlen(t), format, ap);
+
+ syslog(LOG_ERR, "%s", t);
+
+ va_end(ap);
+
+ return fail;
+}
+
+//***************************************************************************
+// msNow
+//***************************************************************************
+
+MsTime msNow()
+{
+ timeval tv;
+
+ gettimeofday(&tv, 0);
+
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..747a085
--- /dev/null
+++ b/common.h
@@ -0,0 +1,45 @@
+/*
+ * common.h: EPG2VDR plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __COMMON_H
+#define __COMMON_H
+
+//***************************************************************************
+//
+//***************************************************************************
+
+enum Misc
+{
+ success = 0,
+ done = success,
+ fail = -1,
+ ignore = -2,
+ na = -1,
+ yes = 1,
+ on = 1,
+ off = 0,
+ no = 0,
+ TB = 1
+};
+
+//***************************************************************************
+// Time
+//***************************************************************************
+
+typedef unsigned long long MsTime;
+
+MsTime msNow();
+
+//***************************************************************************
+// Tell
+//***************************************************************************
+
+void tell(int eloquence, const char* format, ...);
+int error(const char* format, ...);
+
+//***************************************************************************
+#endif //___COMMON_H
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..02e33ad
--- /dev/null
+++ b/config.c
@@ -0,0 +1,60 @@
+/*
+ * config.c
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "config.h"
+#include "common.h"
+
+
+//***************************************************************************
+// Global Configuration
+//***************************************************************************
+
+cBobConfig cfg;
+
+//***************************************************************************
+// cConfigData
+//***************************************************************************
+
+cBobConfig::cBobConfig()
+{
+ // to be configured
+ frequence = 25;
+ threshold = 20;
+ gamma = 10;
+ value = 80;
+ saturation = 30;
+ speed = 60;
+ autospeed = 0;
+ interpolation = 1; //bool
+ priority = 128;
+
+ detectCineBars = cbBoth;
+
+ loglevel = 0;
+
+ showMainmenu = yes;
+ viewMode = vmAtmo;
+ fixedR = 111;
+ fixedG = 101;
+ fixedB = 0;
+
+ dirty = 0;
+}
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..9978928
--- /dev/null
+++ b/config.h
@@ -0,0 +1,71 @@
+/*
+ * config.h
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BOB_CONFIG_H
+#define __BOB_CONFIG_H
+
+#include "common.h"
+#include "ambiservice.h"
+
+//***************************************************************************
+// Configuration
+//***************************************************************************
+
+class cBobConfig : public cAmbiService
+{
+ public:
+
+ cBobConfig();
+
+ // adjust
+
+ int frequence;
+ int threshold;
+ int gamma;
+ int value;
+ int saturation;
+ int speed;
+ int autospeed;
+
+ int interpolation; //bool
+ int priority;
+
+ // technical
+
+ ViewMode viewMode;
+ int fixedR;
+ int fixedG;
+ int fixedB;
+
+ int showMainmenu; //bool
+ Cinebars detectCineBars;
+
+ int loglevel;
+
+ int dirty;
+
+};
+
+//***************************************************************************
+// Global Configuration
+//***************************************************************************
+
+extern cBobConfig cfg;
+
+#endif
diff --git a/lib/boblight-functions.h b/lib/boblight-functions.h
new file mode 100644
index 0000000..0e0731e
--- /dev/null
+++ b/lib/boblight-functions.h
@@ -0,0 +1,41 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see <http://www.gnu.org/licenses/>.
+ */
+
+//these definitions can be expanded to make normal prototypes, or functionpointers and dlsym lines
+
+BOBLIGHT_FUNCTION(void*, boblight_init, ());
+BOBLIGHT_FUNCTION(void, boblight_destroy, (void* vpboblight));
+
+BOBLIGHT_FUNCTION(int, boblight_connect, (void* vpboblight, const char* address, int port, int usectimeout));
+BOBLIGHT_FUNCTION(int, boblight_setpriority, (void* vpboblight, int priority));
+BOBLIGHT_FUNCTION(const char*, boblight_geterror, (void* vpboblight));
+BOBLIGHT_FUNCTION(int, boblight_getnrlights, (void* vpboblight));
+BOBLIGHT_FUNCTION(const char*, boblight_getlightname, (void* vpboblight, int lightnr));
+
+BOBLIGHT_FUNCTION(int, boblight_getnroptions, (void* vpboblight));
+BOBLIGHT_FUNCTION(const char*, boblight_getoptiondescript,(void* vpboblight, int option));
+BOBLIGHT_FUNCTION(int, boblight_setoption, (void* vpboblight, int lightnr, const char* option));
+BOBLIGHT_FUNCTION(int, boblight_getoption, (void* vpboblight, int lightnr, const char* option, const char** output));
+
+BOBLIGHT_FUNCTION(void, boblight_setscanrange, (void* vpboblight, int width, int height));
+
+BOBLIGHT_FUNCTION(int, boblight_addpixel, (void* vpboblight, int lightnr, int* rgb));
+BOBLIGHT_FUNCTION(void, boblight_addpixelxy, (void* vpboblight, int x, int y, int* rgb));
+
+BOBLIGHT_FUNCTION(int, boblight_sendrgb, (void* vpboblight, int sync, int* outputused));
+BOBLIGHT_FUNCTION(int, boblight_ping, (void* vpboblight, int* outputused));
diff --git a/lib/boblight.cpp b/lib/boblight.cpp
new file mode 100644
index 0000000..6c52136
--- /dev/null
+++ b/lib/boblight.cpp
@@ -0,0 +1,120 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "boblight.h"
+#include "boblight_client.h"
+
+using namespace boblight;
+
+//C wrapper for C++ class
+
+void* boblight_init()
+{
+ CBoblight* boblight = new CBoblight;
+ return reinterpret_cast<void*>(boblight);
+}
+
+void boblight_destroy(void* vpboblight)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ delete boblight;
+}
+
+int boblight_connect(void* vpboblight, const char* address, int port, int usectimeout)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->Connect(address, port, usectimeout);
+}
+
+int boblight_setpriority(void* vpboblight, int priority)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->SetPriority(priority);
+}
+
+const char* boblight_geterror(void* vpboblight)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->GetError();
+}
+
+int boblight_getnrlights(void* vpboblight)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->GetNrLights();
+}
+
+const char* boblight_getlightname(void* vpboblight, int lightnr)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->GetLightName(lightnr);
+}
+
+int boblight_getnroptions(void* vpboblight)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->GetNrOptions();
+}
+
+const char* boblight_getoptiondescript(void* vpboblight, int option)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->GetOptionDescription(option);
+}
+
+int boblight_setoption(void* vpboblight, int lightnr, const char* option)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->SetOption(lightnr, option);
+}
+
+int boblight_getoption(void* vpboblight, int lightnr, const char* option, const char** output)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->GetOption(lightnr, option, output);
+}
+
+void boblight_setscanrange(void* vpboblight, int width, int height)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ boblight->SetScanRange(width, height);
+}
+
+int boblight_addpixel(void* vpboblight, int lightnr, int* rgb)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->AddPixel(lightnr, rgb);
+}
+
+void boblight_addpixelxy(void* vpboblight, int x, int y, int* rgb)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ boblight->AddPixel(rgb, x, y);
+}
+
+int boblight_sendrgb(void* vpboblight, int sync, int* outputused)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->SendRGB(sync, outputused);
+}
+
+int boblight_ping(void* vpboblight, int* outputused)
+{
+ CBoblight* boblight = reinterpret_cast<CBoblight*>(vpboblight);
+ return boblight->Ping(outputused, true);
+}
diff --git a/lib/boblight.h b/lib/boblight.h
new file mode 100644
index 0000000..c596357
--- /dev/null
+++ b/lib/boblight.h
@@ -0,0 +1,103 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see <http://www.gnu.org/licenses/>.
+ */
+
+//if you define BOBLIGHT_DLOPEN, all boblight functions are defined as pointers
+//you can then call boblight_loadlibrary to load libboblight with dlopen and the function pointers with dlsym
+//if you pass NULL to boblight_loadlibrary's first argument, the default filename for libboblight is used
+//if boblight_loadlibrary returns NULL, dlopen and dlsym went ok, if not it returns a char* from dlerror
+
+//if you want to use the boblight functions from multiple files, you can define BOBLIGHT_DLOPEN in one file,
+//and define BOBLIGHT_DLOPEN_EXTERN in the other file, the functionpointers are then defined as extern
+
+#ifndef LIBBOBLIGHT
+#define LIBBOBLIGHT
+
+ #if !defined(BOBLIGHT_DLOPEN) && !defined(BOBLIGHT_DLOPEN_EXTERN)
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ //generate normal prototypes
+ #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) returnvalue name arguments
+ #include "boblight-functions.h"
+ #undef BOBLIGHT_FUNCTION
+
+ #ifdef __cplusplus
+ }
+ #endif
+
+ #elif defined(BOBLIGHT_DLOPEN)
+
+ #include <dlfcn.h>
+ #include <stddef.h>
+
+ //generate function pointers
+ #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) returnvalue (* name ) arguments = NULL
+ #include "boblight-functions.h"
+ #undef BOBLIGHT_FUNCTION
+
+ #ifdef __cplusplus
+ #define BOBLIGHT_CAST(value) reinterpret_cast<value>
+ #else
+ #define BOBLIGHT_CAST(value) (value)
+ #endif
+
+ //gets a functionpointer from dlsym, and returns char* from dlerror if it didn't work
+ #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) \
+ name = BOBLIGHT_CAST(returnvalue (*) arguments)(dlsym(p_boblight, #name)); \
+ { char* error = dlerror(); if (error) return error; }
+
+ void* p_boblight = NULL; //where we put the lib
+
+ //load function pointers
+ char* boblight_loadlibrary(const char* filename)
+ {
+ if (filename == NULL)
+ filename = "libboblight.so";
+
+ if (p_boblight != NULL)
+ {
+ dlclose(p_boblight);
+ p_boblight = NULL;
+ }
+
+ p_boblight = dlopen(filename, RTLD_NOW);
+ if (p_boblight == NULL)
+ return dlerror();
+
+ //generate dlsym lines
+ #include "boblight-functions.h"
+
+ return NULL;
+ }
+ #undef BOBLIGHT_FUNCTION
+ #undef BOBLIGHT_CAST
+
+ //you can define BOBLIGHT_DLOPEN_EXTERN when you load the library in another file
+ #elif defined(BOBLIGHT_DLOPEN_EXTERN)
+
+ extern char* boblight_loadlibrary(const char* filename);
+ extern void* p_boblight;
+ #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) extern returnvalue (* name ) arguments
+ #include "boblight-functions.h"
+ #undef BOBLIGHT_FUNCTION
+
+ #endif //BOBLIGHT_DLOPEN_EXTERN
+#endif //LIBBOBLIGHT
+
diff --git a/lib/boblight_client.cpp b/lib/boblight_client.cpp
new file mode 100644
index 0000000..dfab262
--- /dev/null
+++ b/lib/boblight_client.cpp
@@ -0,0 +1,704 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "util/inclstdint.h"
+
+#include <string>
+#include <iostream> //debug
+#include <sstream>
+
+#include "boblight_client.h"
+#include "util/misc.h"
+#include "util/timeutils.h"
+#include "protocolversion.h"
+
+using namespace std;
+using namespace boblight;
+
+#define GAMMASIZE (sizeof(m_gammacurve) / sizeof(m_gammacurve[0]))
+
+CLight::CLight()
+{
+ #define BOBLIGHT_OPTION(name, type, min, max, default, variable, postprocess) variable = default;
+ #include "options.h"
+ #undef BOBLIGHT_OPTION
+
+ m_singlechange = 0.0;
+
+ m_width = -1;
+ m_height = -1;
+
+ memset(m_rgb, 0, sizeof(m_rgb));
+ m_rgbcount = 0;
+ memset(m_prevrgb, 0, sizeof(m_prevrgb));
+ memset(m_hscanscaled, 0, sizeof(m_hscanscaled));
+ memset(m_vscanscaled, 0, sizeof(m_vscanscaled));
+
+ for (int i = 0; i < GAMMASIZE; i++)
+ m_gammacurve[i] = i;
+}
+
+string CLight::SetOption(const char* option, bool& send)
+{
+ string stroption = option;
+ string strname;
+
+ send = false;
+
+ if (!GetWord(stroption, strname))
+ return "emtpy option"; //string with only whitespace
+
+ #define BOBLIGHT_OPTION(name, type, min, max, default, variable, postprocess) \
+ if (strname == #name) \
+ { \
+ type value; \
+ if (#type == "bool")\
+ {\
+ if (!StrToBool(stroption, *(bool*)(&value)))\
+ return "invalid value " + stroption + " for option " + strname + " with type " + #type; \
+ }\
+ else\
+ {\
+ stringstream stream; \
+ stream << stroption; \
+ stream >> value; \
+ if (stream.fail()) return "invalid value " + stroption + " for option " + strname + " with type " + #type; \
+ \
+ }\
+ variable = value; \
+ postprocess\
+ \
+ return ""; \
+ }
+ #include "options.h"
+ #undef BOBLIGHT_OPTION
+
+ return "unknown option " + strname;
+}
+
+std::string CLight::GetOption(const char* option, std::string& output)
+{
+ string stroption = option;
+ string strname;
+
+ if (!GetWord(stroption, strname))
+ return "emtpy option"; //string with only whitespace
+
+ #define BOBLIGHT_OPTION(name, type, min, max, default, variable, postprocess) \
+ if (#name == strname)\
+ {\
+ output = ToString(variable);\
+ return "";\
+ }
+ #include "options.h"
+ #undef BOBLIGHT_OPTION
+
+ return "unknown option";
+}
+
+void CLight::AddPixel(int* rgb)
+{
+ if (rgb[0] >= m_threshold || rgb[1] >= m_threshold || rgb[2] >= m_threshold)
+ {
+ if (m_gamma == 1.0)
+ {
+ m_rgb[0] += Clamp(rgb[0], 0, 255);
+ m_rgb[1] += Clamp(rgb[1], 0, 255);
+ m_rgb[2] += Clamp(rgb[2], 0, 255);
+ }
+ else
+ {
+ m_rgb[0] += m_gammacurve[Clamp(rgb[0], 0, GAMMASIZE - 1)];
+ m_rgb[1] += m_gammacurve[Clamp(rgb[1], 0, GAMMASIZE - 1)];
+ m_rgb[2] += m_gammacurve[Clamp(rgb[2], 0, GAMMASIZE - 1)];
+ }
+ }
+ m_rgbcount++;
+}
+
+void CLight::GetRGB(float* rgb)
+{
+ //if no pixels are set, the denominator is 0, so just return black
+ if (m_rgbcount == 0)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ rgb[i] = 0.0f;
+ m_rgb[i] = 0.0f;
+ }
+
+ return;
+ }
+
+ //convert from numerator/denominator to float
+ for (int i = 0; i < 3; i++)
+ {
+ rgb[i] = Clamp(m_rgb[i] / (float)m_rgbcount / 255.0f, 0.0f, 1.0f);
+ m_rgb[i] = 0.0f;
+ }
+ m_rgbcount = 0;
+
+ //this tries to set the speed based on how fast the input is changing
+ //it needs sync mode to work properly
+ if (m_autospeed > 0.0)
+ {
+ float change = Abs(rgb[0] - m_prevrgb[0]) + Abs(rgb[1] - m_prevrgb[1]) + Abs(rgb[2] - m_prevrgb[2]);
+ change /= 3.0;
+
+ //only apply singlechange if it's large enough, otherwise we risk sending it continously
+ if (change > 0.001)
+ m_singlechange = Clamp(change * m_autospeed / 10.0, 0.0, 1.0);
+ else
+ m_singlechange = 0.0;
+ }
+
+ memcpy(m_prevrgb, rgb, sizeof(m_prevrgb));
+
+ //we need some hsv adjustments
+ if (m_value != 1.0 || m_valuerange[0] != 0.0 || m_valuerange[1] != 1.0 ||
+ m_saturation != 1.0 || m_satrange[0] != 0.0 || m_satrange[1] != 1.0)
+ {
+ //rgb - hsv conversion, thanks wikipedia!
+ float hsv[3];
+ float max = Max(rgb[0], rgb[1], rgb[2]);
+ float min = Min(rgb[0], rgb[1], rgb[2]);
+
+ if (min == max) //grayscale
+ {
+ hsv[0] = -1.0f; //undefined
+ hsv[1] = 0.0; //no saturation
+ hsv[2] = min; //value
+ }
+ else
+ {
+ if (max == rgb[0]) //red zone
+ {
+ hsv[0] = (60.0f * ((rgb[1] - rgb[2]) / (max - min)) + 360.0f);
+ while (hsv[0] >= 360.0f)
+ hsv[0] -= 360.0f;
+ }
+ else if (max == rgb[1]) //green zone
+ {
+ hsv[0] = 60.0f * ((rgb[2] - rgb[0]) / (max - min)) + 120.0f;
+ }
+ else if (max == rgb[2]) //blue zone
+ {
+ hsv[0] = 60.0f * ((rgb[0] - rgb[1]) / (max - min)) + 240.0f;
+ }
+
+ hsv[1] = (max - min) / max; //saturation
+ hsv[2] = max; //value
+ }
+
+ //saturation and value adjustment
+ hsv[1] = Clamp(hsv[1] * m_saturation, m_satrange[0], m_satrange[1]);
+ hsv[2] = Clamp(hsv[2] * m_value, m_valuerange[0], m_valuerange[1]);
+
+ if (hsv[0] == -1.0f) //grayscale
+ {
+ for (int i = 0; i < 3; i++)
+ rgb[i] = hsv[2];
+ }
+ else
+ {
+ int hi = (int)(hsv[0] / 60.0f) % 6;
+ float f = (hsv[0] / 60.0f) - (float)(int)(hsv[0] / 60.0f);
+
+ float s = hsv[1];
+ float v = hsv[2];
+ float p = v * (1.0f - s);
+ float q = v * (1.0f - f * s);
+ float t = v * (1.0f - (1.0f - f) * s);
+
+ if (hi == 0)
+ { rgb[0] = v; rgb[1] = t; rgb[2] = p; }
+ else if (hi == 1)
+ { rgb[0] = q; rgb[1] = v; rgb[2] = p; }
+ else if (hi == 2)
+ { rgb[0] = p; rgb[1] = v; rgb[2] = t; }
+ else if (hi == 3)
+ { rgb[0] = p; rgb[1] = q; rgb[2] = v; }
+ else if (hi == 4)
+ { rgb[0] = t; rgb[1] = p; rgb[2] = v; }
+ else if (hi == 5)
+ { rgb[0] = v; rgb[1] = p; rgb[2] = q; }
+ }
+
+ for (int i = 0; i < 3; i++)
+ rgb[i] = Clamp(rgb[i], 0.0f, 1.0f);
+ }
+}
+
+//scale the light's scanrange to the dimensions set with boblight_setscanrange()
+void CLight::SetScanRange(int width, int height)
+{
+ m_width = width;
+ m_height = height;
+
+ m_hscanscaled[0] = Round32(m_hscan[0] / 100.0 * ((float)width - 1));
+ m_hscanscaled[1] = Round32(m_hscan[1] / 100.0 * ((float)width - 1));
+ m_vscanscaled[0] = Round32(m_vscan[0] / 100.0 * ((float)height - 1));
+ m_vscanscaled[1] = Round32(m_vscan[1] / 100.0 * ((float)height - 1));
+}
+
+int CBoblight::Connect(const char* address, int port, int usectimeout)
+{
+ CMessage message;
+ CTcpData data;
+ int64_t now;
+ int64_t target;
+ string word;
+
+ //set address
+ m_usectimeout = usectimeout;
+ if (address)
+ m_address = address;
+ else
+ m_address = "127.0.0.1";
+
+ //set port
+ if (port >= 0)
+ m_port = port;
+ else
+ m_port = 19333;
+
+ //try to open a tcp connection
+ if (m_socket.Open(m_address, m_port, m_usectimeout) != SUCCESS)
+ {
+ m_error = m_socket.GetError();
+ return 0;
+ }
+
+ //write hello to the server, we should get hello back
+ if (!WriteDataToSocket("hello\n"))
+ return 0;
+
+ if (!ReadDataToQueue())
+ return 0;
+
+ message = m_messagequeue.GetMessage();
+ if (!ParseWord(message, "hello"))
+ {
+ m_error = m_address + ":" + ToString(m_port) + " sent gibberish";
+ return 0;
+ }
+
+ //get the protocol version from the server
+ if (!WriteDataToSocket("get version\n"))
+ return 0;
+
+ if (!ReadDataToQueue())
+ return 0;
+
+ message = m_messagequeue.GetMessage();
+
+ if (!ParseWord(message, "version") || !GetWord(message.message, word))
+ {
+ m_error = m_address + ":" + ToString(m_port) + " sent gibberish";
+ return 0;
+ }
+
+ //if we don't get the same protocol version back as we have, we can't work together
+ if (word != PROTOCOLVERSION)
+ {
+ m_error = "version mismatch, " + m_address + ":" + ToString(m_port) + " has version \"" + word +
+ "\", libboblight has version \"" + PROTOCOLVERSION + "\"";
+ return 0;
+ }
+
+ //get lights info, like number, names and area
+ if (!WriteDataToSocket("get lights\n"))
+ return 0;
+
+ if (!ReadDataToQueue())
+ return 0;
+
+ message = m_messagequeue.GetMessage();
+ if (!ParseLights(message))
+ {
+ m_error = m_address + ":" + ToString(m_port) + " sent gibberish";
+ return 0;
+ }
+
+ return 1;
+}
+
+CBoblight::CBoblight()
+{
+ int padsize = 1;
+ //get option name pad size so it looks pretty
+ #define BOBLIGHT_OPTION(name, type, min, max, default, variable, postprocess) \
+ if (strlen(#name) + 1 > padsize)\
+ padsize = strlen(#name) + 1;
+ #include "options.h"
+ #undef BOBLIGHT_OPTION
+
+ //stick in a line that describes the options
+ string option = "name";
+ option.append(Max(padsize - option.length(), 1), ' ');
+ option += "type min max default";
+ m_options.push_back(option);
+
+ //fill vector with option strings
+ #define BOBLIGHT_OPTION(name, type, min, max, default, variable, postprocess) \
+ {\
+ string option = #name;\
+ option.append(padsize - strlen(#name), ' ');\
+ \
+ option += #type;\
+ option.append(Max(8 - strlen(#type), 1), ' ');\
+ \
+ option += #min;\
+ option.append(Max(8 - strlen(#min), 1), ' ');\
+ \
+ option += #max;\
+ option.append(Max(8 - strlen(#max), 1), ' ');\
+ \
+ if (strcmp(#default, "-1.0") == 0)\
+ option += "set by boblightd";\
+ else\
+ option += #default;\
+ \
+ m_options.push_back(option);\
+ }
+ #include "options.h"
+ #undef BOBLIGHT_OPTION
+}
+
+//reads from socket until timeout or one message has arrived
+bool CBoblight::ReadDataToQueue()
+{
+ CTcpData data;
+ int64_t now = GetTimeUs();
+ int64_t target = now + m_usectimeout;
+ int nrmessages = m_messagequeue.GetNrMessages();
+
+ while (now < target && m_messagequeue.GetNrMessages() == nrmessages)
+ {
+ if (m_socket.Read(data) != SUCCESS)
+ {
+ m_error = m_socket.GetError();
+ return false;
+ }
+
+ m_messagequeue.AddData(data.GetData());
+
+ if (m_messagequeue.GetRemainingDataSize() > MAXDATA)
+ {
+ m_error = m_address + ":" + ToString(m_port) + " sent too much data";
+ return false;
+ }
+
+ now = GetTimeUs();
+ }
+
+ if (nrmessages == m_messagequeue.GetNrMessages())
+ {
+ m_error = m_address + ":" + ToString(m_port) + " read timed out";
+ return false;
+ }
+ return true;
+}
+
+bool CBoblight::WriteDataToSocket(std::string strdata)
+{
+ CTcpData data;
+ data.SetData(strdata);
+
+ if (m_socket.Write(data) != SUCCESS)
+ {
+ m_error = m_socket.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+//removes one word from the string in the messages, and compares it to wordtocmp
+bool CBoblight::ParseWord(CMessage& message, std::string wordtocmp)
+{
+ string readword;
+ if (!GetWord(message.message, readword) || readword != wordtocmp)
+ return false;
+
+ return true;
+}
+
+bool CBoblight::ParseLights(CMessage& message)
+{
+ string word;
+ int nrlights;
+
+ //first word in the message is "lights", second word is the number of lights
+ if (!ParseWord(message, "lights") || !GetWord(message.message, word) || !StrToInt(word, nrlights) || nrlights < 1)
+ return false;
+
+ for (int i = 0; i < nrlights; i++)
+ {
+ CLight light;
+
+ //read some data to the message queue if we have no messages
+ if (m_messagequeue.GetNrMessages() == 0)
+ {
+ if (!ReadDataToQueue())
+ return false;
+ }
+
+ message = m_messagequeue.GetMessage();
+
+ //first word sent is "light, second one is the name
+ if (!ParseWord(message, "light") || !GetWord(message.message, light.m_name))
+ {
+ return false;
+ }
+
+ //third one is "scan"
+ if (!ParseWord(message, "scan"))
+ return false;
+
+ //now we read the scanrange
+ string scanarea;
+ for (int i = 0; i < 4; i++)
+ {
+ if (!GetWord(message.message, word))
+ return false;
+
+ scanarea += word + " ";
+ }
+
+ ConvertFloatLocale(scanarea); //workaround for locale mismatch (, and .)
+
+ if (sscanf(scanarea.c_str(), "%f %f %f %f", light.m_vscan, light.m_vscan + 1, light.m_hscan, light.m_hscan + 1) != 4)
+ return false;
+
+ m_lights.push_back(light);
+ }
+ return true;
+}
+
+const char* CBoblight::GetLightName(int lightnr)
+{
+ if (lightnr < 0) //negative lights don't exist, so we set it to an invalid number to get the error message
+ lightnr = m_lights.size();
+
+ if (CheckLightExists(lightnr))
+ return m_lights[lightnr].m_name.c_str();
+
+ return NULL;
+}
+
+int CBoblight::SetPriority(int priority)
+{
+ string data = "set priority " + ToString(priority) + "\n";
+
+ return WriteDataToSocket(data);
+}
+
+bool CBoblight::CheckLightExists(int lightnr, bool printerror /*= true*/)
+{
+ if (lightnr >= (int)m_lights.size())
+ {
+ if (printerror)
+ {
+ m_error = "light " + ToString(lightnr) + " doesn't exist (have " + ToString(m_lights.size()) + " lights)";
+ }
+ return false;
+ }
+ return true;
+}
+
+void CBoblight::SetScanRange(int width, int height)
+{
+ for (int i = 0; i < m_lights.size(); i++)
+ {
+ m_lights[i].SetScanRange(width, height);
+ }
+}
+
+int CBoblight::AddPixel(int lightnr, int* rgb)
+{
+ if (!CheckLightExists(lightnr))
+ return 0;
+
+ if (lightnr < 0)
+ {
+ for (int i = 0; i < m_lights.size(); i++)
+ m_lights[i].AddPixel(rgb);
+ }
+ else
+ {
+ m_lights[lightnr].AddPixel(rgb);
+ }
+
+ return 1;
+}
+
+void CBoblight::AddPixel(int* rgb, int x, int y)
+{
+ for (int i = 0; i < m_lights.size(); i++)
+ {
+ if (x >= m_lights[i].m_hscanscaled[0] && x <= m_lights[i].m_hscanscaled[1] &&
+ y >= m_lights[i].m_vscanscaled[0] && y <= m_lights[i].m_vscanscaled[1])
+ {
+ m_lights[i].AddPixel(rgb);
+ }
+ }
+}
+
+int CBoblight::SendRGB(int sync, int* outputused)
+{
+ string data;
+
+ for (int i = 0; i < m_lights.size(); i++)
+ {
+ float rgb[3];
+ m_lights[i].GetRGB(rgb);
+ data += "set light " + m_lights[i].m_name + " rgb " + ToString(rgb[0]) + " " + ToString(rgb[1]) + " " + ToString(rgb[2]) + "\n";
+ if (m_lights[i].m_autospeed > 0.0 && m_lights[i].m_singlechange > 0.0)
+ data += "set light " + m_lights[i].m_name + " singlechange " + ToString(m_lights[i].m_singlechange) + "\n";
+ }
+
+ //send a message that we want devices to sync to our input
+ if (sync)
+ data += "sync\n";
+
+ //if we want to check if our output is used, send a ping message
+ if (outputused)
+ data += "ping\n";
+
+ if (!WriteDataToSocket(data))
+ return 0;
+
+ if (outputused)
+ return Ping(outputused, false);
+ else
+ return 1;
+}
+
+int CBoblight::Ping(int* outputused, bool send)
+{
+ string word;
+
+ if (send)
+ {
+ if (!WriteDataToSocket("ping\n"))
+ return 0;
+ }
+
+ if (!ReadDataToQueue())
+ return 0;
+
+ CMessage message = m_messagequeue.GetMessage();
+
+ if (!GetWord(message.message, word) || word != "ping")
+ {
+ m_error = m_address + ":" + ToString(m_port) + " sent gibberish";
+ return 0;
+ }
+
+ //client can set outputused to NULL
+ if (outputused)
+ {
+ if (!GetWord(message.message, word) || !StrToInt(word, *outputused))
+ {
+ m_error = m_address + ":" + ToString(m_port) + " sent gibberish";
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int CBoblight::GetNrOptions()
+{
+ return m_options.size();
+}
+
+const char* CBoblight::GetOptionDescription(int option)
+{
+ if (option < 0 || option >= m_options.size())
+ return NULL;
+
+ return m_options[option].c_str();
+}
+
+int CBoblight::SetOption(int lightnr, const char* option)
+{
+ string error;
+ string data;
+ bool send;
+
+ if (!CheckLightExists(lightnr))
+ return 0;
+
+ if (lightnr < 0)
+ {
+ for (int i = 0; i < m_lights.size(); i++)
+ {
+ error = m_lights[i].SetOption(option, send);
+ if (!error.empty())
+ {
+ m_error = error;
+ return 0;
+ }
+ if (send)
+ {
+ data += "set light " + m_lights[i].m_name + " " + option + "\n";
+ }
+ }
+ }
+ else
+ {
+ error = m_lights[lightnr].SetOption(option, send);
+ if (!error.empty())
+ {
+ m_error = error;
+ return 0;
+ }
+ if (send)
+ {
+ data += "set light " + m_lights[lightnr].m_name + " " + option + "\n";
+ }
+ }
+
+ if (!WriteDataToSocket(data))
+ return 0;
+
+ return 1;
+}
+
+int CBoblight::GetOption(int lightnr, const char* option, const char** output)
+{
+ if (lightnr < 0) //negative lights don't exist, so we set it to an invalid number to get the error message
+ lightnr = m_lights.size();
+
+ if (!CheckLightExists(lightnr))
+ return 0;
+
+ string error = m_lights[lightnr].GetOption(option, m_lastoption);
+ if (!error.empty())
+ {
+ m_error = error;
+ return 0;
+ }
+
+ *output = m_lastoption.c_str();
+
+ return 1;
+}
diff --git a/lib/boblight_client.h b/lib/boblight_client.h
new file mode 100644
index 0000000..aac141d
--- /dev/null
+++ b/lib/boblight_client.h
@@ -0,0 +1,115 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CBOBLIGHT
+#define CBOBLIGHT
+
+#include <string>
+#include <vector>
+
+#include "util/tcpsocket.h"
+#include "util/messagequeue.h"
+
+namespace boblight
+{
+ class CLight
+ {
+ public:
+ CLight();
+
+ std::string SetOption(const char* option, bool& send);
+ std::string GetOption(const char* option, std::string& output);
+
+ void SetScanRange(int width, int height);
+ void AddPixel(int* rgb);
+
+ std::string m_name;
+ float m_speed;
+ float m_autospeed;
+ float m_singlechange;
+
+ bool m_interpolation;
+ bool m_use;
+
+ float m_value;
+ float m_valuerange[2];
+ float m_saturation;
+ float m_satrange[2];
+ int m_threshold;
+ float m_gamma;
+ float m_gammacurve[256];
+
+ float m_rgb[3];
+ int m_rgbcount;
+ float m_prevrgb[3];
+ void GetRGB(float* rgb);
+
+ float m_hscan[2];
+ float m_vscan[2];
+ int m_width;
+ int m_height;
+ int m_hscanscaled[2];
+ int m_vscanscaled[2];
+ };
+
+ class CBoblight
+ {
+ public:
+ CBoblight();
+
+ int Connect(const char* address, int port, int usectimeout);
+ const char* GetError() { return m_error.c_str(); }
+
+ int GetNrLights() { return m_lights.size(); }
+ const char* GetLightName (int lightnr);
+
+ int SetPriority (int priority);
+ void SetScanRange (int width, int height);
+
+ int AddPixel(int lightnr, int* rgb);
+ void AddPixel(int* rgb, int x, int y);
+
+ int SendRGB(int sync, int* outputused);
+ int Ping(int* outputused, bool send);
+
+ int GetNrOptions();
+ const char* GetOptionDescription(int option);
+ int SetOption(int lightnr, const char* option);
+ int GetOption(int lightnr, const char* option, const char** output);
+
+ private:
+ CTcpClientSocket m_socket;
+ std::string m_address;
+ int m_port;
+ std::string m_error;
+ CMessageQueue m_messagequeue;
+ int m_usectimeout;
+
+ bool ReadDataToQueue();
+ bool WriteDataToSocket(std::string strdata);
+ bool ParseWord(CMessage& message, std::string wordtocmp);
+ bool ParseLights(CMessage& message);
+ bool CheckLightExists(int lightnr, bool printerror = true);
+
+ std::vector<CLight> m_lights;
+
+ std::vector<std::string> m_options;
+ std::string m_lastoption; //place to store the last option retrieved by GetOption
+ };
+}
+#endif //CBOBLIGHT
diff --git a/lib/options.h b/lib/options.h
new file mode 100644
index 0000000..05c3e82
--- /dev/null
+++ b/lib/options.h
@@ -0,0 +1,41 @@
+/*
+ * boblight
+ * Copyright (C) Bob 2009
+ *
+ * boblight 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * boblight 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, see <http://www.gnu.org/licenses/>.
+ */
+
+// name type min max default variable post-process
+BOBLIGHT_OPTION(speed, float, 0.0, 100.0, 100.0, m_speed, m_speed = Clamp(m_speed, 0.0, 100.0); send = true;)
+BOBLIGHT_OPTION(autospeed, float, 0, 100.0, 0.0, m_autospeed, m_autospeed = Max(m_autospeed, 0.0);)
+BOBLIGHT_OPTION(interpolation, bool, false, true, false, m_interpolation, send = true;)
+BOBLIGHT_OPTION(use, bool, false, true, true, m_use, send = true;)
+BOBLIGHT_OPTION(saturation, float, 0.0, 20.0, 1.0, m_saturation, m_saturation = Max(m_saturation, 0.0);)
+BOBLIGHT_OPTION(saturationmin, float, 0.0, 1.0, 0.0, m_satrange[0], m_satrange[0] = Clamp(m_satrange[0], 0.0, m_satrange[1]);)
+BOBLIGHT_OPTION(saturationmax, float, 0.0, 1.0, 1.0, m_satrange[1], m_satrange[1] = Clamp(m_satrange[1], m_satrange[0], 1.0);)
+BOBLIGHT_OPTION(value, float, 0.0, 20.0, 1.0, m_value, m_value = Max(m_value, 0.0);)
+BOBLIGHT_OPTION(valuemin, float, 0.0, 1.0, 0.0, m_valuerange[0], m_valuerange[0] = Clamp(m_valuerange[0], 0.0, m_valuerange[1]);)
+BOBLIGHT_OPTION(valuemax, float, 0.0, 1.0, 1.0, m_valuerange[1], m_valuerange[1] = Clamp(m_valuerange[1], m_valuerange[0], 1.0);)
+BOBLIGHT_OPTION(threshold, int, 0, 255, 0, m_threshold, m_threshold = Clamp(m_threshold, 0, 255);)
+
+BOBLIGHT_OPTION(gamma, float, 0.0, 10.0, 1.0, m_gamma, m_gamma = Max(m_gamma, 0.0); \
+ for (int i = 0; i < GAMMASIZE; i++) \
+ m_gammacurve[i] = pow((float)i / ((float)GAMMASIZE - 1.0f), m_gamma) * (GAMMASIZE - 1.0f);)
+
+BOBLIGHT_OPTION(hscanstart, float, 0.0, 100.0, -1.0, m_hscan[0], m_hscan[0] = Clamp(m_hscan[0], 0.0, m_hscan[1]); SetScanRange(m_width, m_height);)
+BOBLIGHT_OPTION(hscanend, float, 0.0, 100.0, -1.0, m_hscan[1], m_hscan[1] = Clamp(m_hscan[1], m_hscan[0], 100.0); SetScanRange(m_width, m_height);)
+BOBLIGHT_OPTION(vscanstart, float, 0.0, 100.0, -1.0, m_vscan[0], m_vscan[0] = Clamp(m_vscan[0], 0.0, m_vscan[1]); SetScanRange(m_width, m_height);)
+BOBLIGHT_OPTION(vscanend, float, 0.0, 100.0, -1.0, m_vscan[1], m_vscan[1] = Clamp(m_vscan[1], m_vscan[0], 100.0); SetScanRange(m_width, m_height);)
+
+
diff --git a/softhdservice.h b/softhdservice.h
new file mode 100644
index 0000000..3e98c7e
--- /dev/null
+++ b/softhdservice.h
@@ -0,0 +1,38 @@
+///
+/// @file softhddev_service.h @brief software HD device service header file.
+///
+/// Copyright (c) 2012 by durchflieger. All Rights Reserved.
+///
+/// Contributor(s):
+///
+/// License: AGPLv3
+///
+/// This program is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License as
+/// published by the Free Software Foundation, either version 3 of the
+/// License.
+///
+/// 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 Affero General Public License for more details.
+///
+/// $Id: softhdservice.h,v 1.2 2012/11/13 08:58:11 wendel Exp $
+//////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#define ATMO1_GRAB_SERVICE "SoftHDDevice-AtmoGrabService-v1.1"
+
+struct SoftHDDevice_AtmoGrabService_v1_1_t
+{
+ // [in/out]
+
+ int width;
+ int height;
+
+ // [out]
+
+ int size;
+ void* img;
+}; \ No newline at end of file
diff --git a/vdrboblight.c b/vdrboblight.c
new file mode 100644
index 0000000..20ae8a5
--- /dev/null
+++ b/vdrboblight.c
@@ -0,0 +1,412 @@
+/*
+ * vdrboblight.c
+ *
+ * Copyright (C) 2013 - Christian Völlinger
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <vdr/plugin.h>
+
+#include "config.h"
+#include "ambiservice.h"
+#include "ambithread.h"
+
+//***************************************************************************
+//
+//***************************************************************************
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "Boblight with data from softhddevice";
+static const char *MAINMENUENTRY = "Boblight";
+
+//***************************************************************************
+// Setup
+//***************************************************************************
+
+class cAmbiSetup : public cMenuSetupPage, public cAmbiService
+{
+ public:
+
+ cAmbiSetup();
+
+ protected:
+
+ virtual void Setup();
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Store();
+
+ const char* cineBars[cbCount];
+ const char* seduRGBOrders[6];
+ int rgbOrderIndex;
+};
+
+//***************************************************************************
+// Plugin
+//***************************************************************************
+
+class cPluginBoblight : public cPlugin
+{
+ public:
+
+ cPluginBoblight(void);
+ virtual ~cPluginBoblight();
+ virtual const char* Version(void) { return VERSION; }
+ virtual const char* Description(void) { return DESCRIPTION; }
+ virtual const char* CommandLineHelp(void) { return 0; }
+ virtual bool ProcessArgs(int argc, char* argv[]);
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual void Housekeeping(void) { };
+ virtual void MainThreadHook(void) { };
+ virtual cString Active(void);
+ virtual time_t WakeupTime(void);
+ virtual const char* MainMenuEntry(void) { return cfg.showMainmenu ? MAINMENUENTRY : 0; }
+ virtual cOsdObject* MainMenuAction(void);
+ virtual cMenuSetupPage* SetupMenu(void) { return new cAmbiSetup; }
+ virtual bool SetupParse(const char* Name, const char* Value);
+ virtual bool Service(const char* Id, void* Data = NULL);
+ virtual const char** SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char* Command, const char* Option, int &ReplyCode);
+
+ int startAtmo();
+ int stopAtmo();
+ cAmbiThread* update;
+
+ int isRunning()
+ {
+ if (!update)
+ return no;
+
+ return update->isRunning();
+ }
+};
+
+//***************************************************************************
+// Plugins Main Menu
+//***************************************************************************
+
+class cSeduPluginMenu : public cMenuSetupPage
+{
+ public:
+
+ cSeduPluginMenu(const char* title, cPluginBoblight* aPlugin);
+ virtual ~cSeduPluginMenu() { };
+
+ virtual eOSState ProcessKey(eKeys key);
+
+ protected:
+
+ void Store() { }
+ cPluginBoblight* plugin;
+};
+
+cSeduPluginMenu::cSeduPluginMenu(const char* title, cPluginBoblight* aPlugin)
+{
+ SetTitle(title ? title : "");
+ plugin = aPlugin;
+
+ Clear();
+
+ cOsdMenu::Add(new cMenuEditStraItem(tr("View Mode"),
+ (int*)&cfg.viewMode,
+ (int)cAmbiService::vmCount,
+ cAmbiService::viewModes));
+
+ Add(new cMenuEditIntItem(tr("Fixed Color Red"), &cfg.fixedR, 0, 255));
+ Add(new cMenuEditIntItem(tr("Fixed Color Green"), &cfg.fixedG, 0, 255));
+ Add(new cMenuEditIntItem(tr("Fixed Color Blue"), &cfg.fixedB, 0, 255));
+
+ SetHelp(0, 0, 0, 0);
+
+ Display();
+}
+
+//***************************************************************************
+// Process Key
+//***************************************************************************
+
+eOSState cSeduPluginMenu::ProcessKey(eKeys key)
+{
+ eOSState state = cOsdMenu::ProcessKey(key);
+
+ if (key == kLeft || key == kRight)
+ {
+ if (cfg.viewMode == cAmbiService::vmDetached && plugin->isRunning())
+ plugin->stopAtmo();
+ else if (cfg.viewMode != cAmbiService::vmDetached && !plugin->isRunning())
+ plugin->startAtmo();
+ }
+
+ if (state != osUnknown)
+ return state;
+
+ if (key == kOk)
+ {
+ SetupStore("FixedColorRed", cfg.fixedR);
+ SetupStore("FixedColorGreen", cfg.fixedG);
+ SetupStore("FixedColorBlue", cfg.fixedB);
+ SetupStore("ViewMode", (int)cfg.viewMode);
+
+ return osEnd;
+ }
+ return state;
+}
+
+//***************************************************************************
+// Plugin
+//***************************************************************************
+
+cPluginBoblight::cPluginBoblight(void)
+{
+ update = 0;
+}
+
+cPluginBoblight::~cPluginBoblight()
+{
+ stopAtmo();
+}
+
+bool cPluginBoblight::ProcessArgs(int argc, char* argv[])
+{
+ return true;
+}
+
+bool cPluginBoblight::Initialize(void)
+{
+ return true;
+}
+
+bool cPluginBoblight::Start(void)
+{
+ startAtmo();
+
+ return true;
+}
+
+int cPluginBoblight::startAtmo()
+{
+ if (update)
+ {
+ update->Stop();
+ delete update;
+ }
+
+ update = new cAmbiThread();
+ update->Start();
+
+ return done;
+}
+
+int cPluginBoblight::stopAtmo()
+{
+ if (update)
+ update->Stop();
+
+ delete update;
+ update = 0;
+
+ return done;
+}
+
+void cPluginBoblight::Stop(void)
+{
+ stopAtmo();
+}
+
+cString cPluginBoblight::Active(void)
+{
+ return 0;
+}
+
+time_t cPluginBoblight::WakeupTime(void)
+{
+ return 0;
+}
+
+cOsdObject* cPluginBoblight::MainMenuAction(void)
+{
+ return new cSeduPluginMenu(MAINMENUENTRY, this);
+}
+
+bool cPluginBoblight::SetupParse(const char* Name, const char* Value)
+{
+ if (!strcasecmp(Name, "LogLevel")) cfg.loglevel = atoi(Value);
+ else if (!strcasecmp(Name, "ShowMainmenu")) cfg.showMainmenu = atoi(Value);
+ else if (!strcasecmp(Name, "ViewMode")) cfg.viewMode = (cAmbiService::ViewMode)atoi(Value);
+
+ else if (!strcasecmp(Name, "DetectCineBars")) cfg.detectCineBars = (cAmbiService::Cinebars)atoi(Value);
+
+ else if (!strcasecmp(Name, "Frequence")) cfg.frequence = atoi(Value);
+ else if (!strcasecmp(Name, "Threshold")) cfg.threshold = atoi(Value);
+ else if (!strcasecmp(Name, "Value")) cfg.value = atoi(Value);
+ else if (!strcasecmp(Name, "Saturation")) cfg.saturation = atoi(Value);
+ else if (!strcasecmp(Name, "Speed")) cfg.speed = atoi(Value);
+ else if (!strcasecmp(Name, "Autospeed")) cfg.autospeed = atoi(Value);
+ else if (!strcasecmp(Name, "Interpolation")) cfg.interpolation = atoi(Value);
+ else if (!strcasecmp(Name, "Priority")) cfg.priority = atoi(Value);
+
+
+ else if (!strcasecmp(Name, "FixedColorRed")) cfg.fixedR = atoi(Value);
+ else if (!strcasecmp(Name, "FixedColorGreen")) cfg.fixedG = atoi(Value);
+ else if (!strcasecmp(Name, "FixedColorBlue")) cfg.fixedB = atoi(Value);
+
+ else
+ return false;
+
+ return true;
+}
+
+bool cPluginBoblight::Service(const char* Id, void* Data)
+{
+ return false;
+}
+
+cString cPluginBoblight::SVDRPCommand(const char* Command, const char* Option, int &ReplyCode)
+{
+ if (!update)
+ return "Error: Plugin not initialized!";
+
+ if (!strcasecmp(Command, "MODE"))
+ {
+ if (Option && strcasecmp(Option, "atmo") == 0)
+ {
+ cfg.viewMode = cAmbiService::vmAtmo;
+ startAtmo();
+ ReplyCode = 550;
+ return "atmo mode activated";
+ }
+ else if (Option && strcasecmp(Option, "fixed") == 0)
+ {
+ cfg.viewMode = cAmbiService::vmFixedCol;
+ startAtmo();
+ ReplyCode = 550;
+ return "fixed color activated";
+ }
+ else if (Option && strcasecmp(Option, "black") == 0)
+ {
+ cfg.viewMode = cAmbiService::vmBlack;
+ startAtmo();
+
+ ReplyCode = 550;
+ return "stripes black";
+ }
+ else if (Option && strcasecmp(Option, "detach") == 0)
+ {
+ cfg.viewMode = cAmbiService::vmDetached;
+ stopAtmo();
+
+ ReplyCode = 550;
+ return "stripes detached";
+ }
+ else
+ {
+ ReplyCode = 901;
+ return "Error: Unexpected option";
+ }
+ }
+
+ return 0;
+}
+
+const char** cPluginBoblight::SVDRPHelpPages(void)
+{
+ static const char* HelpPages[] =
+ {
+ "MODE <mode>\n"
+ " Set mode {atmo|fixed|black|detach}\n",
+ 0
+ };
+
+ return HelpPages;
+}
+
+//***************************************************************************
+// Class Setup Menu
+//***************************************************************************
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cAmbiSetup::cAmbiSetup()
+{
+ cineBars[0] = "Horizontal";
+ cineBars[1] = "Vertical";
+ cineBars[2] = "Both";
+
+ Setup();
+}
+
+//***************************************************************************
+// Setup
+//***************************************************************************
+
+void cAmbiSetup::Setup()
+{
+ Clear();
+
+ Add(new cMenuEditIntItem(tr("Log level"), &cfg.loglevel, 0, 3));
+ Add(new cMenuEditBoolItem(tr("Show mainmenu"), &cfg.showMainmenu));
+
+ Add(new cMenuEditIntItem(tr("Updaterate [Hz]"), &cfg.frequence, 1, 100));
+
+ Add(new cMenuEditStraItem(tr("Detect cinema bars"), (int*)&cfg.detectCineBars, 3, cineBars));
+
+ Add(new cMenuEditIntItem(tr("Threshold (0-255)"), &cfg.threshold, 0, 255));
+ Add(new cMenuEditIntItem(tr("Gamma (0-10.0)"), &cfg.gamma, 0, 100));
+ Add(new cMenuEditIntItem(tr("Value (0-20.0)"), &cfg.value, 0, 200));
+ Add(new cMenuEditIntItem(tr("Saturation (0-20.0)"), &cfg.saturation, 0, 200));
+ Add(new cMenuEditIntItem(tr("Speed (0-100)"), &cfg.speed, 0, 100));
+ Add(new cMenuEditIntItem(tr("Autospeed (0-100)"), &cfg.autospeed, 0, 100));
+ Add(new cMenuEditBoolItem(tr("Interpolation"), &cfg.interpolation));
+ Add(new cMenuEditIntItem(tr("Priority 0=Highest, 255=Lowest"), &cfg.priority, 0, 255));
+}
+
+eOSState cAmbiSetup::ProcessKey(eKeys key)
+{
+ eOSState state = cMenuSetupPage::ProcessKey(key);
+
+ return state;
+}
+
+void cAmbiSetup::Store()
+{
+ cfg.dirty = 1;
+ SetupStore("LogLevel", cfg.loglevel);
+ SetupStore("ShowMainmenu", cfg.showMainmenu);
+ SetupStore("ViewMode", (int)cfg.viewMode);
+
+ SetupStore("DetectCineBars", cfg.detectCineBars);
+
+ SetupStore("Updaterate", cfg.frequence);
+ SetupStore("Threshold", cfg.threshold);
+ SetupStore("Gamma", cfg.gamma);
+ SetupStore("Value", cfg.value);
+ SetupStore("Saturation", cfg.saturation);
+ SetupStore("Speed", cfg.speed);
+ SetupStore("Autospeed", cfg.autospeed);
+ SetupStore("Interpolation", cfg.interpolation);
+ SetupStore("Priority", cfg.priority);
+
+ SetupStore("FixedColorRed", cfg.fixedR);
+ SetupStore("FixedColorGreen", cfg.fixedG);
+ SetupStore("FixedColorBlue", cfg.fixedB);
+}
+
+//***************************************************************************
+// VDR Internal
+//***************************************************************************
+
+VDRPLUGINCREATOR(cPluginBoblight);