Logo Search packages:      
Sourcecode: captury version File versions  Download package

libGLcaptury.cpp

/////////////////////////////////////////////////////////////////////////////
//
//  Captury - http://rm-rf.in/captury
//  $Id$
//
//  Copyright (c) 2007 by Christian Parpart <trapni@gentoo.org>
//
//  This file as well as its whole library is licensed under
//  the terms of GPL. See the file COPYING.
//
/////////////////////////////////////////////////////////////////////////////
#include "libGLcaptury.h"
#include "TScreenshot.h"
#include "log.h"

#include <captury/captury.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>

#define GLX_GLXEXT_PROTOTYPES // required for glXGetProcAddressARB when using xorg-x11 GL headers
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>

#include <dlfcn.h>

#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

#include <png.h>

#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>

// ------------------------------------------------------------------------------------------------
//
// ENVIRONMENT VARIABLES:
//
//          CAPTURY_OUTPUT_DIR            : base directory of where to store the captured movies (default: /tmp)
//          CAPTURY_FPS                   : the fps to capture with (default: 25)
//          CAPTURY_SCALE                 : image down scaling (default: 0)
//          CAPTURY_HOTKEY_MOVIE      : defines the hotkey for toggling movie capture (default: F12)
//          CAPTURY_HOTKEY_SCREENSHOT : defines hotkey for taking screenshot (default: F10)
//          CAPTURY_EXPECTED        : enforces exit(1), if set to any value and a hook resolv error happend
//
// TODO
//
// * all predicated Xlib functions: (XIfEvent, XCheckIfEvent...)
//   we shall use our own predicate to hook our handleXEvent() inside the predicate
//   and pass any remaining event to `predicate` until it returns True.
// * print HUD with FPS and MiB/s state on capture

// {{{ programName()
bool isPathSeperator(char AChar) {
      return AChar == '/' || AChar == '\\';
}

const char *programName() {
      static char buf[1024] = { 0 };

      if (buf[0] == '\0') {
            int i = readlink("/proc/self/exe", buf, sizeof(buf));
            if (i != -1) {
                  // extract program name from full path
                  buf[i] = 0;
                  for (--i; i >= 0 && !isPathSeperator(buf[i]); --i);
                  if (isPathSeperator(buf[i]))
                        strcpy(buf, buf + i + 1);

                  if (strcmp(buf, "wine-preloader") == 0) {
                        // in case of wine/cedega, handle the application name with slight more precision
                        int fd = open("/proc/self/cmdline", O_RDONLY);
                        assert(fd != 0);

                        char cmdline[4096];
                        int nread = read(fd, cmdline, sizeof(cmdline));
                        assert(nread != -1);
                        cmdline[nread] = 0;

                        char *args[8];
                        int nargs = 0;

                        for (char *p = cmdline; *p && nargs < int(sizeof(args) / sizeof(args[0])); ++p)
                              for (args[nargs++] = p; *p; ++p);

                        for (i = 1; i < nargs; ++i) { // cedega should match here
                              if (strcmp(args[i - 1], "--") == 0) {
                                    strncpy(buf, args[i], sizeof(buf));
                                    break;
                              }
                        }

                        if (i == nargs) // otherwise, it's wine
                              strncpy(buf, args[0], sizeof(buf));

                        for (i = strlen(buf) - 1; i >= 0 && !isPathSeperator(buf[i]); --i);

                        if (isPathSeperator(buf[i]))
                              strcpy(buf, buf + i + 1);
                  }
            } else {
                  logWarning("Error resolving program name: %s", strerror(errno));
                  strcpy(buf, "unknown");
            }
      }
      return buf;
}
// }}}

// {{{ hooks
int hookErrors = 0;

void hookError(const char *baseName) {
      ++hookErrors;

      logError("Could not hook into function %s: %s", baseName, dlerror());

      if (getenv("CAPTURY_EXPECTED") != 0)
            exit(1);
}

void TFunctionTable::load() {
      HOOK(glXGetProcAddressARB);
      HOOK(glXSwapBuffers);

      HOOK(XPending);
      HOOK(XNextEvent);
      HOOK(XPeekEvent);
      HOOK(XWindowEvent);
      HOOK(XCheckWindowEvent);
      HOOK(XMaskEvent);
      HOOK(XCheckMaskEvent);
      HOOK(XCheckTypedEvent);
      HOOK(XCheckTypedWindowEvent);

      HOOK(XIfEvent);
      HOOK(XCheckIfEvent);
      HOOK(XPeekEvent);
}
// }}}

