summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Moreau <oreaus@gmail.com>2010-10-18 10:25:40 -0600
committerScott Moreau <oreaus@gmail.com>2010-10-18 10:25:40 -0600
commitb649b509743d5806811b906b8ef4bf48b566c7a1 (patch)
tree12b7f9c8e0c198b7068c5932955d98698f727edd
downloadvidcap-b649b509743d5806811b906b8ef4bf48b566c7a1.tar.gz
vidcap-b649b509743d5806811b906b8ef4bf48b566c7a1.tar.bz2
Initial C++ port.
-rw-r--r--CMakeLists.txt5
-rw-r--r--images/seom.pngbin0 -> 8989 bytes
-rw-r--r--images/watermark.pngbin0 -> 15143 bytes
-rw-r--r--src/vidcap.cpp591
-rw-r--r--src/vidcap.h142
-rw-r--r--vidcap.xml.in123
6 files changed, 861 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9682472
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,5 @@
+find_package (Compiz REQUIRED)
+
+include (CompizPlugin)
+
+compiz_plugin (vidcap PLUGINDEPS composite opengl PKGDEPS seom)
diff --git a/images/seom.png b/images/seom.png
new file mode 100644
index 0000000..e98443c
--- /dev/null
+++ b/images/seom.png
Binary files differ
diff --git a/images/watermark.png b/images/watermark.png
new file mode 100644
index 0000000..3e49165
--- /dev/null
+++ b/images/watermark.png
Binary files differ
diff --git a/src/vidcap.cpp b/src/vidcap.cpp
new file mode 100644
index 0000000..179e735
--- /dev/null
+++ b/src/vidcap.cpp
@@ -0,0 +1,591 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ * Copyright Tomac Carnecky 2006
+ *
+ * Ported to Compiz 0.9.x
+ * Copyright : (c) 2010 Scott Moreau <oreaus@gmail.com>
+ *
+ * vidcap.cpp
+ *
+ * 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: David Reveman <davidr@novell.com>
+ * Author: Tomas Carnecky <tom@dbservice.com>
+ */
+
+#include "vidcap.h"
+
+void
+VidcapScreen::toggleFunctions (bool enabled)
+{
+ screen->handleEventSetEnabled (this, enabled);
+ cScreen->preparePaintSetEnabled (this, enabled);
+ gScreen->glPaintOutputSetEnabled (this, enabled);
+ cScreen->donePaintSetEnabled (this, enabled);
+}
+
+void
+VidcapScreen::updateCursor ()
+{
+ if (!optionGetCaptureCursor ())
+ return;
+
+ XFixesCursorImage *ci = XFixesGetCursorImage (screen->dpy ());
+
+ cT.width = ci->width;
+ cT.height = ci->height;
+ cT.hotX = ci->xhot;
+ cT.hotY = ci->yhot;
+
+ unsigned char *pixels = (unsigned char*) malloc (ci->width * ci->height * 4);
+
+ for (int 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, cT.name);
+ glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, cT.width, cT.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+ glBindTexture (GL_TEXTURE_RECTANGLE_ARB, 0);
+ glDisable (GL_TEXTURE_RECTANGLE_ARB);
+
+ free(pixels);
+}
+
+void
+VidcapScreen::showCursor ()
+{
+ if (optionGetCaptureCursor ())
+ {
+ XFixesSelectCursorInput (screen->dpy (), screen->root (), 0);
+ XFixesShowCursor (screen->dpy (), screen->root ());
+ }
+}
+
+void
+VidcapScreen::hideCursor ()
+{
+ if (optionGetCaptureCursor ())
+ {
+ XFixesSelectCursorInput (screen->dpy (), screen->root (), XFixesDisplayCursorNotifyMask);
+ XFixesHideCursor (screen->dpy (), screen->root ());
+ updateCursor ();
+ }
+}
+
+bool
+VidcapScreen::toggleFullscreenCapture (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector options)
+{
+ if (clientRegion)
+ {
+ toggleFunctions (false);
+
+ seomClientDestroy (clientRegion);
+ clientRegion = NULL;
+
+ showCursor ();
+
+ if (optionGetCommandString ().c_str ())
+ {
+ if (fork () == 0)
+ {
+ putenv (const_cast <char *> (screen->displayString ()));
+ execl ("/bin/sh", "/bin/sh", "-c", optionGetCommandString ().c_str (), NULL);
+ exit (0);
+ }
+ }
+ }
+ else
+ if (clientScreen)
+ {
+ toggleFunctions (false);
+
+ seomClientDestroy (clientScreen);
+ clientScreen = NULL;
+
+ showCursor ();
+
+ if (optionGetCommandString ().c_str ())
+ {
+ if (fork () == 0)
+ {
+ putenv (const_cast <char *> (screen->displayString ()));
+ execl ("/bin/sh", "/bin/sh", "-c", optionGetCommandString ().c_str (), NULL);
+ exit (0);
+ }
+ }
+ }
+ else
+ {
+ seomClientConfig config;
+
+ config.size[0] = screen->width ();
+ config.size[1] = screen->height ();
+ config.scale = optionGetScaleAmount ();
+ config.fps = optionGetFramerate ();
+ config.output = (char*) optionGetOutputString ().c_str ();
+
+ hideCursor();
+ clientScreen = seomClientCreate (&config);
+ if (clientScreen == NULL)
+ compLogMessage ("vidcap", CompLogLevelWarn,
+ "Failed to create seom client.");
+ else
+ toggleFunctions (true);
+
+ }
+
+ return false;
+}
+
+bool
+VidcapScreen::initiateRegionCapture (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector options)
+{
+ if (screen->otherGrabExist (NULL) || screen->grabExist ("vidcap"))
+ return false;
+
+ if (!grabIndex)
+ grabIndex = screen->pushGrab (None, "capture");
+
+ if (state & CompAction::StateInitButton)
+ action->setState (action->state () | CompAction::StateTermButton);
+
+ toggleFunctions (true);
+
+ x1 = x2 = pointerX;
+ y1 = y2 = pointerY;
+
+ if (clientRegion)
+ {
+ seomClientDestroy (clientRegion);
+ clientRegion = NULL;
+
+ showCursor ();
+
+ if (optionGetCommandString ().c_str ())
+ {
+ if (fork () == 0)
+ {
+ putenv (const_cast <char *> (screen->displayString ()));
+ execl("/bin/sh", "/bin/sh", "-c", optionGetCommandString ().c_str (), NULL);
+ exit (0);
+ }
+ }
+ }
+
+ return false;
+}
+
+bool
+VidcapScreen::terminateRegionCapture (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector options)
+{
+ if (grabIndex)
+ {
+ screen->removeGrab (grabIndex, NULL);
+ grabIndex = 0;
+
+ if (x1 != x2 && y1 != y2)
+ {
+ seomClientConfig config;
+
+ config.size[0] = x1 < x2 ? x2 - x1 : x1 - x2;
+ config.size[1] = y1 < y2 ? y2 - y1 : y1 - y2;
+ config.scale = optionGetScaleAmount ();
+ config.fps = optionGetFramerate ();
+ config.output = (char*) optionGetOutputString ().c_str ();
+
+ cScreen->damageScreen ();
+
+ hideCursor ();
+
+ clientRegion = seomClientCreate (&config);
+
+ if (clientRegion == NULL)
+ compLogMessage ("vidcap", CompLogLevelWarn,
+ "Failed to create seom client.");
+ else
+ toggleFunctions (true);
+ }
+ }
+
+ action->setState (action->state () &
+ ~(CompAction::StateTermKey | CompAction::StateTermButton));
+
+ return false;
+}
+
+bool
+VidcapScreen::glPaintOutput (const GLScreenPaintAttrib &attrib,
+ const GLMatrix &transform,
+ const CompRegion &region,
+ CompOutput *output,
+ unsigned int mask)
+{
+ bool status;
+
+ status = gScreen->glPaintOutput (attrib, transform, region, output, mask);
+
+ /* XXX: inputzoom clears the mask, so we have to reapply it */
+ if ((clientScreen || clientRegion) && optionGetCaptureCursor ())
+ XFixesSelectCursorInput (screen->dpy (), screen->root (), XFixesDisplayCursorNotifyMask);
+
+ if ((clientScreen || clientRegion) && optionGetCaptureCursor ())
+ {
+ int mouseX, mouseY, winX, winY;
+ unsigned int mask;
+ Window root, child;
+
+ XQueryPointer (screen->dpy (), screen->root (), &root, &child,
+ &mouseX, &mouseY, &winX, &winY, &mask);
+
+ glPushMatrix ();
+ glLoadIdentity ();
+
+// prepareXCoords(output, -DEFAULT_Z_CAMERA);
+
+ glTranslatef(mouseX, mouseY, 0.0);
+ int x = -cT.hotX;
+ int y = -cT.hotY;
+
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cT.name);
+
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(0, cT.height);
+ glVertex2f(x, y + cT.height);
+ glTexCoord2d(cT.width, cT.height);
+ glVertex2f(x + cT.width, y + cT.height);
+ glTexCoord2d(cT.width, 0);
+ glVertex2f(x + cT.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)
+ {
+
+ x1 = MIN(x1, x2);
+ y1 = MIN(y1, y2);
+ x2 = MAX(x1, x2);
+ y2 = MAX(y1, y2);
+
+ if (grabIndex) {
+ glPushMatrix();
+ glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
+ glScalef(1.0f / screen->width (), -1.0f / screen->height (), 1.0f);
+ glTranslatef(0.0f, -screen->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 (clientRegion) {
+ glPushMatrix();
+ glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
+ glScalef(1.0f / screen->width (), -1.0f / screen->height (), 1.0f);
+ glTranslatef(0.0f, -screen->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 ((clientScreen || clientRegion) && wT.texture.size () && optionGetDisplayWatermark ())
+ {
+ GLMatrix sTransform (transform);
+
+ sTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA);
+ sTransform.translate (wT.trans.x (), wT.trans.y (), 0.0f);
+
+ glEnable (GL_BLEND);
+ glPushMatrix ();
+ glLoadMatrixf (sTransform.getMatrix ());
+
+ foreach (GLTexture *tex, wT.texture)
+ {
+ unsigned int pos = 0;
+ CompSize &size = wT.size;
+
+ tex->enable (GLTexture::Fast);
+
+ glBegin (GL_QUADS);
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (tex->matrix (), 0),
+ COMP_TEX_COORD_Y (tex->matrix (), size.height ()));
+ glVertex2i (0, size.height ());
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (tex->matrix (), size.width ()),
+ COMP_TEX_COORD_Y (tex->matrix (), size.height ()));
+ glVertex2i (size.width (), size.height ());
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (tex->matrix (), size.width ()),
+ COMP_TEX_COORD_Y (tex->matrix (), 0));
+ glVertex2i (size.width (), 0);
+
+ glTexCoord2f (
+ COMP_TEX_COORD_X (tex->matrix (), 0),
+ COMP_TEX_COORD_Y (tex->matrix (), 0));
+ glVertex2i (0, 0);
+
+ pos++;
+
+ glEnd ();
+ tex->disable ();
+ }
+
+ glDisable (GL_BLEND);
+ glPopMatrix ();
+ }
+
+ return status;
+}
+
+void
+VidcapScreen::donePaint ()
+{
+
+ cScreen->donePaint ();
+
+ /* at this point, 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 (clientRegion)
+ {
+ seomClientCapture (clientRegion, MIN (x1, x2), screen->height () - MAX (y1, y2));
+ cScreen->damageScreen ();
+ }
+
+ if (clientScreen)
+ {
+ seomClientCapture (clientScreen, 0, 0);
+ cScreen->damageScreen ();
+ }
+
+ glReadBuffer(GL_BACK);
+}
+
+void
+VidcapScreen::handleMotionEvent (int xRoot, int yRoot)
+{
+ if (grabIndex)
+ {
+ x2 = xRoot;
+ y2 = yRoot;
+
+ cScreen->damageScreen ();
+ }
+}
+
+void
+VidcapScreen::handleEvent(XEvent *event)
+{
+
+ switch (event->type)
+ {
+ case MotionNotify:
+ handleMotionEvent(pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ handleMotionEvent (pointerX, pointerY);
+ default:
+ if (fixesEventBase && event->type == fixesEventBase + XFixesCursorNotify)
+ updateCursor();
+ break;
+ }
+
+ screen->handleEvent (event);
+}
+
+void
+VidcapScreen::updateWatermarkPos ()
+{
+ CompPoint &trans = wT.trans;
+ CompPoint off (optionGetWatermarkXOffset (), optionGetWatermarkYOffset ());
+
+ switch (optionGetWatermarkPosition ())
+ {
+ case VidcapOptions::WatermarkPositionTopLeft:
+ trans = off;
+ break;
+ case VidcapOptions::WatermarkPositionTopRight:
+ trans.setY (off.y ());
+ trans.setX (screen->width () - wT.size.width () - off.x ());
+ break;
+ case VidcapOptions::WatermarkPositionBottomLeft:
+ trans.setY (screen->height () - wT.size.height () - off.y ());
+ trans.setX (off.x ());
+ break;
+ case VidcapOptions::WatermarkPositionBottomRight:
+ trans.setX (screen->width () - wT.size.width () - off.x ());
+ trans.setY (screen->height () - wT.size.height () - off.y ());
+ break;
+ default:
+ break;
+ }
+}
+
+void
+VidcapScreen::optionChange (CompOption *option,
+ Options num)
+{
+ switch (num)
+ {
+ case VidcapOptions::WatermarkImage:
+ {
+ CompString path = optionGetWatermarkImage ();
+ wT.texture = GLTexture::readImageToTexture (path,
+ wT.size);
+
+ if (!wT.texture.size ())
+ compLogMessage ("vidcap", CompLogLevelWarn,
+ "Failed to load image: %s",
+ path.c_str ());
+
+ updateWatermarkPos ();
+ }
+ break;
+ case VidcapOptions::WatermarkPosition:
+ case VidcapOptions::WatermarkXOffset:
+ case VidcapOptions::WatermarkYOffset:
+ updateWatermarkPos ();
+ default:
+ break;
+ }
+}
+
+
+VidcapScreen::VidcapScreen (CompScreen *screen) :
+ PluginClassHandler<VidcapScreen,CompScreen> (screen),
+ cScreen (CompositeScreen::get (screen)),
+ gScreen (GLScreen::get (screen)),
+ fixesEventBase (0),
+ grabIndex (0),
+ clientScreen (NULL),
+ clientRegion (NULL)
+{
+ ScreenInterface::setHandler (screen, false);
+ CompositeScreenInterface::setHandler (cScreen, false);
+ GLScreenInterface::setHandler (gScreen, false);
+
+ int eventBase, errorBase;
+
+ if (XFixesQueryExtension (screen->dpy (), &eventBase, &errorBase))
+ {
+ int major, minor;
+ XFixesQueryVersion (screen->dpy (), &major, &minor);
+
+ if (major >= 4)
+ fixesEventBase = eventBase;
+ }
+
+ glEnable (GL_TEXTURE_RECTANGLE_ARB);
+
+ glGenTextures(1, &cT.name);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cT.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);
+
+
+ optionSetToggleScreenInitiate (boost::bind (&VidcapScreen::
+ toggleFullscreenCapture, this, _1, _2, _3));
+
+ optionSetInitiateRegionInitiate (boost::bind (&VidcapScreen::
+ initiateRegionCapture, this, _1, _2, _3));
+ optionSetInitiateRegionTerminate (boost::bind (&VidcapScreen::
+ terminateRegionCapture, this, _1, _2, _3));
+
+ optionSetWatermarkImageNotify (boost::bind (&VidcapScreen::
+ optionChange, this, _1, _2));
+ optionSetWatermarkPositionNotify (boost::bind (&VidcapScreen::
+ optionChange, this, _1, _2));
+ optionSetWatermarkXOffsetNotify (boost::bind (&VidcapScreen::
+ optionChange, this, _1, _2));
+ optionSetWatermarkYOffsetNotify (boost::bind (&VidcapScreen::
+ optionChange, this, _1, _2));
+}
+
+VidcapScreen::~VidcapScreen ()
+{
+ glDeleteTextures (1, &cT.name);
+}
+
+bool
+VidcapPluginVTable::init ()
+{
+ if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) |
+ !CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) |
+ !CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI))
+ return false;
+
+ return true;
+}
diff --git a/src/vidcap.h b/src/vidcap.h
new file mode 100644
index 0000000..19121b4
--- /dev/null
+++ b/src/vidcap.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ * Copyright Tomac Carnecky 2006
+ *
+ * Ported to Compiz 0.9.x
+ * Copyright : (c) 2010 Scott Moreau <oreaus@gmail.com>
+ *
+ * vidcap.h
+ *
+ * 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: David Reveman <davidr@novell.com>
+ * Author: Tomas Carnecky <tom@dbservice.com>
+ */
+
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <dlfcn.h>
+
+extern "C"
+{
+#include <seom/seom.h>
+}
+
+#include <core/core.h>
+#include <composite/composite.h>
+#include <opengl/opengl.h>
+
+#include "vidcap_options.h"
+
+typedef struct cursorTexture
+{
+ GLuint name;
+ int width, height, hotX, hotY;
+} cursorTexture;
+
+class watermarkTexture
+{
+ public:
+
+ watermarkTexture () {};
+
+ GLTexture::List texture;
+ CompSize size;
+ CompPoint trans;
+};
+
+
+class VidcapScreen :
+ public PluginClassHandler <VidcapScreen, CompScreen>,
+ public VidcapOptions,
+ public ScreenInterface,
+ public CompositeScreenInterface,
+ public GLScreenInterface
+{
+ public:
+
+ VidcapScreen (CompScreen *screen);
+ ~VidcapScreen ();
+
+ CompositeScreen *cScreen;
+ GLScreen *gScreen;
+
+ int fixesEventBase;
+ int x1, y1, x2, y2;
+
+ CompScreen::GrabHandle grabIndex;
+
+ seomClient *clientScreen;
+ seomClient *clientRegion;
+
+ cursorTexture cT;
+ watermarkTexture wT;
+
+ public:
+
+ void toggleFunctions (bool enabled);
+
+ void updateCursor ();
+
+ void showCursor ();
+
+ void hideCursor ();
+
+ void
+ optionChange (CompOption *, Options);
+
+ void
+ updateWatermarkPos ();
+
+ bool toggleFullscreenCapture (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector options);
+
+ bool initiateRegionCapture (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector options);
+
+ bool terminateRegionCapture (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector options);
+
+ bool glPaintOutput (const GLScreenPaintAttrib &attrib,
+ const GLMatrix &transform,
+ const CompRegion &region,
+ CompOutput *output,
+ unsigned int mask);
+
+ void donePaint ();
+
+ void handleMotionEvent (int xRoot, int yRoot);
+
+ void handleEvent(XEvent *event);
+};
+
+class VidcapPluginVTable :
+ public CompPlugin::VTableForScreen <VidcapScreen>
+{
+ public:
+ bool init ();
+};
+
+COMPIZ_PLUGIN_20090315 (vidcap, VidcapPluginVTable);
+
diff --git a/vidcap.xml.in b/vidcap.xml.in
new file mode 100644
index 0000000..83a31f5
--- /dev/null
+++ b/vidcap.xml.in
@@ -0,0 +1,123 @@
+<?xml version="1.0"?>
+<compiz>
+ <plugin name="vidcap" useBcop="true">
+ <_short>Vidcap</_short>
+ <_long>Video capture plugin</_long>
+ <category>Extras</category>
+ <deps>
+ <relation type="after">
+ <plugin>opengl</plugin>
+ <plugin>imgpng</plugin>
+ <plugin>imgsvg</plugin>
+ <plugin>imgjpeg</plugin>
+ <plugin>decor</plugin>
+ </relation>
+ <requirement>
+ <plugin>opengl</plugin>
+ </requirement>
+ </deps>
+ <options>
+ <group>
+ <_short>Bindings</_short>
+ <option name="toggle_screen" type="key">
+ <_short>Toggle Screen</_short>
+ <_long>Toggle capturing of the current screen.</_long>
+ <default>&lt;Super&gt;F8</default>
+ </option>
+ <option name="initiate_region" type="button">
+ <_short>Initiate Region</_short>
+ <_long>Initiate region capture.</_long>
+ <default>&lt;Super&gt;Button2</default>
+ </option>
+ </group>
+ <group>
+ <_short>Settings</_short>
+ <option name="output_string" type="string">
+ <_short>Output</_short>
+ <_long>File path or server address.</_long>
+ <hints>file;</hints>
+ <default>file:///tmp/capture.seom</default>
+ </option>
+ <option name="framerate" type="float">
+ <_short>Framerate</_short>
+ <_long>Capture framerate (per second).</_long>
+ <default>5.0</default>
+ <min>0.2</min>
+ <max>200</max>
+ <precision>0.2</precision>
+ </option>
+ <option name="scale_amount" type="int">
+ <_short>Scale</_short>
+ <_long>Scale video down.</_long>
+ <min>0</min>
+ <max>5</max>
+ <default>0</default>
+ </option>
+ <option name="capture_cursor" type="bool">
+ <_short>Capture Cursor</_short>
+ <_long>Capture the cursor while recording video.</_long>
+ <default>true</default>
+ </option>
+ <option name="display_watermark" type="bool">
+ <_short>Watermark</_short>
+ <_long>Display watermark over the captured video.</_long>
+ <default>false</default>
+ </option>
+ <option name="watermark_image" type="string">
+ <_short>Watermark Image</_short>
+ <_long>Image to use for watermark.</_long>
+ <hints>file;image;</hints>
+ <default>watermark.png</default>
+ </option>
+ <subgroup>
+ <short>Watermark</short>
+ <option name="watermark_position" type="int">
+ <_short>Watermark Position</_short>
+ <_long>Where to place the watermark on screen</_long>
+ <min>0</min>
+ <max>3</max>
+ <default>2</default>
+ <desc>
+ <value>0</value>
+ <_name>Top Right</_name>
+ </desc>
+ <desc>
+ <value>1</value>
+ <_name>Top Left</_name>
+ </desc>
+ <desc>
+ <value>2</value>
+ <_name>Bottom Right</_name>
+ </desc>
+ <desc>
+ <value>3</value>
+ <_name>Bottom Left</_name>
+ </desc>
+ </option>
+ <option name="watermark_x_offset" type="int">
+ <_short>X Offset</_short>
+ <_long>Watermark X Offset</_long>
+ <default>0</default>
+ <min>0</min>
+ <max>200</max>
+ </option>
+ <option name="watermark_y_offset" type="int">
+ <_short>Y Offset</_short>
+ <_long>Watermark Y Offset</_long>
+ <default>0</default>
+ <min>0</min>
+ <max>200</max>
+ </option>
+ </subgroup>
+ </group>
+ <group>
+ <_short>Post Processing</_short>
+ <option name="command_string" type="string">
+ <_short>Command</_short>
+ <_long>Command line that is executed after capturing.</_long>
+ <default>seom-filter /tmp/capture.seom &gt; /tmp/seom.out</default>
+ </option>
+ </group>
+ </options>
+ </plugin>
+</compiz>