summaryrefslogtreecommitdiff
path: root/beryl-plugins-vidcap
diff options
context:
space:
mode:
authorquinn <quinn@d7aaf104-2d23-0410-ae22-9d23157bf5a3>2007-01-23 18:04:08 +0000
committerquinn <quinn@d7aaf104-2d23-0410-ae22-9d23157bf5a3>2007-01-23 18:04:08 +0000
commit257397610dfc420b572da1096dc771795fc8adf7 (patch)
treedcee30e524d38765aeb73e43156732c0a3561aa9 /beryl-plugins-vidcap
parent7a3ae8ed7ad5b73280f10fa080fdd6b15953485b (diff)
downloadmarex-dev-257397610dfc420b572da1096dc771795fc8adf7.tar.gz
marex-dev-257397610dfc420b572da1096dc771795fc8adf7.tar.bz2
rename beryl-vidcap to follow the new naming scheme
git-svn-id: file:///beryl/trunk@3052 d7aaf104-2d23-0410-ae22-9d23157bf5a3
Diffstat (limited to 'beryl-plugins-vidcap')
-rw-r--r--beryl-plugins-vidcap/Makefile30
-rw-r--r--beryl-plugins-vidcap/README5
-rw-r--r--beryl-plugins-vidcap/capture.c839
-rw-r--r--beryl-plugins-vidcap/seom.pngbin0 -> 8989 bytes
4 files changed, 874 insertions, 0 deletions
diff --git a/beryl-plugins-vidcap/Makefile b/beryl-plugins-vidcap/Makefile
new file mode 100644
index 0000000..8527dd5
--- /dev/null
+++ b/beryl-plugins-vidcap/Makefile
@@ -0,0 +1,30 @@
+PREFIX=/usr/local
+LIB=lib
+LIBDIR=$(PREFIX)/$(LIB)
+DESTDIR=$(LIBDIR)/beryl/
+
+PLUGIN = capture
+
+CC = gcc
+LIBTOOL = libtool
+INSTALL = install
+
+CFLAGS = -Wall `pkg-config --cflags beryl`
+LDFLAGS = `pkg-config --libs beryl` -lseom
+
+%.lo: %.c
+ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(CFLAGS) -c -o $@ $<
+
+%.la: $(PLUGIN).lo
+ $(LIBTOOL) --tag=CC --mode=link $(CC) $(LDFLAGS) -module -avoid-version -no-undefined -rpath $(DESTDIR) -o $@ $<
+
+all: lib$(PLUGIN).la
+
+install: lib$(PLUGIN).la
+ @mkdir -p $(DESTDIR) $(PREFIX)/share/beryl
+ $(LIBTOOL) --tag=CC --mode=install $(INSTALL) lib$(PLUGIN).la $(DESTDIR)/lib$(PLUGIN).la
+ cp seom.png $(PREFIX)/share/beryl
+
+clean:
+ rm -rf *.lo *.o lib$(PLUGIN).* .libs
+
diff --git a/beryl-plugins-vidcap/README b/beryl-plugins-vidcap/README
new file mode 100644
index 0000000..21dbf37
--- /dev/null
+++ b/beryl-plugins-vidcap/README
@@ -0,0 +1,5 @@
+In order to use beryl-vidcap, you must have seom. You can check it out
+via svn at: svn://dbservice.com/big/svn/seom/trunk
+
+The command to check it out is:
+# svn co svn://dbservice.com/big/svn/seom/trunk seom
diff --git a/beryl-plugins-vidcap/capture.c b/beryl-plugins-vidcap/capture.c
new file mode 100644
index 0000000..814aaf2
--- /dev/null
+++ b/beryl-plugins-vidcap/capture.c
@@ -0,0 +1,839 @@
+/*
+ * Copyright Tomac Carnecky 2006
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Tomas Carnecky <tom@dbservice.com>
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <dlfcn.h>
+
+#include <beryl.h>
+
+#include <seom/seom.h>
+
+
+/*
+ * options
+ */
+
+enum CaptureOptions {
+ CAPTURE_OPTION_SCREEN = 0,
+ CAPTURE_OPTION_REGION,
+ CAPTURE_OPTION_OUTPUT,
+ CAPTURE_OPTION_FRAMERATE,
+ CAPTURE_OPTION_SCALE,
+ CAPTURE_OPTION_COMMAND,
+ CAPTURE_OPTION_CURSOR,
+ CAPTURE_OPTION_WATERMARK,
+ CAPTURE_OPTION_NUM
+};
+
+#define CAPTURE_OPTION_SCREEN_KEY "F8"
+#define CAPTURE_OPTION_SCREEN_MOD CompSuperMask
+
+#define CAPTURE_OPTION_REGION_BUTTON Button2
+#define CAPTURE_OPTION_REGION_MOD CompSuperMask
+
+#define CAPTURE_OPTION_OUTPUT_DEFAULT "file:///tmp/beryl-capture.seom"
+#define CAPTURE_OPTION_FRAMERATE_DEFAULT 25.0f, 5.0f, 60.0f, 1.0f
+#define CAPTURE_OPTION_SCALE_DEFAULT 1, 0, 5
+#define CAPTURE_OPTION_COMMAND_DEFAULT "seom-backup /tmp $HOME/Desktop"
+#define CAPTURE_OPTION_CURSOR_DEFAULT FALSE
+#define CAPTURE_OPTION_WATERMARK_DEFAULT TRUE
+
+
+/*
+ * static variables
+ */
+
+static int displayPrivateIndex;
+
+
+/*
+ * private stuctures
+ */
+
+typedef struct _CaptureDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[CAPTURE_OPTION_NUM];
+
+ int fixesEventBase;
+
+ struct {
+ GLuint name;
+ int width, height, hotX, hotY;
+ } cursorTexture;
+
+ struct {
+ GLuint name;
+ int width, height;
+ } watermarkTexture;
+} CaptureDisplay;
+
+typedef struct _CaptureScreen {
+ PaintScreenProc paintScreen;
+ DonePaintScreenProc donePaintScreen;
+
+ int grabIndex;
+
+ int x1, y1, x2, y2;
+
+ seomClient *clientScreen;
+ seomClient *clientRegion;
+} CaptureScreen;
+
+
+/*
+ * macros
+ */
+
+#define GET_CAPTURE_DISPLAY(d) ((CaptureDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define CAPTURE_DISPLAY(d) CaptureDisplay *cd = GET_CAPTURE_DISPLAY (d)
+
+#define GET_CAPTURE_SCREEN(s, cd) ((CaptureScreen *) (s)->privates[(cd)->screenPrivateIndex].ptr)
+
+#define CAPTURE_SCREEN(s) CaptureScreen *cs = GET_CAPTURE_SCREEN (s, GET_CAPTURE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *berylOptionCreate(CompOption *opts, int index, CompOptionType type, char *name, char *group, char *subGroup)
+{
+ CompOption *o = &opts[index];
+
+ o->type = type;
+ o->name = name;
+ o->group = N_(group);
+ o->subGroup = N_(subGroup);
+
+ /* XXX: is this still needed? Now that we have subGroup.. */
+ o->advanced = FALSE;
+ o->displayHints = "";
+
+ return o;
+}
+
+static void berylOptionDescription(CompOption *o, char *shortDesc, char *longDesc)
+{
+ o->shortDesc = N_(shortDesc);
+ o->longDesc = N_(longDesc);
+}
+
+static void berylOptionDefault(CompOption *o, ...)
+{
+ va_list args;
+ va_start(args, o);
+
+ /* FIXME: handle CompOptionTypeAction and CompOptionTypeList */
+ switch (o->type) {
+ case CompOptionTypeBool:
+ o->value.b = va_arg(args, Bool);
+ break;
+ case CompOptionTypeFloat:
+ o->value.f = va_arg(args, double);
+ o->rest.f.min = va_arg(args, double);
+ o->rest.f.max = va_arg(args, double);
+ o->rest.f.precision = va_arg(args, double);
+ break;
+ case CompOptionTypeInt:
+ o->value.i = va_arg(args, int);
+ o->rest.i.min = va_arg(args, int);
+ o->rest.i.max = va_arg(args, int);
+ case CompOptionTypeString:
+ o->value.s = strdup(va_arg(args, char *));
+ /* FIXME: set restrictions */
+ break;
+ default:
+ break;
+ }
+
+ va_end(args);
+}
+
+
+static void updateCursor(Display *dpy, CaptureDisplay *cd)
+{
+ if (!cd->opt[CAPTURE_OPTION_CURSOR].value.b)
+ return;
+
+ XFixesCursorImage *ci = XFixesGetCursorImage(dpy);
+
+ cd->cursorTexture.width = ci->width;
+ cd->cursorTexture.height = ci->height;
+ cd->cursorTexture.hotX = ci->xhot;
+ cd->cursorTexture.hotY = ci->yhot;
+
+ unsigned char *pixels = malloc(ci->width * ci->height * 4);
+ int i;
+
+ for (i = 0; i < ci->width * ci->height; i++) {
+ unsigned long pix = ci->pixels[i];
+
+ pixels[i * 4] = pix & 0xff;
+ pixels[(i * 4) + 1] = (pix >> 8) & 0xff;
+ pixels[(i * 4) + 2] = (pix >> 16) & 0xff;
+ pixels[(i * 4) + 3] = (pix >> 24) & 0xff;
+ }
+
+ XFree(ci);
+
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cd->cursorTexture.name);
+ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, cd->cursorTexture.width, cd->cursorTexture.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+ glDisable(GL_TEXTURE_RECTANGLE_ARB);
+
+ free(pixels);
+}
+
+
+static void showCursor(CompDisplay * d, CompScreen * s)
+{
+ CAPTURE_DISPLAY(s->display);
+
+ if (cd->opt[CAPTURE_OPTION_CURSOR].value.b) {
+ XFixesSelectCursorInput(s->display->display, s->root, 0);
+ XFixesShowCursor(s->display->display, s->root);
+ }
+}
+
+static void hideCursor(CompDisplay * d, CompScreen * s)
+{
+ CAPTURE_DISPLAY(s->display);
+
+ if (cd->opt[CAPTURE_OPTION_CURSOR].value.b) {
+ XFixesSelectCursorInput(s->display->display, s->root, XFixesDisplayCursorNotifyMask);
+ XFixesHideCursor(s->display->display, s->root);
+ updateCursor(d->display, GET_CAPTURE_DISPLAY(d));
+ }
+}
+
+
+static Bool captureInitiateScreen(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ CAPTURE_DISPLAY(d);
+
+ xid = getIntOptionNamed(option, nOption, "root", 0);
+
+ s = findScreenAtDisplay(d, xid);
+ if (s) {
+ CAPTURE_SCREEN(s);
+
+ if (cs->clientRegion) {
+ seomClientDestroy(cs->clientRegion);
+ cs->clientRegion = NULL;
+
+ showCursor(d, s);
+
+ if (cd->opt[CAPTURE_OPTION_COMMAND].value.s) {
+ if (fork() == 0) {
+ putenv(d->displayString);
+ execl("/bin/sh", "/bin/sh", "-c", cd->opt[CAPTURE_OPTION_COMMAND].value.s, NULL);
+ exit(0);
+ }
+ }
+ } else if (cs->clientScreen) {
+ seomClientDestroy(cs->clientScreen);
+ cs->clientScreen = NULL;
+
+ showCursor(d, s);
+
+ if (cd->opt[CAPTURE_OPTION_COMMAND].value.s) {
+ if (fork() == 0) {
+ putenv(d->displayString);
+ execl("/bin/sh", "/bin/sh", "-c", cd->opt[CAPTURE_OPTION_COMMAND].value.s, NULL);
+ exit(0);
+ }
+ }
+ } else {
+ seomClientConfig config;
+
+ config.size[0] = s->width;
+ config.size[1] = s->height;
+ config.scale = cd->opt[CAPTURE_OPTION_SCALE].value.i;
+ config.fps = cd->opt[CAPTURE_OPTION_FRAMERATE].value.f;
+ config.output = cd->opt[CAPTURE_OPTION_OUTPUT].value.s;
+
+ hideCursor(d, s);
+ cs->clientScreen = seomClientCreate(&config);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool captureTerminateScreen(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption)
+{
+ return FALSE;
+}
+
+static Bool captureInitiateRegion(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ xid = getIntOptionNamed(option, nOption, "root", 0);
+
+ s = findScreenAtDisplay(d, xid);
+ if (s) {
+ CAPTURE_SCREEN(s);
+
+ if (otherScreenGrabExist(s, "capture", 0))
+ return FALSE;
+
+ if (!cs->grabIndex)
+ cs->grabIndex = pushScreenGrab(s, None, "capture");
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ cs->x1 = cs->x2 = d->pointerX;
+ cs->y1 = cs->y2 = d->pointerY;
+
+ if (cs->clientRegion) {
+ seomClientDestroy(cs->clientRegion);
+ cs->clientRegion = NULL;
+
+ showCursor(d, s);
+
+ CAPTURE_DISPLAY(d);
+ if (cd->opt[CAPTURE_OPTION_COMMAND].value.s) {
+ if (fork() == 0) {
+ putenv(d->displayString);
+ execl("/bin/sh", "/bin/sh", "-c", cd->opt[CAPTURE_OPTION_COMMAND].value.s, NULL);
+ exit(0);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static Bool captureTerminateRegion(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ Window xid;
+
+ CAPTURE_DISPLAY(d);
+
+ xid = getIntOptionNamed(option, nOption, "root", 0);
+
+ for (s = d->screens; s; s = s->next) {
+ CAPTURE_SCREEN(s);
+
+ if (xid && s->root != xid)
+ continue;
+
+ if (cs->grabIndex) {
+ removeScreenGrab(s, cs->grabIndex, NULL);
+ cs->grabIndex = 0;
+
+ if (cs->x1 != cs->x2 && cs->y1 != cs->y2) {
+ seomClientConfig config;
+
+ config.size[0] = cs->x1 < cs->x2 ? cs->x2 - cs->x1 : cs->x1 - cs->x2;
+ config.size[1] = cs->y1 < cs->y2 ? cs->y2 - cs->y1 : cs->y1 - cs->y2;
+ config.scale = cd->opt[CAPTURE_OPTION_SCALE].value.i;
+ config.fps = cd->opt[CAPTURE_OPTION_FRAMERATE].value.f;
+ config.output = cd->opt[CAPTURE_OPTION_OUTPUT].value.s;
+
+ damageScreen(s);
+
+ hideCursor(d, s);
+ cs->clientRegion = seomClientCreate(&config);
+ }
+ }
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+static Bool capturePaintScreen(CompScreen * s, const ScreenPaintAttrib * sAttrib, Region region, int output, unsigned int mask)
+{
+ Bool status;
+
+ CAPTURE_DISPLAY(s->display);
+ CAPTURE_SCREEN(s);
+
+ UNWRAP(cs, s, paintScreen);
+ status = (*s->paintScreen) (s, sAttrib, region, output, mask);
+ WRAP(cs, s, paintScreen, capturePaintScreen);
+
+ /* XXX: inputzoom clears the mask, so we have to reapply it */
+ if ((cs->clientScreen || cs->clientRegion) && cd->opt[CAPTURE_OPTION_CURSOR].value.b)
+ XFixesSelectCursorInput(s->display->display, s->root, XFixesDisplayCursorNotifyMask);
+
+ if ((cs->clientScreen || cs->clientRegion) && cd->opt[CAPTURE_OPTION_CURSOR].value.b) {
+ int mouseX, mouseY, winX, winY;
+ unsigned int mask;
+ Window root, child;
+ XQueryPointer(s->display->display, s->root, &root, &child, &mouseX, &mouseY, &winX, &winY, &mask);
+
+ glPushMatrix();
+ glLoadIdentity();
+ prepareXCoords(s, output, -DEFAULT_Z_CAMERA);
+
+ glTranslatef(mouseX, mouseY, 0.0);
+ int x = -cd->cursorTexture.hotX;
+ int y = -cd->cursorTexture.hotY;
+
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cd->cursorTexture.name);
+
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(0, cd->cursorTexture.height);
+ glVertex2f(x, y + cd->cursorTexture.height);
+ glTexCoord2d(cd->cursorTexture.width, cd->cursorTexture.height);
+ glVertex2f(x + cd->cursorTexture.width, y + cd->cursorTexture.height);
+ glTexCoord2d(cd->cursorTexture.width, 0);
+ glVertex2f(x + cd->cursorTexture.width, y);
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+ glDisable(GL_TEXTURE_RECTANGLE_ARB);
+ glDisable(GL_BLEND);
+ glPopMatrix();
+ }
+
+ /* FIXME: WTF does 'status' mean ??? */
+ if (status) {
+ int x1, x2, y1, y2;
+
+ x1 = MIN(cs->x1, cs->x2);
+ y1 = MIN(cs->y1, cs->y2);
+ x2 = MAX(cs->x1, cs->x2);
+ y2 = MAX(cs->y1, cs->y2);
+
+ if (cs->grabIndex) {
+ glPushMatrix();
+ glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
+ glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
+ glTranslatef(0.0f, -s->height, 0.0f);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_BLEND);
+ glColor4us(0x2fff, 0x2fff, 0x4fff, 0x4fff);
+ glRecti(x1, y2, x2, y1);
+ glColor4us(0x2fff, 0x2fff, 0x4fff, 0x9fff);
+ glBegin(GL_LINE_LOOP);
+ glVertex2i(x1, y1);
+ glVertex2i(x2, y1);
+ glVertex2i(x2, y2);
+ glVertex2i(x1, y2);
+ glEnd();
+ glColor4usv(defaultColor);
+ glDisable(GL_BLEND);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix();
+ } else if (cs->clientRegion) {
+ glPushMatrix();
+ glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
+ glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
+ glTranslatef(0.0f, -s->height, 0.0f);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_BLEND);
+ glColor4us(0x4fff, 0x2fff, 0x2fff, 0x9fff);
+ glLineWidth(3.0);
+ glBegin(GL_LINE_LOOP);
+ glVertex2i(x1 - 2, y1 - 2);
+ glVertex2i(x2 + 1, y1 - 2);
+ glVertex2i(x2 + 1, y2 + 1);
+ glVertex2i(x1 - 2, y2 + 1);
+ glEnd();
+ glColor4usv(defaultColor);
+ glDisable(GL_BLEND);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix();
+ }
+ }
+
+ if (cs->clientScreen && cd->watermarkTexture.name && cd->opt[CAPTURE_OPTION_WATERMARK].value.b) {
+ glPushMatrix();
+ glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
+ glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
+ glTranslatef(0.0f, -s->height, 0.0f);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_BLEND);
+ glColor4us(0x6fff, 0x2fff, 0x2fff, 0x8fff);
+ glRecti(s->width - cd->watermarkTexture.width - 18, s->height - 6, s->width - 6, s->height - cd->watermarkTexture.height - 18);
+ glColor4usv(defaultColor);
+ glDisable(GL_BLEND);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix();
+
+ glPushMatrix();
+ glLoadIdentity();
+ prepareXCoords(s, output, -DEFAULT_Z_CAMERA);
+
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cd->watermarkTexture.name);
+
+ int x = s->width - cd->watermarkTexture.width - 12;
+ int y = s->height - cd->watermarkTexture.height - 12;
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(0, cd->watermarkTexture.height);
+ glVertex2f(x, y + cd->watermarkTexture.height);
+ glTexCoord2d(cd->watermarkTexture.width, cd->watermarkTexture.height);
+ glVertex2f(x + cd->watermarkTexture.width, y + cd->watermarkTexture.height);
+ glTexCoord2d(cd->watermarkTexture.width, 0);
+ glVertex2f(x + cd->watermarkTexture.width, y);
+ glEnd();
+
+ glColor4usv(defaultColor);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+ glDisable(GL_TEXTURE_RECTANGLE_ARB);
+ glDisable(GL_BLEND);
+ glPopMatrix();
+ }
+
+ return status;
+}
+
+static void captureDonePaintScreen(CompScreen * s)
+{
+ CAPTURE_SCREEN(s);
+
+ UNWRAP(cs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP(cs, s, donePaintScreen, captureDonePaintScreen);
+
+ /* at this point, beryl-core has already called glXSwapBuffers(), so we need to set
+ * the read buffer to GL_FRONT and reset it to GL_BACK when we're done */
+ glReadBuffer(GL_FRONT);
+
+ if (cs->clientRegion) {
+ seomClientCapture(cs->clientRegion, MIN(cs->x1, cs->x2), s->height - MAX(cs->y1, cs->y2));
+ damageScreen(s);
+ }
+
+ if (cs->clientScreen) {
+ seomClientCapture(cs->clientScreen, 0, 0);
+ damageScreen(s);
+ }
+
+ glReadBuffer(GL_BACK);
+}
+
+static void captureHandleMotionEvent(CompScreen * s, int xRoot, int yRoot)
+{
+ CAPTURE_SCREEN(s);
+
+ if (cs->grabIndex) {
+ cs->x2 = xRoot;
+ cs->y2 = yRoot;
+
+ damageScreen(s);
+ }
+}
+
+static void captureHandleEvent(CompDisplay * d, XEvent * event)
+{
+ CompScreen *s;
+
+ CAPTURE_DISPLAY(d);
+
+ switch (event->type) {
+ case MotionNotify:
+ s = findScreenAtDisplay(d, event->xmotion.root);
+ if (s)
+ captureHandleMotionEvent(s, d->pointerX, d->pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay(d, event->xcrossing.root);
+ if (s)
+ captureHandleMotionEvent(s, d->pointerX, d->pointerY);
+ default:
+ if (cd->fixesEventBase && event->type == cd->fixesEventBase + XFixesCursorNotify) {
+ updateCursor(d->display, cd);
+ }
+ break;
+ }
+
+ UNWRAP(cd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP(cd, d, handleEvent, captureHandleEvent);
+}
+
+
+static void captureDisplayInitOptions(CaptureDisplay * cd)
+{
+ CompOption *o;
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_SCREEN, CompOptionTypeAction, "screen", "Bindings", "");
+ berylOptionDescription(o, "Screen", "Toggle capturing of the current screen");
+ o->value.action.initiate = captureInitiateScreen;
+ o->value.action.terminate = captureTerminateScreen;
+ o->value.action.bell = FALSE;
+ o->value.action.edgeMask = 0;
+ o->value.action.type = CompBindingTypeKey;
+ o->value.action.state = CompActionStateInitKey;
+ o->value.action.key.modifiers = CAPTURE_OPTION_SCREEN_MOD;
+ o->value.action.key.keysym = XStringToKeysym(CAPTURE_OPTION_SCREEN_KEY);
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_REGION, CompOptionTypeAction, "region", "Bindings", "");
+ berylOptionDescription(o, "Region", "Initiate region capture");
+ o->value.action.initiate = captureInitiateRegion;
+ o->value.action.terminate = captureTerminateRegion;
+ o->value.action.bell = FALSE;
+ o->value.action.edgeMask = 0;
+ o->value.action.type = CompBindingTypeButton;
+ o->value.action.state = CompActionStateInitButton;
+ o->value.action.button.modifiers = CAPTURE_OPTION_REGION_MOD;
+ o->value.action.button.button = CAPTURE_OPTION_REGION_BUTTON;
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_OUTPUT, CompOptionTypeString, "output", "Options", "");
+ berylOptionDescription(o, "Output", "File path or server address");
+ o->value.s = strdup(CAPTURE_OPTION_OUTPUT_DEFAULT);
+ o->rest.s.string = 0;
+ o->rest.s.nString = 0;
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_FRAMERATE, CompOptionTypeFloat, "framerate", "Options", "");
+ berylOptionDescription(o, "Framerate", "Capture framerate");
+ berylOptionDefault(o, CAPTURE_OPTION_FRAMERATE_DEFAULT);
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_SCALE, CompOptionTypeInt, "scale", "Options", "");
+ berylOptionDescription(o, "Scale", "Scale video down");
+ berylOptionDefault(o, CAPTURE_OPTION_SCALE_DEFAULT);
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_COMMAND, CompOptionTypeString, "command", "Post-Processing", "");
+ berylOptionDescription(o, "Command", "Command line that is executed after capturing");
+ o->value.s = strdup(CAPTURE_OPTION_COMMAND_DEFAULT);
+ o->rest.s.string = 0;
+ o->rest.s.nString = 0;
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_CURSOR, CompOptionTypeBool, "cursor", "Options", "");
+ berylOptionDescription(o, "Display Cursor", "Enable if the cursor is invisible in the video");
+ berylOptionDefault(o, CAPTURE_OPTION_CURSOR_DEFAULT);
+
+ o = berylOptionCreate(cd->opt, CAPTURE_OPTION_WATERMARK, CompOptionTypeBool, "watermark", "Options", "");
+ berylOptionDescription(o, "Display Watermark", "Display watermark over the captured video");
+ berylOptionDefault(o, CAPTURE_OPTION_WATERMARK_DEFAULT);
+}
+
+static CompOption *captureGetDisplayOptions(CompDisplay * display, int *count)
+{
+ if (display) {
+ CAPTURE_DISPLAY(display);
+
+ *count = NUM_OPTIONS(cd);
+ return cd->opt;
+ } else {
+ CaptureDisplay *cd = malloc(sizeof(CaptureDisplay));
+ captureDisplayInitOptions(cd);
+ *count = NUM_OPTIONS(cd);
+ return cd->opt;
+ }
+}
+
+static Bool captureSetDisplayOption(CompDisplay * display, char *name, CompOptionValue * value)
+{
+ CompOption *o;
+ int index;
+
+ CAPTURE_DISPLAY(display);
+
+ o = compFindOption(cd->opt, NUM_OPTIONS(cd), name, &index);
+ if (!o)
+ return FALSE;
+
+ return compSetOption(display, o, value);
+}
+
+static Bool captureInitDisplay(CompPlugin * p, CompDisplay * d)
+{
+ CaptureDisplay *cd;
+
+ cd = malloc(sizeof(CaptureDisplay));
+ if (!cd)
+ return FALSE;
+
+ cd->screenPrivateIndex = allocateScreenPrivateIndex(d);
+ if (cd->screenPrivateIndex < 0) {
+ free(cd);
+ return FALSE;
+ }
+
+ WRAP(cd, d, handleEvent, captureHandleEvent);
+
+ captureDisplayInitOptions(cd);
+
+ d->privates[displayPrivateIndex].ptr = cd;
+
+ cd->fixesEventBase = 0;
+ int eventBase, errorBase;
+ if (XFixesQueryExtension(d->display, &eventBase, &errorBase)) {
+ int major, minor;
+ XFixesQueryVersion(d->display, &major, &minor);
+
+ if (major >= 4)
+ cd->fixesEventBase = eventBase;
+ }
+
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+
+ void *data = NULL;
+ readImageFromFile(d, "seom.png", &cd->watermarkTexture.width, &cd->watermarkTexture.height, &data);
+ if (data) {
+ glGenTextures(1, &cd->watermarkTexture.name);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cd->watermarkTexture.name);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, cd->watermarkTexture.width, cd->watermarkTexture.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
+ free(data);
+ } else {
+ cd->watermarkTexture.name = 0;
+ }
+
+ glGenTextures(1, &cd->cursorTexture.name);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cd->cursorTexture.name);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+ glDisable(GL_TEXTURE_RECTANGLE_ARB);
+
+ return TRUE;
+}
+
+static void captureFiniDisplay(CompPlugin * p, CompDisplay * d)
+{
+ CAPTURE_DISPLAY(d);
+
+ freeScreenPrivateIndex(d, cd->screenPrivateIndex);
+
+ UNWRAP(cd, d, handleEvent);
+
+ glDeleteTextures(1, &cd->watermarkTexture.name);
+ glDeleteTextures(1, &cd->cursorTexture.name);
+
+ free(cd);
+}
+
+static Bool captureInitScreen(CompPlugin * p, CompScreen * s)
+{
+ CaptureScreen *cs;
+
+ CAPTURE_DISPLAY(s->display);
+
+ cs = malloc(sizeof(CaptureScreen));
+ if (!cs)
+ return FALSE;
+
+ cs->clientScreen = NULL;
+ cs->clientRegion = NULL;
+
+ addScreenAction(s, &cd->opt[CAPTURE_OPTION_SCREEN].value.action);
+ addScreenAction(s, &cd->opt[CAPTURE_OPTION_REGION].value.action);
+
+ WRAP(cs, s, paintScreen, capturePaintScreen);
+ WRAP(cs, s, donePaintScreen, captureDonePaintScreen);
+
+ s->privates[cd->screenPrivateIndex].ptr = cs;
+
+ cs->grabIndex = 0;
+
+ return TRUE;
+}
+
+static void captureFiniScreen(CompPlugin * p, CompScreen * s)
+{
+ CAPTURE_SCREEN(s);
+ CAPTURE_DISPLAY(s->display);
+
+ UNWRAP(cs, s, paintScreen);
+ UNWRAP(cs, s, donePaintScreen);
+
+ removeScreenAction(s, &cd->opt[CAPTURE_OPTION_SCREEN].value.action);
+ removeScreenAction(s, &cd->opt[CAPTURE_OPTION_REGION].value.action);
+
+ free(cs);
+}
+
+static Bool captureInit(CompPlugin * p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void captureFini(CompPlugin * p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex(displayPrivateIndex);
+}
+
+static CompPluginVTable captureVTable = {
+ "capture",
+ N_("Capture"),
+ N_("Video capture plugin"),
+ captureInit,
+ captureFini,
+ captureInitDisplay,
+ captureFiniDisplay,
+ captureInitScreen,
+ captureFiniScreen,
+ NULL, /* initWindow */
+ NULL, /* iiniWindow */
+ captureGetDisplayOptions,
+ captureSetDisplayOption,
+ NULL, /* getScreenOptions */
+ NULL, /* setScreenOption */
+ NULL, /* deps */
+ 0, /* nDeps */
+ NULL, /* features */
+ 0, /* nFeatures */
+ BERYL_ABI_INFO, /* version / ABI info */
+ "beryl-vidcap", /* gettext domain */
+ "misc", /* category */
+ NULL, /* groupDescs */
+ 0, /* nGroupDescs */
+ False, /* defaultEnabled */
+};
+
+CompPluginVTable *getCompPluginInfo(void)
+{
+ return &captureVTable;
+}
diff --git a/beryl-plugins-vidcap/seom.png b/beryl-plugins-vidcap/seom.png
new file mode 100644
index 0000000..e98443c
--- /dev/null
+++ b/beryl-plugins-vidcap/seom.png
Binary files differ