// ------------------------------------------------------------------------------------------------
// runtime state vars
TFunctionTable hooked;
captury_t *cd = 0;

bool captureMovie = false;
bool takeScreenshot = false;

// global config vars
THotkeys hotkeys;
char outputBaseDir[1024] = { 0 };
double fps = 25.0;
int scale = 0;
bool showCursor = false;

// ------------------------------------------------------------------------------------------------

void loadConfig() {
      // - /etc/captury.conf
      // - $HOME/.config/captury.conf

      // determine hotkey for toggling movie capture
      char *value = getenv("CAPTURY_HOTKEY_MOVIE");
      if (!value || !*value)
            value = "F12";

      hotkeys.toggleCaptureMovie = XStringToKeysym(value);

      // screenshot hotkey...
      value = getenv("CAPTURY_HOTKEY_SCREENSHOT");
      if (!value || !*value)
            value = "F10";

      hotkeys.takeScreenshot = XStringToKeysym(value);

      // auto-start movie capturing
      value = getenv("CAPTURY_AUTO_CAPTURE");
      if (value && (!strcmp(value, "yes") || !strcmp(value, "1")))
            captureMovie = true;

      // explicit cursor drawing?
      value = getenv("CAPTURY_CURSOR");
      if (value && (!strcmp(value, "yes") || !strcmp(value, "1")))
            showCursor = true;

      // determine output base dir (must not contain trailing slash)
      if (getenv("CAPTURY_OUTPUT_DIR") != 0)
            strncpy(outputBaseDir, getenv("CAPTURY_OUTPUT_DIR"), sizeof(outputBaseDir));

      if (!outputBaseDir[0])
            strncpy(outputBaseDir, "/tmp", sizeof(outputBaseDir));

      if (outputBaseDir[strlen(outputBaseDir) - 1] == '/')
            outputBaseDir[strlen(outputBaseDir) - 1] = 0;

      // determine fps to capture with
      if (getenv("CAPTURY_FPS") != 0) {
            fps = strtod(getenv("CAPTURY_FPS"), 0);
      }

      // determine video scaling value
      if (getenv("CAPTURY_SCALE") != 0) {
            scale = atoi(getenv("CAPTURY_SCALE"));
      }
}

const char *mediaFileNameBase() {
      static char result[1024];
      snprintf(result, sizeof(result), "%s/%s - %s", outputBaseDir, programName(), getTimeStamp());

      return result;
}

const char *movieFileName() {
      static char result[1024];
      snprintf(result, sizeof(result), "%s.cps", mediaFileNameBase());
      return result;
}

const char *screenshotFileName() {
      static char result[1024];
      snprintf(result, sizeof(result), "%s.png", mediaFileNameBase());
      return result;
}

// ------------------------------------------------------------------------------------------------
unsigned FCurrentWidth = 0;
unsigned FCurrentHeight = 0;

void updateGeometry(Display *dpy, Drawable drawable) {
      int stmp;
      unsigned utmp;

      Window root;
      XGetGeometry(dpy, drawable, &root, &stmp, &stmp, &FCurrentWidth, &FCurrentHeight, &utmp, &utmp);
}

bool startMovieCapture(Display *dpy, Window window) {
      captury_config_t config;
      bzero(&config, sizeof(config));

      config.x = 0;
      config.y = 0;
      config.width = FCurrentWidth;
      config.height = FCurrentHeight;
      config.fps = fps;
      config.scale = scale;
      config.deviceType = CAPTURY_DEVICE_GLX;
      config.deviceHandle = dpy;
      config.windowHandle = window;
      config.cursor = showCursor;

      cd = CapturyOpen(&config);
      if (!cd) {
            logError("could not open captury device");
            return false;
      }

      const char *fileName = movieFileName();
      log(1, "initiated movie capture at %dx%d+%d+%d: %s", config.width, config.height, config.x, config.y, fileName);
      if (CapturySetOutputFileName(cd, fileName) == -1) {
            logError("error setting output stream: %s", strerror(errno));
            CapturyClose(cd);
            cd = 0;
      }
      return true;
}

void stopMovieCapture() {
      if (!cd)
            return;

      log(1, "stopping movie capture");
      CapturyClose(cd);
      cd = 0;
}

// ------------------------------------------------------------------------------------------------
__attribute__((constructor))
void initialize() {
      log(2, "initializing (%s)", programName());

      hooked.load();
      loadConfig();
}

__attribute__((destructor))
void finalize() {
      if (cd) {
            stopMovieCapture();
      }
}

// vim:ai:noet:ts=4:nowrap

Generated by  Doxygen 1.6.0   Back to index