summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2020-07-11 16:47:00 -0700
committerVito Caputo <vcaputo@pengaru.com>2020-07-11 16:47:00 -0700
commit3625160acc1715fc380f58ec3c4248485bed2370 (patch)
treedc95a32d81daac298cef69879a639029797fb762 /src
parentcfcca8681b88a171fb2cdbb83daa5f22bbedb6b8 (diff)
*: drop {gtk,qt}-recordmydesktop subdirs
This restores the recordmydesktop/ subdir as root from the mirror I cloned by fork from. I have no particular interest in the gtk/qt frontends and it doesn't appear they were part of a single tree in the past. But I will probably preserve backwards compatibility of the cli so they can continue to work with this fork installed.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am94
-rw-r--r--src/rmd.c184
-rw-r--r--src/rmd_cache.c218
-rw-r--r--src/rmd_cache.h94
-rw-r--r--src/rmd_cache_audio.c104
-rw-r--r--src/rmd_cache_audio.h42
-rw-r--r--src/rmd_cache_frame.c319
-rw-r--r--src/rmd_cache_frame.h43
-rw-r--r--src/rmd_capture_sound.c165
-rw-r--r--src/rmd_capture_sound.h42
-rw-r--r--src/rmd_encode_cache.c72
-rw-r--r--src/rmd_encode_cache.h41
-rw-r--r--src/rmd_encode_image_buffer.c99
-rw-r--r--src/rmd_encode_image_buffer.h50
-rw-r--r--src/rmd_encode_sound_buffer.c178
-rw-r--r--src/rmd_encode_sound_buffer.h51
-rw-r--r--src/rmd_error.c59
-rw-r--r--src/rmd_error.h49
-rw-r--r--src/rmd_flush_to_ogg.c235
-rw-r--r--src/rmd_flush_to_ogg.h40
-rw-r--r--src/rmd_frame.c142
-rw-r--r--src/rmd_frame.h101
-rw-r--r--src/rmd_get_frame.c593
-rw-r--r--src/rmd_get_frame.h41
-rw-r--r--src/rmd_getzpixmap.c117
-rw-r--r--src/rmd_getzpixmap.h99
-rw-r--r--src/rmd_init_encoder.c346
-rw-r--r--src/rmd_init_encoder.h47
-rw-r--r--src/rmd_initialize_data.c252
-rw-r--r--src/rmd_initialize_data.h62
-rw-r--r--src/rmd_jack.c209
-rw-r--r--src/rmd_jack.h59
-rw-r--r--src/rmd_load_cache.c336
-rw-r--r--src/rmd_load_cache.h42
-rw-r--r--src/rmd_macro.h83
-rw-r--r--src/rmd_make_dummy_pointer.c94
-rw-r--r--src/rmd_make_dummy_pointer.h59
-rw-r--r--src/rmd_math.c33
-rw-r--r--src/rmd_math.h44
-rw-r--r--src/rmd_opendev.c189
-rw-r--r--src/rmd_opendev.h84
-rw-r--r--src/rmd_parseargs.c511
-rw-r--r--src/rmd_parseargs.h47
-rw-r--r--src/rmd_poll_events.c245
-rw-r--r--src/rmd_poll_events.h49
-rw-r--r--src/rmd_queryextensions.c88
-rw-r--r--src/rmd_queryextensions.h53
-rw-r--r--src/rmd_rectinsert.c480
-rw-r--r--src/rmd_rectinsert.h55
-rw-r--r--src/rmd_register_callbacks.c86
-rw-r--r--src/rmd_register_callbacks.h41
-rw-r--r--src/rmd_rescue.c124
-rw-r--r--src/rmd_rescue.h42
-rw-r--r--src/rmd_setbrwindow.c115
-rw-r--r--src/rmd_setbrwindow.h53
-rw-r--r--src/rmd_shortcuts.c142
-rw-r--r--src/rmd_shortcuts.h57
-rw-r--r--src/rmd_specsfile.c151
-rw-r--r--src/rmd_specsfile.h59
-rw-r--r--src/rmd_threads.c169
-rw-r--r--src/rmd_threads.h43
-rw-r--r--src/rmd_timer.c81
-rw-r--r--src/rmd_timer.h44
-rw-r--r--src/rmd_types.h366
-rw-r--r--src/rmd_update_image.c95
-rw-r--r--src/rmd_update_image.h70
-rw-r--r--src/rmd_wm_check.c76
-rw-r--r--src/rmd_wm_check.h45
-rw-r--r--src/rmd_wm_is_compositing.c61
-rw-r--r--src/rmd_wm_is_compositing.h48
-rw-r--r--src/rmd_yuv_utils.c517
-rw-r--r--src/rmd_yuv_utils.h83
-rw-r--r--src/skeleton.c296
-rw-r--r--src/skeleton.h78
-rw-r--r--src/test-rectinsert-data.c382
-rw-r--r--src/test-rectinsert-data.h30
-rw-r--r--src/test-rectinsert-types.h41
-rw-r--r--src/test-rectinsert.c177
78 files changed, 10311 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..a63c3c4
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,94 @@
+bin_PROGRAMS = recordmydesktop
+
+recordmydesktop_SOURCES = \
+ rmd.c \
+ rmd_cache.c \
+ rmd_cache.h \
+ rmd_cache_audio.c \
+ rmd_cache_audio.h \
+ rmd_cache_frame.c \
+ rmd_cache_frame.h \
+ rmd_capture_sound.c \
+ rmd_capture_sound.h \
+ rmd_encode_cache.c \
+ rmd_encode_cache.h \
+ rmd_encode_image_buffer.c \
+ rmd_encode_image_buffer.h \
+ rmd_encode_sound_buffer.c \
+ rmd_encode_sound_buffer.h \
+ rmd_error.c \
+ rmd_error.h \
+ rmd_flush_to_ogg.c \
+ rmd_flush_to_ogg.h \
+ rmd_frame.c \
+ rmd_frame.h \
+ rmd_get_frame.c \
+ rmd_get_frame.h \
+ rmd_getzpixmap.c \
+ rmd_getzpixmap.h \
+ rmd_init_encoder.c \
+ rmd_init_encoder.h \
+ rmd_initialize_data.c \
+ rmd_initialize_data.h \
+ rmd_jack.c \
+ rmd_jack.h \
+ rmd_load_cache.c \
+ rmd_load_cache.h \
+ rmd_macro.h \
+ rmd_make_dummy_pointer.c \
+ rmd_make_dummy_pointer.h \
+ rmd_math.c \
+ rmd_math.h \
+ rmd_opendev.c \
+ rmd_opendev.h \
+ rmd_parseargs.c \
+ rmd_parseargs.h \
+ rmd_poll_events.c \
+ rmd_poll_events.h \
+ rmd_queryextensions.c \
+ rmd_queryextensions.h \
+ rmd_rectinsert.c \
+ rmd_rectinsert.h \
+ rmd_register_callbacks.c \
+ rmd_register_callbacks.h \
+ rmd_rescue.c \
+ rmd_rescue.h \
+ rmd_setbrwindow.c \
+ rmd_setbrwindow.h \
+ rmd_shortcuts.c \
+ rmd_shortcuts.h \
+ rmd_specsfile.c \
+ rmd_specsfile.h \
+ rmd_threads.c \
+ rmd_threads.h \
+ rmd_timer.c \
+ rmd_timer.h \
+ rmd_types.h \
+ rmd_update_image.c \
+ rmd_update_image.h \
+ rmd_wm_check.c \
+ rmd_wm_check.h \
+ rmd_wm_is_compositing.c \
+ rmd_wm_is_compositing.h \
+ rmd_yuv_utils.c \
+ rmd_yuv_utils.h \
+ skeleton.c \
+ skeleton.h
+
+recordmydesktop_CPPFLAGS = -D_THREAD_SAFE -pthread -Wall
+recordmydesktop_LDFLAGS = @X_LIBS@ @X_EXTRA_LIBS@ @X_PRE_LIBS@
+
+
+# RectInsert test
+TESTS = test-rectinsert
+EXTRA_PROGRAMS = test-rectinsert
+CLEANFILES = $(EXTRA_PROGRAMS)
+
+test_rectinsert_SOURCES = \
+ test-rectinsert.c \
+ test-rectinsert-data.c \
+ test-rectinsert-data.h \
+ test-rectinsert-types.h \
+ rmd_rectinsert.c
+
+test_rectinsert_CFLAGS = -Wall
diff --git a/src/rmd.c b/src/rmd.c
new file mode 100644
index 0000000..5a93e79
--- /dev/null
+++ b/src/rmd.c
@@ -0,0 +1,184 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+
+#include "rmd_cache.h"
+#include "rmd_encode_cache.h"
+#include "rmd_error.h"
+#include "rmd_initialize_data.h"
+#include "rmd_parseargs.h"
+#include "rmd_queryextensions.h"
+#include "rmd_rescue.h"
+#include "rmd_setbrwindow.h"
+#include "rmd_shortcuts.h"
+#include "rmd_threads.h"
+#include "rmd_wm_is_compositing.h"
+#include "rmd_types.h"
+
+#include <X11/Xlib.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+int main(int argc, char **argv){
+ ProgData pdata = {};
+ EncData enc_data = {};
+ CacheData cache_data = {};
+ int exit_status = 0;
+
+#ifdef HAVE_LIBJACK
+ JackData jdata;
+
+ // Give jack access to program data, mainly for program state
+ jdata.pdata = &pdata;
+ pdata.jdata = &jdata;
+#endif
+
+ rmdSetupDefaultArgs(&pdata.args);
+
+ if (!rmdParseArgs(argc, argv, &pdata.args))
+ exit(1);
+
+ if (pdata.args.rescue_path != NULL)
+ exit(rmdRescue(pdata.args.rescue_path));
+
+ if (XInitThreads() == 0) {
+ fprintf(stderr, "Couldn't initialize thread support!\n");
+ exit(7);
+ }
+
+ if (pdata.args.display != NULL) {
+ pdata.dpy = XOpenDisplay(pdata.args.display);
+ XSetErrorHandler(rmdErrorHandler);
+ } else {
+ fprintf(stderr, "No display specified for connection!\n");
+ exit(8);
+ }
+
+ if (pdata.dpy == NULL) {
+ fprintf(stderr, "Cannot connect to X server %s\n", pdata.args.display);
+ exit(9);
+ }
+
+ // Query display specs
+ pdata.specs.screen = DefaultScreen(pdata.dpy);
+ pdata.specs.width = DisplayWidth(pdata.dpy, pdata.specs.screen);
+ pdata.specs.height = DisplayHeight(pdata.dpy, pdata.specs.screen);
+ pdata.specs.root = RootWindow(pdata.dpy, pdata.specs.screen);
+ pdata.specs.visual = DefaultVisual(pdata.dpy, pdata.specs.screen);
+ pdata.specs.gc = DefaultGC(pdata.dpy, pdata.specs.screen);
+ pdata.specs.depth = DefaultDepth(pdata.dpy, pdata.specs.screen);
+
+ if (pdata.specs.depth != 32 && pdata.specs.depth != 24 && pdata.specs.depth != 16) {
+ fprintf(stderr,
+ "Only 32bpp, 24bpp, and 16bpp"
+ " color depth modes are currently supported.\n");
+ exit(10);
+ }
+
+ if (!rmdSetBRWindow(pdata.dpy, &pdata.brwin, &pdata.specs, &pdata.args))
+ exit(11);
+
+ if (!pdata.args.nowmcheck && rmdWMIsCompositing( pdata.dpy, pdata.specs.screen ) ) {
+
+ fprintf(stderr, "\nDetected compositing window manager.\n"
+ "Reverting to full screen capture at every frame.\n"
+ "To disable this check run with --no-wm-check\n"
+ "(though that is not advised, since it will "
+ "probably produce faulty results).\n\n");
+ pdata.args.full_shots = 1;
+ pdata.args.noshared = 0;
+ }
+
+ rmdQueryExtensions( pdata.dpy,
+ &pdata.args,
+ &pdata.damage_event,
+ &pdata.damage_error,
+ &pdata.shm_opcode);
+
+ exit_status = rmdInitializeData(&pdata, &enc_data, &cache_data);
+ if (exit_status)
+ exit(exit_status);
+
+ if ( !strcmp(pdata.args.pause_shortcut, pdata.args.stop_shortcut) ||
+ rmdRegisterShortcut( pdata.dpy, pdata.specs.root,
+ pdata.args.pause_shortcut,
+ &(pdata.pause_key)) ||
+ rmdRegisterShortcut( pdata.dpy,
+ pdata.specs.root,
+ pdata.args.stop_shortcut,
+ &(pdata.stop_key))) {
+
+ fprintf(stderr, "Invalid shortcut, or shortcuts are the same!\n\nUsing defaults.\n");
+
+ rmdRegisterShortcut( pdata.dpy,
+ pdata.specs.root,
+ "Control+Mod1+p",
+ &(pdata.pause_key));
+
+ rmdRegisterShortcut( pdata.dpy,
+ pdata.specs.root,
+ "Control+Mod1+s",
+ &(pdata.stop_key));
+ }
+
+ //this is where the capturing happens.
+ rmdThreads(&pdata);
+
+ XCloseDisplay(pdata.dpy);
+ fprintf(stderr, ".\n");
+
+ //encode and then cleanup cache
+ if (!pdata.args.encOnTheFly && !pdata.args.no_encode) {
+ if (!pdata.aborted)
+ rmdEncodeCache(&pdata);
+
+ fprintf(stderr, "Cleanning up cache...\n");
+ if (rmdPurgeCache(pdata.cache_data, !pdata.args.nosound))
+ fprintf(stderr, "Some error occured while cleaning up cache!\n");
+
+ fprintf(stderr, "Done!!!\n");
+ }
+
+ if (pdata.aborted && pdata.args.encOnTheFly) {
+ if (remove(pdata.args.filename)) {
+ perror("Error while removing file:\n");
+ return 1;
+ } else {
+ fprintf(stderr, "SIGABRT received, file %s removed\n",
+ pdata.args.filename);
+ return 0;
+ }
+ } else
+ fprintf(stderr, "Goodbye!\n");
+
+ rmdCleanUp();
+
+ return exit_status;
+}
diff --git a/src/rmd_cache.c b/src/rmd_cache.c
new file mode 100644
index 0000000..1353bc2
--- /dev/null
+++ b/src/rmd_cache.c
@@ -0,0 +1,218 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_cache.h"
+
+#include "rmd_specsfile.h"
+#include "rmd_types.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+
+/**
+*Construct an number postfixed name
+*
+* \param name base name
+*
+* \param newname modified name
+*
+* \n number to be used as a postfix
+*
+*/
+static void rmdCacheFileN(char *name, char **newname, int n) { // Nth cache file
+ char numbuf[8];
+
+ strcpy(*newname, name);
+ strcat(*newname, ".");
+ snprintf(numbuf, 8, "%d", n);
+ strcat(*newname, numbuf);
+}
+
+int rmdSwapCacheFilesWrite(char *name, int n, gzFile *fp, FILE **ucfp) {
+ char *newname = malloc(strlen(name) + 10);
+
+ rmdCacheFileN(name, &newname, n);
+ if (*fp == NULL) {
+ fflush(*ucfp);
+ fclose(*ucfp);
+ *ucfp = fopen(newname, "wb");
+ } else {
+ gzflush(*fp, Z_FINISH);
+ gzclose(*fp);
+ *fp = gzopen(newname, "wb0f");
+ }
+ free(newname);
+
+ return ((*fp == NULL) && (*ucfp == NULL));
+}
+
+int rmdSwapCacheFilesRead(char *name, int n, gzFile *fp, FILE **ucfp) {
+ char *newname = malloc(strlen(name) + 10);
+
+ rmdCacheFileN(name, &newname, n);
+ if (*fp == NULL) {
+ fclose(*ucfp);
+ *ucfp = fopen(newname, "rb");
+ } else {
+ gzclose(*fp);
+ *fp = gzopen(newname, "rb");
+ }
+ free(newname);
+
+ return ((*fp == NULL) && (*ucfp == NULL));
+}
+
+int rmdPurgeCache(CacheData *cache_data_t, int sound) {
+ struct stat buff;
+ char *fname;
+ int exit_value = 0;
+ int nth_cache = 1;
+
+ fname = malloc(strlen(cache_data_t->imgdata) + 10);
+ strcpy(fname, cache_data_t->imgdata);
+
+ while (stat(fname, &buff) == 0) {
+ if (remove(fname)) {
+ fprintf(stderr, "Couldn't remove temporary file %s", cache_data_t->imgdata);
+ exit_value = 1;
+ }
+ rmdCacheFileN(cache_data_t->imgdata, &fname, nth_cache);
+ nth_cache++;
+ }
+
+ free(fname);
+
+ if (sound) {
+ if (remove(cache_data_t->audiodata)) {
+ fprintf(stderr, "Couldn't remove temporary file %s", cache_data_t->audiodata);
+ exit_value = 1;
+ }
+ }
+
+ if (remove(cache_data_t->specsfile)) {
+ fprintf(stderr, "Couldn't remove temporary file %s", cache_data_t->specsfile);
+ exit_value = 1;
+ }
+
+ if (remove(cache_data_t->projname)) {
+ fprintf(stderr, "Couldn't remove temporary directory %s", cache_data_t->projname);
+ exit_value = 1;
+ }
+
+ return exit_value;
+}
+
+void rmdInitCacheData(ProgData *pdata, EncData *enc_data_t, CacheData *cache_data_t) {
+ int width, height, pid;
+ char pidbuf[8];
+
+ //we set the buffer only since there's
+ //no need to initialize the encoder from now.
+ width = ((pdata->brwin.rrect.width + 15) >> 4) << 4;
+ height = ((pdata->brwin.rrect.height + 15) >> 4) << 4;
+
+ (pdata)->enc_data = enc_data_t;
+
+ enc_data_t->yuv.y = (unsigned char *)malloc(height * width);
+ enc_data_t->yuv.u = (unsigned char *)malloc(height * width / 4);
+ enc_data_t->yuv.v = (unsigned char *)malloc(height * width / 4);
+ enc_data_t->yuv.y_width = width;
+ enc_data_t->yuv.y_height = height;
+ enc_data_t->yuv.y_stride = width;
+
+ enc_data_t->yuv.uv_width = width / 2;
+ enc_data_t->yuv.uv_height = height / 2;
+ enc_data_t->yuv.uv_stride = width / 2;
+
+ //now we set the cache files
+ (pdata)->cache_data = cache_data_t;
+
+ cache_data_t->workdir = (pdata->args).workdir;
+ pid = getpid();
+
+ snprintf( pidbuf, 8, "%d", pid );
+ //names are stored relatively to current dir(i.e. no chdir)
+ cache_data_t->projname = malloc(strlen(cache_data_t->workdir) + 12 + strlen(pidbuf) + 3);
+ //projname
+ strcpy(cache_data_t->projname, cache_data_t->workdir);
+ strcat(cache_data_t->projname, "/");
+ strcat(cache_data_t->projname, "rMD-session-");
+ strcat(cache_data_t->projname, pidbuf);
+ strcat(cache_data_t->projname, "/");
+ //image data
+ cache_data_t->imgdata = malloc(strlen(cache_data_t->projname) + 11);
+ strcpy(cache_data_t->imgdata, cache_data_t->projname);
+ strcat(cache_data_t->imgdata, "img.out");
+ //audio data
+ cache_data_t->audiodata = malloc(strlen(cache_data_t->projname) + 10);
+ strcpy(cache_data_t->audiodata, cache_data_t->projname);
+ strcat(cache_data_t->audiodata, "audio.pcm");
+ //specsfile
+ cache_data_t->specsfile = malloc(strlen(cache_data_t->projname) + 10);
+ strcpy(cache_data_t->specsfile, cache_data_t->projname);
+ strcat(cache_data_t->specsfile, "specs.txt");
+
+ //now that've got out buffers and our filenames we start
+ //creating the needed files
+
+ if (mkdir(cache_data_t->projname, 0777)) {
+ fprintf(stderr, "Could not create temporary directory %s !!!\n", cache_data_t->projname);
+ exit(13);
+ }
+
+ if (!pdata->args.zerocompression) {
+ cache_data_t->ifp = gzopen(cache_data_t->imgdata, "wb0f");
+ if (cache_data_t->ifp == NULL) {
+ fprintf(stderr, "Could not create temporary file %s !!!\n", cache_data_t->imgdata);
+ exit(13);
+ }
+ } else {
+ cache_data_t->uncifp = fopen(cache_data_t->imgdata, "wb0f");
+ if (cache_data_t->uncifp == NULL) {
+ fprintf(stderr, "Could not create temporary file %s !!!\n", cache_data_t->imgdata);
+ exit(13);
+ }
+ }
+
+ if (!pdata->args.nosound) {
+ cache_data_t->afp = fopen(cache_data_t->audiodata, "wb");
+ if (cache_data_t->afp == NULL) {
+ fprintf(stderr, "Could not create temporary file %s !!!\n", cache_data_t->audiodata);
+ exit(13);
+ }
+ }
+
+ if (rmdWriteSpecsFile(pdata)) {
+ fprintf(stderr, "Could not write specsfile %s !!!\n", cache_data_t->specsfile);
+ exit(13);
+ }
+}
diff --git a/src/rmd_cache.h b/src/rmd_cache.h
new file mode 100644
index 0000000..9f4acce
--- /dev/null
+++ b/src/rmd_cache.h
@@ -0,0 +1,94 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_CACHE_H
+#define RMD_CACHE_H 1
+
+#include "rmd_types.h"
+
+#include <zlib.h>
+
+#include <stdio.h>
+
+
+
+/**
+* Change file pointer to a new file while writting
+* (file name is incremented with CacheFileN)
+*
+* \param name base file name
+*
+* \param n number to be used as a postfix
+*
+* \param fp File pointer if compression is used(must be NULL otherwise)
+*
+* \param ucfp File pointer if compression is NOT used(must be NULL otherwise)
+*
+* \returns 0 on Success 1 on Failure
+*/
+int rmdSwapCacheFilesWrite(char *name,int n,gzFile *fp,FILE **ucfp);
+
+/**
+* Change file pointer to a new file while reading
+* (file name is incremented with CacheFileN)
+*
+* \param name base file name
+*
+* \param n number to be used as a postfix
+*
+* \param fp File pointer if compression is used(must be NULL otherwise)
+*
+* \param ucfp File pointer if compression is NOT used(must be NULL otherwise)
+*
+* \returns 0 on Success 1 on Failure
+*/
+int rmdSwapCacheFilesRead(char *name,int n,gzFile *fp,FILE **ucfp);
+
+/**
+* Delete all cache files
+*
+* \param cache_data_t Caching options(file names etc.)
+*
+* \returns 0 if all files and folders where deleted, 1 otherwise
+*/
+int rmdPurgeCache(CacheData *cache_data_t,int sound);
+
+/**
+* Initializes paths and everything else needed to start caching
+*
+* \param pdata ProgData struct containing all program data
+*
+* \param enc_data_t Encoding options
+*
+* \param cache_data_t Caching options
+*
+*/
+void rmdInitCacheData(ProgData *pdata,
+ EncData *enc_data_t,
+ CacheData *cache_data_t);
+
+
+#endif
diff --git a/src/rmd_cache_audio.c b/src/rmd_cache_audio.c
new file mode 100644
index 0000000..fabcb39
--- /dev/null
+++ b/src/rmd_cache_audio.c
@@ -0,0 +1,104 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_cache_audio.h"
+
+#include "rmd_jack.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+
+
+void *rmdCacheSoundBuffer(ProgData *pdata) {
+ size_t write_size = pdata->periodsize * pdata->sound_framesize;
+
+#ifdef HAVE_LIBJACK
+ void *jackbuf = NULL;
+
+ if (pdata->args.use_jack) {
+ write_size = pdata->sound_framesize * pdata->jdata->buffersize;
+ jackbuf = malloc(pdata->sound_framesize * pdata->jdata->buffersize);
+ }
+#endif
+
+//We are simply going to throw sound on the disk.
+//It's sound is tiny compared to that of image, so
+//compressing would reducethe overall size by only an
+//insignificant fraction.
+
+ while (pdata->running) {
+ SndBuffer *buff = NULL;
+
+ pthread_mutex_lock(&pdata->pause_mutex);
+ while (pdata->paused)
+ pthread_cond_wait(&pdata->pause_cond, &pdata->pause_mutex);
+ pthread_mutex_unlock(&pdata->pause_mutex);
+
+ if (!pdata->args.use_jack) {
+ pthread_mutex_lock(&pdata->sound_buffer_mutex);
+ while (!pdata->sound_buffer && pdata->running)
+ pthread_cond_wait(&pdata->sound_data_read, &pdata->sound_buffer_mutex);
+
+ buff = pdata->sound_buffer;
+ if (buff)
+ pdata->sound_buffer = buff->next;
+ pthread_mutex_unlock(&pdata->sound_buffer_mutex);
+
+ if (!pdata->running)
+ break;
+
+ fwrite(buff->data, 1, write_size, pdata->cache_data->afp);
+ free(buff->data);
+ free(buff);
+ } else {
+#ifdef HAVE_LIBJACK
+ pthread_mutex_lock(&pdata->sound_buffer_mutex);
+ while ( pdata->running &&
+ jack_ringbuffer_read_space(pdata->jdata->sound_buffer) < write_size)
+ pthread_cond_wait(&pdata->sound_data_read, &pdata->sound_buffer_mutex);
+
+ if (pdata->running)
+ jack_ringbuffer_read(pdata->jdata->sound_buffer, jackbuf, write_size);
+ pthread_mutex_unlock(&pdata->sound_buffer_mutex);
+
+ if (!pdata->running)
+ break;
+
+ fwrite(jackbuf, 1, write_size, pdata->cache_data->afp);
+#endif
+ }
+ pdata->avd -= pdata->periodtime;
+ }
+
+ fclose(pdata->cache_data->afp);
+ pthread_exit(&errno);
+}
diff --git a/src/rmd_cache_audio.h b/src/rmd_cache_audio.h
new file mode 100644
index 0000000..1ba3221
--- /dev/null
+++ b/src/rmd_cache_audio.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef CACHE_AUDIO_H
+#define CACHE_AUDIO_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Sound caching thread. Simply writes the pcm buffers on disk
+*
+* \param pdata ProgData struct containing all program data
+*
+*/
+void *rmdCacheSoundBuffer(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_cache_frame.c b/src/rmd_cache_frame.c
new file mode 100644
index 0000000..7b8a2f3
--- /dev/null
+++ b/src/rmd_cache_frame.c
@@ -0,0 +1,319 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_cache_frame.h"
+
+#include "rmd_yuv_utils.h"
+#include "rmd_cache.h"
+#include "rmd_types.h"
+
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+
+
+#define BYTES_PER_MB (1024 * 1024)
+#define CACHE_OUT_BUFFER_SIZE (4 * 1024)
+#define CACHE_FILE_SIZE_LIMIT (500 * 1024 * 1024)
+
+
+static int rmdFlushBlock(
+ unsigned char *buf,
+ int blockno,
+ int width,
+ int height,
+ int blockwidth,
+ gzFile fp,
+ FILE *ucfp,
+ int flush) {
+
+ int bytes_written = 0,
+ block_i = (!blockwidth) ? 0 : (blockno / (width / blockwidth)),//place on the grid
+ block_k = (!blockwidth) ? 0 : (blockno % (width / blockwidth));
+ register unsigned char *buf_reg = (&buf[(block_i * width + block_k) * blockwidth]);
+ static unsigned char out_buffer[CACHE_OUT_BUFFER_SIZE];
+ static unsigned int out_buffer_bytes = 0;
+
+ if (out_buffer_bytes + pow(blockwidth, 2) >= CACHE_OUT_BUFFER_SIZE || (flush && out_buffer_bytes)) {
+ if (ucfp == NULL)
+ gzwrite(fp, (void *)out_buffer, out_buffer_bytes);
+ else
+ fwrite((void *)out_buffer, 1, out_buffer_bytes, ucfp);
+
+ bytes_written = out_buffer_bytes;
+ out_buffer_bytes = 0;
+ }
+
+ if (!flush) {
+ register unsigned char *out_buf_reg = &out_buffer[out_buffer_bytes];
+
+ for (int j = 0;j < blockwidth; j++) {
+
+ for(int i = 0;i < blockwidth; i++)
+ (*out_buf_reg++) = (*buf_reg++);
+
+ out_buffer_bytes += blockwidth;
+ buf_reg += width - blockwidth;
+ }
+ }
+
+ return bytes_written;
+}
+
+void *rmdCacheImageBuffer(ProgData *pdata) {
+
+ gzFile fp = NULL;
+ FILE *ucfp = NULL;
+ int index_entry_size = sizeof(u_int32_t),
+ blocknum_x = pdata->enc_data->yuv.y_width / Y_UNIT_WIDTH,
+ blocknum_y = pdata->enc_data->yuv.y_height / Y_UNIT_WIDTH,
+ firstrun = 1,
+ frameno = 0,
+ nbytes = 0,
+ nth_cache = 1;
+ u_int32_t ynum, unum, vnum,
+ y_short_blocks[blocknum_x * blocknum_y],
+ u_short_blocks[blocknum_x * blocknum_y],
+ v_short_blocks[blocknum_x * blocknum_y];
+ unsigned long long int total_bytes = 0;
+ unsigned long long int total_received_bytes = 0;
+ unsigned int capture_frameno = 0;
+
+ if (!pdata->args.zerocompression) {
+ fp = pdata->cache_data->ifp;
+
+ if (fp == NULL)
+ exit(13);
+ } else {
+ ucfp = pdata->cache_data->uncifp;
+
+ if (ucfp == NULL)
+ exit(13);
+ }
+
+ while (pdata->running) {
+ FrameHeader fheader;
+
+ ynum = unum = vnum = 0;
+
+ pthread_mutex_lock(&pdata->img_buff_ready_mutex);
+ while (pdata->running && capture_frameno >= pdata->capture_frameno)
+ pthread_cond_wait(&pdata->image_buffer_ready, &pdata->img_buff_ready_mutex);
+
+ capture_frameno = pdata->capture_frameno;
+ pthread_mutex_unlock(&pdata->img_buff_ready_mutex);
+
+ pthread_mutex_lock(&pdata->pause_mutex);
+ while (pdata->paused)
+ pthread_cond_wait(&pdata->pause_cond, &pdata->pause_mutex);
+ pthread_mutex_unlock(&pdata->pause_mutex);
+
+ pthread_mutex_lock(&pdata->yuv_mutex);
+
+ //find and flush different blocks
+ if (firstrun) {
+ firstrun = 0;
+ for(int j = 0; j < blocknum_x * blocknum_y; j++) {
+ yblocks[ynum] = 1;
+ y_short_blocks[ynum] = j;
+ ynum++;
+ ublocks[unum] = 1;
+ u_short_blocks[unum] = j;
+ unum++;
+ vblocks[vnum] = 1;
+ v_short_blocks[vnum] = j;
+ vnum++;
+ }
+ } else {
+ /**COMPRESS ARRAYS*/
+ for(int j = 0; j < blocknum_x * blocknum_y; j++) {
+ if (yblocks[j]) {
+ y_short_blocks[ynum] = j;
+ ynum++;
+ }
+ if (ublocks[j]) {
+ u_short_blocks[unum] = j;
+ unum++;
+ }
+ if (vblocks[j]) {
+ v_short_blocks[vnum] = j;
+ vnum++;
+ }
+ }
+ }
+
+ /**WRITE FRAME TO DISK*/
+ if (!pdata->args.zerocompression) {
+ if (ynum * 4 + unum + vnum > (blocknum_x * blocknum_y * 6) / 10)
+ gzsetparams(fp, 1, Z_FILTERED);
+ else
+ gzsetparams(fp, 0, Z_FILTERED);
+ }
+
+ strncpy(fheader.frame_prefix, "FRAM", 4);
+ fheader.frameno = ++frameno;
+ fheader.current_total = pdata->frames_total;
+
+ fheader.Ynum = ynum;
+ fheader.Unum = unum;
+ fheader.Vnum = vnum;
+
+ if (!pdata->args.zerocompression) {
+ nbytes += gzwrite(fp, (void*)&fheader, sizeof(FrameHeader));
+ //flush indexes
+ if (ynum)
+ nbytes += gzwrite(fp, (void*)y_short_blocks, ynum * index_entry_size);
+
+ if (unum)
+ nbytes += gzwrite(fp, (void*)u_short_blocks, unum * index_entry_size);
+
+ if (vnum)
+ nbytes += gzwrite(fp, (void*)v_short_blocks, vnum * index_entry_size);
+ } else {
+ nbytes += sizeof(FrameHeader)*
+ fwrite((void*)&fheader, sizeof(FrameHeader), 1, ucfp);
+ //flush indexes
+ if (ynum)
+ nbytes += index_entry_size * fwrite(y_short_blocks, index_entry_size, ynum, ucfp);
+
+ if (unum)
+ nbytes += index_entry_size * fwrite(u_short_blocks, index_entry_size, unum, ucfp);
+
+ if (vnum)
+ nbytes += index_entry_size * fwrite(v_short_blocks, index_entry_size, vnum, ucfp);
+ }
+
+ //flush the blocks for each buffer
+ if (ynum) {
+ for(int j = 0; j < ynum; j++)
+ nbytes += rmdFlushBlock(pdata->enc_data->yuv.y,
+ y_short_blocks[j],
+ pdata->enc_data->yuv.y_width,
+ pdata->enc_data->yuv.y_height,
+ Y_UNIT_WIDTH,
+ fp,
+ ucfp,
+ 0);
+ }
+
+ if (unum) {
+ for(int j = 0; j < unum; j++)
+ nbytes += rmdFlushBlock(pdata->enc_data->yuv.u,
+ u_short_blocks[j],
+ pdata->enc_data->yuv.uv_width,
+ pdata->enc_data->yuv.uv_height,
+ UV_UNIT_WIDTH,
+ fp,
+ ucfp,
+ 0);
+ }
+
+ if (vnum) {
+ for(int j = 0; j < vnum; j++)
+ nbytes += rmdFlushBlock(pdata->enc_data->yuv.v,
+ v_short_blocks[j],
+ pdata->enc_data->yuv.uv_width,
+ pdata->enc_data->yuv.uv_height,
+ UV_UNIT_WIDTH,
+ fp,
+ ucfp,
+ 0);
+ }
+
+ //release main buffer
+ pthread_mutex_unlock(&pdata->yuv_mutex);
+
+ nbytes += rmdFlushBlock(NULL, 0, 0, 0, 0, fp, ucfp, 1);
+ /**@________________@**/
+ pdata->avd += pdata->frametime;
+
+ if (nbytes > CACHE_FILE_SIZE_LIMIT) {
+ if (rmdSwapCacheFilesWrite(pdata->cache_data->imgdata, nth_cache, &fp, &ucfp)) {
+ fprintf(stderr, "New cache file could not be created.\n"
+ "Ending recording...\n");
+ fflush(stderr);
+ raise(SIGINT); //if for some reason we cannot make a new file
+ //we have to stop. If we are out of space,
+ //which means
+ //that encoding cannot happen either,
+ //InitEncoder will cause an abrupt end with an
+ //error code and the cache will remain intact.
+ //If we've chosen separate two-stages,
+ //the program will make a
+ //clean exit.
+ //In either case data will be preserved so if
+ //space is freed the recording
+ //can be proccessed later.
+ }
+ total_bytes += nbytes;
+ nth_cache++;
+ nbytes = 0;
+ }
+ }
+ total_bytes += nbytes;
+
+ {
+ unsigned int bytes_per_pixel = pdata->specs.depth >= 24 ? 4 : 2;
+ unsigned int pixels_per_frame = pdata->enc_data->yuv.y_width * pdata->enc_data->yuv.y_height;
+
+ total_received_bytes = ((unsigned long long int)frameno) * bytes_per_pixel * pixels_per_frame;
+ }
+
+ if (total_received_bytes) {
+ double percent_of_data_left = (total_bytes / (double)total_received_bytes) * 100;
+
+ fprintf(stderr, "\n"
+ "*********************************************\n"
+ "\n"
+ "Cached %llu MB, from %llu MB that were received.\n"
+ "Average cache compression ratio: %.1f %%\n"
+ "\n"
+ "*********************************************\n",
+ total_bytes / BYTES_PER_MB,
+ total_received_bytes / BYTES_PER_MB,
+ 100 - percent_of_data_left);
+
+ }
+
+ fprintf(stderr, "Saved %d frames in a total of %d requests\n",
+ frameno,
+ pdata->frames_total);
+ fflush(stderr);
+
+ if (!pdata->args.zerocompression) {
+ gzflush(fp, Z_FINISH);
+ gzclose(fp);
+ } else {
+ fflush(ucfp);
+ fclose(ucfp);
+ }
+
+ pthread_exit(&errno);
+}
diff --git a/src/rmd_cache_frame.h b/src/rmd_cache_frame.h
new file mode 100644
index 0000000..9361e35
--- /dev/null
+++ b/src/rmd_cache_frame.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef CACHE_FRAME_H
+#define CACHE_FRAME_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Image caching thread. Copies the yuv buffer, compares with the last one and
+* caches the result.
+*
+* \param pdata ProgData struct containing all program data
+*
+*/
+void *rmdCacheImageBuffer(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_capture_sound.c b/src/rmd_capture_sound.c
new file mode 100644
index 0000000..2719c60
--- /dev/null
+++ b/src/rmd_capture_sound.c
@@ -0,0 +1,165 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_capture_sound.h"
+
+#include "rmd_jack.h"
+#include "rmd_opendev.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+void *rmdCaptureSound(ProgData *pdata) {
+
+#ifdef HAVE_LIBASOUND
+ int frames = pdata->periodsize;
+#endif
+ //start capturing only after first frame is taken
+ usleep(pdata->frametime);
+
+ while (pdata->running) {
+ int sret = 0;
+ SndBuffer *newbuf, *tmp;
+
+ if (pdata->paused) {
+#ifdef HAVE_LIBASOUND
+ if (!pdata->hard_pause) {
+ snd_pcm_pause(pdata->sound_handle, 1);
+ pthread_mutex_lock(&pdata->pause_mutex);
+ pthread_cond_wait(&pdata->pause_cond, &pdata->pause_mutex);
+ pthread_mutex_unlock(&pdata->pause_mutex);
+ snd_pcm_pause(pdata->sound_handle, 0);
+ } else {//device doesn't support pause(is this the norm?mine doesn't)
+ snd_pcm_close(pdata->sound_handle);
+ pthread_mutex_lock(&pdata->pause_mutex);
+ pthread_cond_wait(&pdata->pause_cond, &pdata->pause_mutex);
+ pthread_mutex_unlock(&pdata->pause_mutex);
+ pdata->sound_handle =
+ rmdOpenDev(pdata->args.device,
+ &pdata->args.channels,
+ &pdata->args.frequency,
+ &pdata->args.buffsize,
+ NULL,
+ NULL,
+ NULL//let's hope that the device capabilities
+ //didn't magically change
+ );
+ if (pdata->sound_handle == NULL) {
+ fprintf(stderr, "Couldn't reopen sound device.Exiting\n");
+ pdata->running = FALSE;
+ errno = 3;
+ pthread_exit(&errno);
+ }
+ }
+#else
+ close(pdata->sound_handle);
+ pthread_mutex_lock(&pdata->pause_mutex);
+ pthread_cond_wait(&pdata->pause_cond, &pdata->pause_mutex);
+ pthread_mutex_unlock(&pdata->pause_mutex);
+ pdata->sound_handle = rmdOpenDev( pdata->args.device,
+ pdata->args.channels,
+ pdata->args.frequency);
+ if (pdata->sound_handle < 0) {
+ fprintf(stderr, "Couldn't reopen sound device. Exiting\n");
+ pdata->running = FALSE;
+ errno = 3;
+ pthread_exit(&errno);
+ }
+#endif
+ }
+
+ //create new buffer
+ newbuf = (SndBuffer *)malloc(sizeof(SndBuffer));
+#ifdef HAVE_LIBASOUND
+ newbuf->data = (signed char *)malloc(frames * pdata->sound_framesize);
+#else
+ newbuf->data = (signed char *)malloc((pdata->args.buffsize << 1) * pdata->args.channels);
+#endif
+ newbuf->next = NULL;
+
+ //read data into new buffer
+#ifdef HAVE_LIBASOUND
+ while (sret<frames) {
+ int temp_sret = snd_pcm_readi( pdata->sound_handle,
+ newbuf->data + pdata->sound_framesize * sret,
+ frames-sret);
+ if (temp_sret == -EPIPE) {
+ fprintf(stderr, "%s: Overrun occurred.\n",
+ snd_strerror(temp_sret));
+ snd_pcm_prepare(pdata->sound_handle);
+ } else if (temp_sret < 0) {
+ fprintf(stderr, "An error occured while reading sound data:\n"
+ " %s\n",
+ snd_strerror(temp_sret));
+ snd_pcm_prepare(pdata->sound_handle);
+ } else
+ sret += temp_sret;
+ }
+#else
+ sret = 0;
+ //oss recording loop
+ do {
+ int temp_sret = read( pdata->sound_handle,
+ &newbuf->data[sret],
+ (pdata->args.buffsize << 1) *
+ pdata->args.channels)-sret;
+ if (temp_sret < 0) {
+ fprintf(stderr, "An error occured while reading from soundcard"
+ "%s\nError description:\n%s\n",
+ pdata->args.device, strerror(errno));
+ } else
+ sret += temp_sret;
+ } while (sret < (pdata->args.buffsize << 1) * pdata->args.channels);
+#endif
+ //queue the new buffer
+ pthread_mutex_lock(&pdata->sound_buffer_mutex);
+ tmp = pdata->sound_buffer;
+ if (!tmp)
+ pdata->sound_buffer = newbuf;
+ else {
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+
+ tmp->next = newbuf;
+ }
+ pthread_cond_signal(&pdata->sound_data_read);
+ pthread_mutex_unlock(&pdata->sound_buffer_mutex);
+ }
+#ifdef HAVE_LIBASOUND
+ snd_pcm_close(pdata->sound_handle);
+#else
+ close(pdata->sound_handle);
+#endif
+ pthread_exit(&errno);
+}
diff --git a/src/rmd_capture_sound.h b/src/rmd_capture_sound.h
new file mode 100644
index 0000000..b403fa8
--- /dev/null
+++ b/src/rmd_capture_sound.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef CAPTURE_SOUND_H
+#define CAPTURE_SOUND_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Sound capturing thread. Data are placed on a
+* list to be picked up by other threads.
+*
+* \param pdata ProgData struct containing all program data
+*/
+void *rmdCaptureSound(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_encode_cache.c b/src/rmd_encode_cache.c
new file mode 100644
index 0000000..1e0727c
--- /dev/null
+++ b/src/rmd_encode_cache.c
@@ -0,0 +1,72 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_encode_cache.h"
+
+#include "rmd_flush_to_ogg.h"
+#include "rmd_init_encoder.h"
+#include "rmd_load_cache.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+void rmdEncodeCache(ProgData *pdata){
+ pthread_t flush_to_ogg_t, load_cache_t;
+
+ fprintf(stderr, "STATE:ENCODING\n");
+ fprintf(stderr, "Encoding started!\nThis may take several minutes.\n"
+ "Pressing Ctrl-C will cancel the procedure"
+ " (resuming will not be possible, but\n"
+ "any portion of the video, which is already encoded won't be deleted).\n"
+ "Please wait...\n");
+
+ pdata->running = TRUE;
+ rmdInitEncoder(pdata, pdata->enc_data, 1);
+ //load encoding and flushing threads
+ if (!pdata->args.nosound) {
+ //before we start loading again
+ //we need to free any left-overs
+ while (pdata->sound_buffer != NULL) {
+ free(pdata->sound_buffer->data);
+ pdata->sound_buffer = pdata->sound_buffer->next;
+ }
+ }
+ pthread_create(&flush_to_ogg_t, NULL, (void *)rmdFlushToOgg, (void *)pdata);
+
+ //start loading image and audio
+ pthread_create(&load_cache_t, NULL, (void *)rmdLoadCache, (void *)pdata);
+
+ //join and finish
+ pthread_join(load_cache_t, NULL);
+ fprintf(stderr, "Encoding finished!\nWait a moment please...\n");
+ pthread_join(flush_to_ogg_t, NULL);
+}
diff --git a/src/rmd_encode_cache.h b/src/rmd_encode_cache.h
new file mode 100644
index 0000000..4c37f97
--- /dev/null
+++ b/src/rmd_encode_cache.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef ENCODE_CACHE_H
+#define ENCODE_CACHE_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Encode cache into an
+* ogg stream.
+* \param pdata ProgData struct containing all program data
+*/
+void rmdEncodeCache(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_encode_image_buffer.c b/src/rmd_encode_image_buffer.c
new file mode 100644
index 0000000..c234aa4
--- /dev/null
+++ b/src/rmd_encode_image_buffer.c
@@ -0,0 +1,99 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_encode_image_buffer.h"
+
+#include "rmd_types.h"
+
+#include <errno.h>
+
+
+void *rmdEncodeImageBuffer(ProgData *pdata) {
+ unsigned int encode_frameno = 0;
+
+ pdata->th_encoding_clean = 0;
+
+ while (pdata->running) {
+ EncData *enc_data = pdata->enc_data;
+
+ pthread_mutex_lock(&pdata->img_buff_ready_mutex);
+ while (pdata->running && encode_frameno >= pdata->capture_frameno)
+ pthread_cond_wait(&pdata->image_buffer_ready, &pdata->img_buff_ready_mutex);
+ encode_frameno = pdata->capture_frameno;
+ pthread_mutex_unlock(&pdata->img_buff_ready_mutex);
+
+ pthread_mutex_lock(&pdata->pause_mutex);
+ while (pdata->paused)
+ pthread_cond_wait(&pdata->pause_cond, &pdata->pause_mutex);
+ pthread_mutex_unlock(&pdata->pause_mutex);
+
+ pthread_mutex_lock(&pdata->yuv_mutex);
+ if (theora_encode_YUVin(&enc_data->m_th_st, &enc_data->yuv)) {
+ fprintf(stderr, "Encoder not ready!\n");
+ pthread_mutex_unlock(&pdata->yuv_mutex);
+ } else {
+ pthread_mutex_unlock(&pdata->yuv_mutex);
+
+ if (theora_encode_packetout(&enc_data->m_th_st, 0, &enc_data->m_ogg_pckt1) == 1) {
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ ogg_stream_packetin(&enc_data->m_ogg_ts, &enc_data->m_ogg_pckt1);
+ pdata->avd += pdata->frametime;
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+ }
+ }
+ }
+
+ //last packet
+ pthread_mutex_lock(&pdata->theora_lib_mutex);
+ pdata->th_encoding_clean = 1;
+ pthread_cond_signal(&pdata->theora_lib_clean);
+ pthread_mutex_unlock(&pdata->theora_lib_mutex);
+
+ pthread_exit(&errno);
+}
+
+//this function is meant to be called normally
+//not through a thread of it's own
+void rmdSyncEncodeImageBuffer(ProgData *pdata) {
+ EncData *enc_data = pdata->enc_data;
+
+ if (theora_encode_YUVin(&enc_data->m_th_st, &enc_data->yuv)) {
+ fprintf(stderr, "Encoder not ready!\n");
+ return;
+ }
+
+ if (theora_encode_packetout(&enc_data->m_th_st, !pdata->running, &enc_data->m_ogg_pckt1) == 1) {
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ ogg_stream_packetin(&enc_data->m_ogg_ts, &enc_data->m_ogg_pckt1);
+
+ if (!pdata->running)
+ enc_data->m_ogg_ts.e_o_s = 1;
+
+ pdata->avd += pdata->frametime;
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+ }
+}
diff --git a/src/rmd_encode_image_buffer.h b/src/rmd_encode_image_buffer.h
new file mode 100644
index 0000000..e5ca3db
--- /dev/null
+++ b/src/rmd_encode_image_buffer.h
@@ -0,0 +1,50 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef ENCODE_IMAGE_BUFFER_H
+#define ENCODE_IMAGE_BUFFER_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* feed a yuv buffer to the theora encoder and submit outcome to
+* the ogg stream.
+* \param pdata ProgData struct containing all program data
+*/
+void *rmdEncodeImageBuffer(ProgData *pdata);
+
+/**
+* As rmdEncodeImageBuffer, only with the assumption that
+* this is not a thread on it's own
+*
+* \param pdata ProgData struct containing all program data
+*
+*/
+void rmdSyncEncodeImageBuffer(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_encode_sound_buffer.c b/src/rmd_encode_sound_buffer.c
new file mode 100644
index 0000000..5fa1dc6
--- /dev/null
+++ b/src/rmd_encode_sound_buffer.c
@@ -0,0 +1,178 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_encode_sound_buffer.h"
+
+#include "rmd_jack.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+
+void *rmdEncodeSoundBuffer(ProgData *pdata) {
+ int sampread = pdata->periodsize;
+#ifdef HAVE_LIBJACK
+ void *jackbuf = NULL;
+
+ if (pdata->args.use_jack)
+ jackbuf = malloc(pdata->sound_framesize * pdata->jdata->buffersize);
+#endif
+
+ pdata->v_encoding_clean = 0;
+ while (pdata->running) {
+ float **vorbis_buffer;
+ SndBuffer *buff = NULL;
+
+ pthread_mutex_lock(&pdata->pause_mutex);
+ while (pdata->paused)
+ pthread_cond_wait(&pdata->pause_cond, &pdata->pause_mutex);
+ pthread_mutex_unlock(&pdata->pause_mutex);
+
+ if (!pdata->args.use_jack) {
+ pthread_mutex_lock(&pdata->sound_buffer_mutex);
+ while (!pdata->sound_buffer && pdata->running)
+ pthread_cond_wait(&pdata->sound_data_read, &pdata->sound_buffer_mutex);
+
+ buff = pdata->sound_buffer;
+ if (buff)
+ pdata->sound_buffer = buff->next;
+ pthread_mutex_unlock(&pdata->sound_buffer_mutex);
+
+ if (!pdata->running)
+ break;
+
+ vorbis_buffer = vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp, sampread);
+
+ for (int i = 0, count = 0; i < sampread; i++) {
+ for (int j = 0; j < pdata->args.channels; j++) {
+ vorbis_buffer[j][i] = ((buff->data[count + 1] << 8) |
+ (0x00ff & (int)buff->data[count])) /
+ 32768.f;
+ count += 2;
+ }
+ }
+ free(buff->data);
+ free(buff);
+ } else {
+#ifdef HAVE_LIBJACK
+
+ pthread_mutex_lock(&pdata->sound_buffer_mutex);
+ while ( pdata->running &&
+ jack_ringbuffer_read_space(pdata->jdata->sound_buffer) <
+ pdata->sound_framesize * pdata->jdata->buffersize)
+ pthread_cond_wait(&pdata->sound_data_read, &pdata->sound_buffer_mutex);
+
+ if (pdata->running)
+ jack_ringbuffer_read( pdata->jdata->sound_buffer,
+ jackbuf,
+ (pdata->sound_framesize *
+ pdata->jdata->buffersize)
+ );
+
+ pthread_mutex_unlock(&pdata->sound_buffer_mutex);
+
+ if (!pdata->running)
+ break;
+
+ vorbis_buffer = vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp, sampread);
+
+ for (int j = 0, count = 0; j < pdata->args.channels; j++) {
+ for (int i = 0; i < sampread; i++) {
+ vorbis_buffer[j][i] = ((float*)jackbuf)[count];
+ count++;
+ }
+ }
+#endif
+ }
+ vorbis_analysis_wrote(&pdata->enc_data->m_vo_dsp, sampread);
+
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ while (vorbis_analysis_blockout(&pdata->enc_data->m_vo_dsp, &pdata->enc_data->m_vo_block) == 1) {
+
+ vorbis_analysis(&pdata->enc_data->m_vo_block, NULL);
+ vorbis_bitrate_addblock(&pdata->enc_data->m_vo_block);
+
+ while (vorbis_bitrate_flushpacket(&pdata->enc_data->m_vo_dsp, &pdata->enc_data->m_ogg_pckt2)) {
+ ogg_stream_packetin(&pdata->enc_data->m_ogg_vs, &pdata->enc_data->m_ogg_pckt2);
+ }
+ }
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+
+ /* this needs synchronizing */
+ pdata->avd -= pdata->periodtime;
+ }
+
+ pthread_mutex_lock(&pdata->vorbis_lib_mutex);
+ pdata->v_encoding_clean = 1;
+ pthread_cond_signal(&pdata->vorbis_lib_clean);
+ pthread_mutex_unlock(&pdata->vorbis_lib_mutex);
+ pthread_exit(&errno);
+}
+
+void rmdSyncEncodeSoundBuffer(ProgData *pdata, signed char *buff) {
+ int sampread = (buff != NULL) ? pdata->periodsize : 0;
+ float **vorbis_buffer = vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp, sampread);
+
+ if (!pdata->args.use_jack) {
+ for (int i = 0, count = 0; i < sampread; i++) {
+ for (int j = 0; j < pdata->args.channels; j++) {
+ vorbis_buffer[j][i] = ((buff[count + 1] << 8) |
+ (0x00ff & (int)buff[count]))
+ / 32768.f;
+ count += 2;
+ }
+ }
+ } else {
+ for (int j = 0, count = 0; j < pdata->args.channels; j++) {
+ for (int i = 0; i < sampread; i++) {
+ vorbis_buffer[j][i] = ((float *)buff)[count];
+ count++;
+ }
+ }
+ }
+
+ vorbis_analysis_wrote(&pdata->enc_data->m_vo_dsp, sampread);
+
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ while (vorbis_analysis_blockout(&pdata->enc_data->m_vo_dsp, &pdata->enc_data->m_vo_block) == 1) {
+
+ vorbis_analysis(&pdata->enc_data->m_vo_block, NULL);
+ vorbis_bitrate_addblock(&pdata->enc_data->m_vo_block);
+
+ while (vorbis_bitrate_flushpacket(&pdata->enc_data->m_vo_dsp, &pdata->enc_data->m_ogg_pckt2))
+ ogg_stream_packetin(&pdata->enc_data->m_ogg_vs, &pdata->enc_data->m_ogg_pckt2);
+ }
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+
+ if (!pdata->running)
+ pdata->enc_data->m_ogg_vs.e_o_s = 1;
+
+ pdata->avd -= pdata->periodtime;
+}
diff --git a/src/rmd_encode_sound_buffer.h b/src/rmd_encode_sound_buffer.h
new file mode 100644
index 0000000..7d5c312
--- /dev/null
+++ b/src/rmd_encode_sound_buffer.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef ENCODE_SOUND_BUFFER_H
+#define ENCODE_SOUND_BUFFER_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Sound encoding thread. Picks up data from the buffer queue ,
+* encodes and places them on the vorbis stream.
+*
+* \param pdata ProgData struct containing all program data
+*/
+void *rmdEncodeSoundBuffer(ProgData *pdata);
+
+/**
+* As rmdEncodeSoundBuffer, only with the assumption that
+* this is not a thread on it's own
+*
+* \param pdata ProgData struct containing all program data
+*
+*/
+void rmdSyncEncodeSoundBuffer(ProgData *pdata,signed char *buff);
+
+
+#endif
diff --git a/src/rmd_error.c b/src/rmd_error.c
new file mode 100644
index 0000000..ba95198
--- /dev/null
+++ b/src/rmd_error.c
@@ -0,0 +1,59 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_error.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+int rmdErrorHandler(Display *dpy, XErrorEvent *e)
+{
+ char error_desc[1024];
+
+ XGetErrorText(dpy, e->error_code, error_desc, sizeof(error_desc));
+ fprintf(stderr, "X Error: %s\n", error_desc);
+ fflush(stderr);
+
+ if ((e->error_code==BadWindow)&&(e->request_code==X_GetWindowAttributes)) {
+ fprintf(stderr, "BadWindow on XGetWindowAttributes.\nIgnoring...\n");
+ fflush(stderr);
+ return 0;
+ }
+
+ if ((e->error_code==BadAccess)&&(e->request_code==X_GrabKey)) {
+ fprintf(stderr, "Bad Access on XGrabKey.\n" "Shortcut already assigned.\n");
+ fflush(stderr);
+ return 0;
+ }
+
+ exit(1);
+}
diff --git a/src/rmd_error.h b/src/rmd_error.h
new file mode 100644
index 0000000..4d27264
--- /dev/null
+++ b/src/rmd_error.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_ERROR_H
+#define RMD_ERROR_H 1
+
+#include "rmd_types.h"
+
+
+/*
+ * Handling of X errors.
+ * Ignores, bad access when registering shortcuts
+ * and BadWindow on XQueryTree
+ *
+ * \param dpy Connection to the X Server
+ *
+ * \param e XErrorEvent struct containing error info
+ *
+ * \returns 0 on the two ignored cases, calls exit(1)
+ * otherwise.
+ *
+ */
+int rmdErrorHandler(Display *dpy,XErrorEvent *e);
+
+
+#endif
diff --git a/src/rmd_flush_to_ogg.c b/src/rmd_flush_to_ogg.c
new file mode 100644
index 0000000..87308a6
--- /dev/null
+++ b/src/rmd_flush_to_ogg.c
@@ -0,0 +1,235 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_flush_to_ogg.h"
+
+#include "rmd_encode_image_buffer.h"
+#include "rmd_encode_sound_buffer.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+
+
+//we copy the page because the next call to ogg_stream_pageout
+//will invalidate it. But we must have pages from
+//both streams at every time in
+//order to do correct multiplexing
+static void page_copy(ogg_page *new, ogg_page *old) {
+
+ new->header_len = old->header_len;
+ new->header = malloc(new->header_len);
+ new->body_len = old->body_len;
+ new->body = malloc(new->body_len);
+
+ memcpy(new->header, old->header, new->header_len);
+ memcpy(new->body, old->body, new->body_len);
+}
+
+//free our copy
+static void page_free(ogg_page *pg) {
+ pg->header_len = pg->body_len = 0;
+ free(pg->header);
+ free(pg->body);
+}
+
+void *rmdFlushToOgg(ProgData *pdata) {
+ int videoflag = 0, audioflag = 0;
+ double video_bytesout = 0, audio_bytesout = 0;
+ ogg_page videopage, //owned by libogg
+ videopage_copy, //owned by the application
+ audiopage, //owned by libogg
+ audiopage_copy; //owned by the application
+
+ double audiotime = 0;
+ double videotime = 0;
+ int th_st_fin = 0,
+ v_st_fin = (pdata->args.nosound);
+
+ while (!(th_st_fin && v_st_fin)) {
+ int audio_or_video = 0;
+
+ if (pdata->running) {
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ if (!videoflag) {
+ videoflag = ogg_stream_pageout(&pdata->enc_data->m_ogg_ts, &videopage);
+ videotime = videoflag ? theora_granule_time(
+ &pdata->enc_data->m_th_st,
+ ogg_page_granulepos(&videopage)
+ ) : -1;
+
+ if (videoflag)
+ page_copy(&videopage_copy, &videopage);
+ }
+
+ if (!pdata->args.nosound && !audioflag) {
+ audioflag = ogg_stream_pageout(&pdata->enc_data->m_ogg_vs, &audiopage);
+ audiotime = audioflag ? vorbis_granule_time(
+ &pdata->enc_data->m_vo_dsp,
+ ogg_page_granulepos(&audiopage)
+ ) : -1;
+
+ if (audioflag)
+ page_copy(&audiopage_copy, &audiopage);
+ }
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+ } else {
+ if (!th_st_fin && !videoflag) {
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ videoflag = ogg_stream_flush(&pdata->enc_data->m_ogg_ts, &videopage);
+ videotime = videoflag ? theora_granule_time(
+ &pdata->enc_data->m_th_st,
+ ogg_page_granulepos(&videopage)
+ ) : -1;
+
+ if (videoflag)
+ page_copy(&videopage_copy, &videopage);
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+
+ //we need the last page to properly close the stream
+ if (!videoflag) {
+ pthread_mutex_lock(&pdata->theora_lib_mutex);
+ while (!pdata->th_encoding_clean)
+ pthread_cond_wait(&pdata->theora_lib_clean, &pdata->theora_lib_mutex);
+ pthread_mutex_unlock(&pdata->theora_lib_mutex);
+ rmdSyncEncodeImageBuffer(pdata);
+ }
+ }
+
+ if (!pdata->args.nosound && !v_st_fin && !audioflag) {
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ audioflag = ogg_stream_flush(&pdata->enc_data->m_ogg_vs, &audiopage);
+ audiotime = audioflag ? vorbis_granule_time(
+ &pdata->enc_data->m_vo_dsp,
+ ogg_page_granulepos(&audiopage)
+ ) : -1;
+
+ if (audioflag)
+ page_copy(&audiopage_copy, &audiopage);
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+
+ //we need the last page to properly close the stream
+ if (!audioflag) {
+ pthread_mutex_lock(&pdata->vorbis_lib_mutex);
+ while (!pdata->v_encoding_clean)
+ pthread_cond_wait(&pdata->vorbis_lib_clean, &pdata->vorbis_lib_mutex);
+ pthread_mutex_unlock(&pdata->vorbis_lib_mutex);
+ rmdSyncEncodeSoundBuffer(pdata, NULL);
+ }
+ }
+ }
+
+#if 0
+ /* I don't understand what this is about, if these are finished we want to lose
+ * their pages?
+ */
+ if (th_st_fin)
+ videoflag=0;
+
+ if (v_st_fin)
+ audioflag=0;
+#endif
+ if ((!audioflag && !v_st_fin && !pdata->args.nosound) || (!videoflag && !th_st_fin)) {
+ usleep(10000);
+ continue;
+ }
+
+ if (!audioflag) {
+ audio_or_video = 1;
+ } else if (!videoflag) {
+ audio_or_video = 0;
+ } else {
+ if (audiotime < videotime)
+ audio_or_video = 0;
+ else
+ audio_or_video = 1;
+ }
+
+ if (audio_or_video == 1) {
+ video_bytesout += fwrite( videopage_copy.header, 1,
+ videopage_copy.header_len,
+ pdata->enc_data->fp);
+
+ video_bytesout += fwrite( videopage_copy.body, 1,
+ videopage_copy.body_len,
+ pdata->enc_data->fp);
+ videoflag = 0;
+
+ if (!pdata->running) {
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ if (ogg_page_eos(&videopage_copy))
+ th_st_fin = 1;
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+ }
+
+ page_free(&videopage_copy);
+ } else {
+ audio_bytesout += fwrite( audiopage_copy.header, 1,
+ audiopage_copy.header_len,
+ pdata->enc_data->fp);
+
+ audio_bytesout += fwrite( audiopage_copy.body, 1,
+ audiopage_copy.body_len,
+ pdata->enc_data->fp);
+ audioflag = 0;
+
+ if (!pdata->running) {
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ if (ogg_page_eos(&audiopage_copy))
+ v_st_fin = 1;
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+ }
+
+ page_free(&audiopage_copy);
+ }
+ }
+
+ pthread_mutex_lock(&pdata->libogg_mutex);
+ ogg_stream_clear(&pdata->enc_data->m_ogg_ts);
+
+ if (!pdata->args.nosound)
+ ogg_stream_clear(&pdata->enc_data->m_ogg_vs);
+
+ pthread_mutex_unlock(&pdata->libogg_mutex);
+
+ theora_clear(&pdata->enc_data->m_th_st);
+
+ if (pdata->enc_data->fp)
+ fclose(pdata->enc_data->fp);
+
+ fprintf(stderr, "\r \nDone.\nWritten %.0f bytes\n"
+ "(%.0f of which were video data and %.0f audio data)\n\n",
+ video_bytesout + audio_bytesout,
+ video_bytesout, audio_bytesout);
+
+ pthread_exit(&errno);
+}
diff --git a/src/rmd_flush_to_ogg.h b/src/rmd_flush_to_ogg.h
new file mode 100644
index 0000000..64094a8
--- /dev/null
+++ b/src/rmd_flush_to_ogg.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef FLUSH_TO_OGG_H
+#define FLUSH_TO_OGG_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Query theora and vorbis streams for ready packages and
+* flush them on the disk
+* \param pdata ProgData struct containing all program data
+*/
+void *rmdFlushToOgg(ProgData *pdata);
+
+#endif
diff --git a/src/rmd_frame.c b/src/rmd_frame.c
new file mode 100644
index 0000000..ee5c4d8
--- /dev/null
+++ b/src/rmd_frame.c
@@ -0,0 +1,142 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_frame.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/shape.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#define BORDER_WIDTH 6
+#define OUTLINE_WIDTH 1
+
+
+void rmdDrawFrame( Display *dpy,
+ int screen,
+ Window win,
+ int width,
+ int height) {
+
+ GC gc;
+ XGCValues gcv;
+ XColor white, white_e, black, black_e;
+ unsigned long gcmask = GCForeground;
+
+ XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "white", &white, &white_e);
+ XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "black", &black, &black_e);
+
+ gcv.foreground = black.pixel;
+ gc = XCreateGC(dpy, win, gcmask, &gcv);
+ XFillRectangle( dpy,
+ win,
+ gc,
+ OUTLINE_WIDTH,
+ OUTLINE_WIDTH,
+ width + (BORDER_WIDTH-OUTLINE_WIDTH) * 2,
+ height + (BORDER_WIDTH-OUTLINE_WIDTH) * 2);
+ gcv.foreground = white.pixel;
+ XChangeGC(dpy, gc, gcmask, &gcv);
+ XFillRectangle( dpy,
+ win,
+ gc,
+ BORDER_WIDTH-OUTLINE_WIDTH,
+ BORDER_WIDTH-OUTLINE_WIDTH,
+ width + OUTLINE_WIDTH * 2,
+ height + OUTLINE_WIDTH * 2);
+
+ XFreeGC(dpy, gc);
+
+}
+
+void rmdMoveFrame( Display *dpy,
+ Window win,
+ int x,
+ int y) {
+
+ XMoveWindow(dpy, win, x-BORDER_WIDTH, y-BORDER_WIDTH);
+// XSync(pdata->dpy, False);
+}
+
+Window rmdFrameInit( Display *dpy,
+ int screen,
+ Window root,
+ int x,
+ int y,
+ int width,
+ int height) {
+
+ XSetWindowAttributes attribs;
+ XColor white, white_e;
+ Window win;
+ unsigned long valuemask = CWBackPixmap|CWBackPixel|
+ CWSaveUnder|CWOverrideRedirect|CWColormap;
+
+ XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "white", &white, &white_e);
+
+ attribs.background_pixmap = None;
+ attribs.background_pixel = white.pixel;
+ attribs.save_under = True;
+ attribs.override_redirect = True;
+ attribs.colormap = DefaultColormap(dpy, screen);
+
+ win = XCreateWindow( dpy,
+ root,
+ x - BORDER_WIDTH,
+ y - BORDER_WIDTH,
+ width + BORDER_WIDTH * 2,
+ height + BORDER_WIDTH * 2,
+ 0,
+ CopyFromParent,
+ InputOutput,
+ CopyFromParent,
+ valuemask,
+ &attribs);
+
+ XRectangle rect;
+ rect.x = rect.y = BORDER_WIDTH;
+ rect.width = width;
+ rect.height = height;
+
+ XShapeCombineRectangles( dpy,
+ win,
+ ShapeBounding,
+ 0,
+ 0,
+ &rect,
+ 1,
+ ShapeSubtract,
+ 0);
+
+ XMapWindow(dpy, win);
+
+ rmdDrawFrame(dpy, screen, win, width, height);
+
+ return win;
+}
diff --git a/src/rmd_frame.h b/src/rmd_frame.h
new file mode 100644
index 0000000..c3969f8
--- /dev/null
+++ b/src/rmd_frame.h
@@ -0,0 +1,101 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_FRAME_H
+#define RMD_FRAME_H 1
+
+#include "rmd_types.h"
+
+
+/*
+ * Create a frame that marks the recording area.
+ *
+ * \param dpy Connection to the X Server
+ *
+ * \param screen Recorded screen
+ *
+ * \param root Root window of the display
+ *
+ * \param x X pos of the recorded area
+ *
+ * \param y Y pos of the recorded area
+ *
+ * \param width Width of the recorded area
+ *
+ * \param height Height of the recorded area
+ *
+ * \returns The WindowID of the frame
+ *
+ */
+Window rmdFrameInit(Display *dpy,
+ int screen,
+ Window root,
+ int x,
+ int y,
+ int width,
+ int height);
+
+
+/*
+ * Move the frame (subtracts the borderwidth)
+ *
+ * \param dpy Connection to the X Server
+ *
+ * \param win WindowId of the frame
+ *
+ * \param x New X pos of the recorded area
+ *
+ * \param y New Y pos of the recorded area
+ *
+ */
+void rmdMoveFrame(Display *dpy,
+ Window win,
+ int x,
+ int y);
+
+
+/*
+ * Redraw the frame that marks the recording area.
+ *
+ * \param dpy Connection to the X Server
+ *
+ * \param screen Recorded screen
+ *
+ * \param win WindoID of the frame
+ *
+ * \param width Width of the recorded area
+ *
+ * \param height Height of the recorded area
+ *
+ */
+void rmdDrawFrame(Display *dpy,
+ int screen,
+ Window win,
+ int width,
+ int height);
+
+
+#endif
diff --git a/src/rmd_get_frame.c b/src/rmd_get_frame.c
new file mode 100644
index 0000000..a9e4daa
--- /dev/null
+++ b/src/rmd_get_frame.c
@@ -0,0 +1,593 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_get_frame.h"
+
+#include "rmd_cache.h"
+#include "rmd_frame.h"
+#include "rmd_getzpixmap.h"
+#include "rmd_poll_events.h"
+#include "rmd_rectinsert.h"
+#include "rmd_update_image.h"
+#include "rmd_yuv_utils.h"
+#include "rmd_types.h"
+
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/XShm.h>
+
+#include <limits.h>
+#include <pthread.h>
+#include <sys/shm.h>
+#include <errno.h>
+#include <stdlib.h>
+
+
+/* clip_event_area dejavu */
+static void clip_dummy_pointer_area(XRectangle *area, XRectangle *clip, XRectangle *res)
+{
+ res->x = (((area->x + area->width >= clip->x) &&
+ (area->x <= clip->x + clip->width)) ?
+ ((area->x <= clip->x) ? clip->x : area->x) : -1);
+
+ res->y = (((area->y + area->height >= clip->y) &&
+ (area->y <= clip->y + clip->height)) ?
+ ((area->y <= clip->y) ? clip->y : area->y) : -1);
+
+ res->width = (area->x <= clip->x) ? area->width - (clip->x - area->x) :
+ (area->x <= clip->x + clip->width) ?
+ (clip->width - area->x + clip->x < area->width) ?
+ clip->width - area->x + clip->x : area->width : 0;
+
+ res->height = (area->y <= clip->y) ? area->height - (clip->y - area->y) :
+ (area->y <= clip->y + clip->height) ?
+ (clip->height - area->y + clip->y < area->height) ?
+ clip->height - area->y + clip->y : area->height : 0;
+
+ if (res->x + res->width > clip->x + clip->width)
+ res->width = clip->x + clip->width - res->x;
+
+ if (res->y + res->height > clip->y + clip->height)
+ res->height = clip->y + clip->height - res->y;
+}
+
+#define MARK_BUFFER_AREA_C( data, \
+ x_tm, \
+ y_tm, \
+ width_tm, \
+ height_tm, \
+ buffer_width, \
+ __depth__) { \
+ \
+ register u_int##__depth__##_t *datapi = \
+ ((u_int##__depth__##_t *)data) + y_tm * buffer_width + x_tm; \
+ \
+ for(int k = 0; k < height_tm; k++) { \
+ for(int i = 0; i < width_tm; i++) { \
+ *datapi += 1; \
+ datapi++; \
+ } \
+ datapi += buffer_width - width_tm; \
+ } \
+}
+
+static void mark_buffer_area( unsigned char *data,
+ int x_tm, int y_tm,
+ int width_tm, int height_tm,
+ int buffer_width, int depth) {
+
+ if ((depth == 24) || (depth == 32)) {
+ MARK_BUFFER_AREA_C( data,
+ x_tm,
+ y_tm,
+ width_tm,
+ height_tm,
+ buffer_width,
+ 32);
+ } else {
+ MARK_BUFFER_AREA_C( data,
+ x_tm,
+ y_tm,
+ width_tm,
+ height_tm,
+ buffer_width,
+ 16);
+ }
+}
+
+//besides taking the first screenshot, this functions primary purpose is to
+//initialize the structures and memory.
+static int rmdFirstFrame(ProgData *pdata, Image *image) {
+ const XRectangle *rrect = &pdata->brwin.rrect;
+
+ if (pdata->args.noshared) {
+ image->ximage = XGetImage( pdata->dpy,
+ pdata->specs.root,
+ rrect->x,
+ rrect->y,
+ rrect->width,
+ rrect->height,
+ AllPlanes,
+ ZPixmap);
+ } else {
+ image->ximage = XShmCreateImage(pdata->dpy,
+ pdata->specs.visual,
+ pdata->specs.depth,
+ ZPixmap,
+ NULL,
+ &image->shm_info,
+ rrect->width,
+ rrect->height);
+
+ image->shm_info.shmid = shmget( IPC_PRIVATE,
+ image->ximage->bytes_per_line *
+ image->ximage->height,
+ IPC_CREAT|0777);
+
+ if (image->shm_info.shmid == -1) {
+ fprintf(stderr, "Failed to obtain Shared Memory segment!\n");
+ return 12;
+ }
+
+ image->shm_info.shmaddr = image->ximage->data = shmat(image->shm_info.shmid, NULL, 0);
+ image->shm_info.readOnly = False;
+ shmctl(image->shm_info.shmid, IPC_RMID, NULL);
+
+ if (!XShmAttach(pdata->dpy, &image->shm_info)) {
+ fprintf(stderr, "Failed to attach shared memory to proccess.\n");
+ return 12;
+ }
+
+ XShmGetImage( pdata->dpy,
+ pdata->specs.root,
+ image->ximage,
+ rrect->x,
+ rrect->y,
+ AllPlanes);
+ }
+
+ rmdUpdateYuvBuffer( &pdata->enc_data->yuv,
+ (unsigned char *)image->ximage->data,
+ NULL,
+ 0,
+ 0,
+ rrect->width,
+ rrect->height,
+ pdata->args.no_quick_subsample,
+ pdata->specs.depth);
+
+ return 0;
+}
+
+//make a deep copy
+static void rmdBRWinCpy(BRWindow *target, BRWindow *source) {
+ target->winrect = source->winrect;
+ target->rrect = source->rrect;
+ target->windowid = source->windowid;
+}
+
+//recenters the capture area to the mouse
+//without exiting the display bounding box
+static void rmdMoveCaptureArea( XRectangle *rect,
+ int cursor_x,
+ int cursor_y,
+ int width,
+ int height) {
+ int t_x = 0, t_y = 0;
+
+ t_x = cursor_x - rect->width / 2;
+ t_x = (t_x >> 1) << 1;
+ rect->x = (t_x < 0) ? 0 : ((t_x + rect->width > width) ? width - rect->width : t_x);
+ t_y = cursor_y - rect->height / 2;
+ t_y = (t_y >> 1) << 1;
+ rect->y = (t_y < 0 ) ? 0 : ((t_y + rect->height > height) ? height - rect->height : t_y);
+}
+
+
+static void rmdBlocksReset(unsigned int blocks_w, unsigned int blocks_h) {
+ memset(yblocks, 0, blocks_w * blocks_h * sizeof(*yblocks));
+ memset(ublocks, 0, blocks_w * blocks_h * sizeof(*ublocks));
+ memset(vblocks, 0, blocks_w * blocks_h * sizeof(*vblocks));
+}
+
+
+/**
+* Extract cache blocks from damage list
+*
+* \param root Root entry of the list with damaged areas
+*
+* \param x_offset left x of the recording area
+*
+* \param y_offset upper y of the recording area
+*
+* \param blocks_w Width of image in blocks
+*
+* \param blocks_h Height of image in blocks
+*/
+static void rmdBlocksFromList( RectArea **root,
+ unsigned int x_offset,
+ unsigned int y_offset,
+ unsigned int blocks_w,
+ unsigned int blocks_h) {
+
+ rmdBlocksReset(blocks_w, blocks_h);
+
+ for (RectArea *temp = *root; temp; temp = temp->next) {
+ int row_start, row_end, column_start, column_end;
+
+ column_start = ((int)(temp->rect.x - x_offset)) / Y_UNIT_WIDTH;
+ column_end = ((int)(temp->rect.x + (temp->rect.width - 1) - x_offset)) / Y_UNIT_WIDTH;
+ row_start = ((int)(temp->rect.y - y_offset)) / Y_UNIT_WIDTH;
+ row_end = ((int)(temp->rect.y + (temp->rect.height - 1) - y_offset)) / Y_UNIT_WIDTH;
+
+ for (int i = row_start; i < row_end + 1; i++) {
+ for (int j = column_start; j < column_end + 1; j++) {
+
+ int blockno = i * blocks_w + j;
+
+ yblocks[blockno] = 1;
+ ublocks[blockno] = 1;
+ vblocks[blockno] = 1;
+ }
+ }
+ }
+}
+
+void *rmdGetFrame(ProgData *pdata) {
+ int blocks_w = pdata->enc_data->yuv.y_width / Y_UNIT_WIDTH,
+ blocks_h = pdata->enc_data->yuv.y_height / Y_UNIT_WIDTH;
+ unsigned int msk_ret;
+ XRectangle mouse_pos_abs, mouse_pos_rel, mouse_pos_temp;
+ BRWindow temp_brwin;
+ Window root_ret, child_ret; //Frame
+ XFixesCursorImage *xcim = NULL;
+ Image image = {}, image_back = {}; //the image that holds
+ //the current full screenshot
+ int init_img1 = 0, init_img2 = 0, img_sel, d_buff;
+
+ img_sel = d_buff = pdata->args.full_shots;
+
+ if ((init_img1 = rmdFirstFrame(pdata, &image) != 0)) {
+ if (pdata->args.encOnTheFly) {
+ if (remove(pdata->args.filename)) {
+ perror("Error while removing file:\n");
+ } else {
+ fprintf(stderr, "SIGABRT received, file %s removed\n",
+ pdata->args.filename);
+ }
+ } else {
+ rmdPurgeCache(pdata->cache_data, !pdata->args.nosound);
+ }
+
+ exit(init_img1);
+ }
+
+ if (d_buff) {
+ if ((init_img2 = rmdFirstFrame(pdata, &image_back) != 0)) {
+ if (pdata->args.encOnTheFly) {
+ if (remove(pdata->args.filename)) {
+ perror("Error while removing file:\n");
+ } else{
+ fprintf(stderr, "SIGABRT received, file %s removed\n",
+ pdata->args.filename);
+ }
+ } else {
+ rmdPurgeCache(pdata->cache_data, !pdata->args.nosound);
+ }
+
+ exit(init_img2);
+ }
+ }
+
+ if (!pdata->args.noframe) {
+ pdata->shaped_w = rmdFrameInit( pdata->dpy,
+ pdata->specs.screen,
+ pdata->specs.root,
+ pdata->brwin.rrect.x,
+ pdata->brwin.rrect.y,
+ pdata->brwin.rrect.width,
+ pdata->brwin.rrect.height);
+
+ XSelectInput(pdata->dpy, pdata->shaped_w, ExposureMask);
+ }
+
+ mouse_pos_abs.x = mouse_pos_temp.x = 0;
+ mouse_pos_abs.y = mouse_pos_temp.y = 0;
+ mouse_pos_abs.width = mouse_pos_temp.width = pdata->dummy_p_size;
+ mouse_pos_abs.height = mouse_pos_temp.height = pdata->dummy_p_size;
+
+ //This is the the place where we call XSelectInput
+ //and arrange so that we listen for damage on all
+ //windows
+ rmdInitEventsPolling(pdata);
+
+ while (pdata->running) {
+ unsigned time_frameno;
+
+ //if we are left behind we must not wait.
+ //also before actually pausing we must make sure the streams
+ //are synced. sound stops so this should only happen quickly.
+ pthread_mutex_lock(&pdata->time_mutex);
+ while ( (pdata->avd > 0 || pdata->args.nosound) &&
+ pdata->capture_frameno >= pdata->time_frameno)
+ pthread_cond_wait(&pdata->time_cond, &pdata->time_mutex);
+
+ time_frameno = pdata->time_frameno;
+ pthread_mutex_unlock(&pdata->time_mutex);
+
+ //read all events and construct list with damage
+ //events (if not full_shots)
+ rmdEventLoop(pdata);
+
+ if (pdata->paused)
+ continue;
+
+ //switch back and front buffers (full_shots only)
+ if (d_buff)
+ img_sel = img_sel ? 0 : 1;
+
+ rmdBRWinCpy(&temp_brwin, &pdata->brwin);
+
+
+ if ( pdata->args.xfixes_cursor ||
+ pdata->args.have_dummy_cursor ||
+ pdata->args.follow_mouse) {
+
+
+ // Pointer sequence:
+ // * Mark previous position as dirty with rmdRectInsert()
+ // * Update to new position
+ // * Mark new position as dirty with rmdRectInsert()
+ if ( !pdata->args.full_shots &&
+ mouse_pos_temp.x >= 0 &&
+ mouse_pos_temp.y >= 0 &&
+ mouse_pos_temp.width > 0 &&
+ mouse_pos_temp.height > 0) {
+ rmdRectInsert(&pdata->rect_root, &mouse_pos_temp);
+ }
+
+ if (pdata->args.xfixes_cursor) {
+ xcim = XFixesGetCursorImage(pdata->dpy);
+ mouse_pos_abs.x = xcim->x - xcim->xhot;
+ mouse_pos_abs.y = xcim->y - xcim->yhot;
+ mouse_pos_abs.width = xcim->width;
+ mouse_pos_abs.height = xcim->height;
+ } else {
+ XQueryPointer( pdata->dpy,
+ pdata->specs.root,
+ &root_ret, &child_ret,
+ (int *)&mouse_pos_abs.x,
+ (int *)&mouse_pos_abs.y,
+ (int *)&mouse_pos_rel.x,
+ (int *)&mouse_pos_rel.y,
+ &msk_ret);
+ }
+
+ clip_dummy_pointer_area(&mouse_pos_abs, &temp_brwin.rrect, &mouse_pos_temp);
+ if ( mouse_pos_temp.x >= 0 &&
+ mouse_pos_temp.y >= 0 &&
+ mouse_pos_temp.width > 0 &&
+ mouse_pos_temp.height > 0) {
+
+ //there are 3 capture scenarios:
+ // * Xdamage
+ // * full-shots with double buffering
+ // * full-shots on a single buffer
+ //The last one cannot be reached through
+ //this code (see above how the d_buf variable is set), but
+ //even if it could, it would not be of interest regarding the
+ //marking of the cursor area. Single buffer means full repaint
+ //on every frame so there is no need for marking at all.
+
+ if (!pdata->args.full_shots) {
+ rmdRectInsert(&pdata->rect_root, &mouse_pos_temp);
+ } else if (d_buff) {
+ unsigned char *back_buff= img_sel ?
+ ((unsigned char*)image.ximage->data) :
+ ((unsigned char*)image_back.ximage->data);
+
+ mark_buffer_area(
+ back_buff,
+ mouse_pos_temp.x - temp_brwin.rrect.x,
+ mouse_pos_temp.y - temp_brwin.rrect.y,
+ mouse_pos_temp.width, mouse_pos_temp.height, temp_brwin.rrect.width,
+ pdata->specs.depth
+ );
+ }
+ }
+ }
+
+ if (pdata->args.follow_mouse) {
+ rmdMoveCaptureArea( &pdata->brwin.rrect,
+ mouse_pos_abs.x + pdata->args.xfixes_cursor ? xcim->xhot : 0,
+ mouse_pos_abs.y + pdata->args.xfixes_cursor ? xcim->yhot : 0,
+ pdata->specs.width,
+ pdata->specs.height);
+
+ if (!pdata->args.noframe)
+ rmdMoveFrame( pdata->dpy,
+ pdata->shaped_w,
+ temp_brwin.rrect.x,
+ temp_brwin.rrect.y);
+ }
+
+ if (!pdata->args.full_shots) {
+ pthread_mutex_lock(&pdata->yuv_mutex);
+ rmdUpdateImage( pdata->dpy,
+ &pdata->enc_data->yuv,
+ &pdata->specs,
+ &pdata->rect_root,
+ &temp_brwin,
+ pdata->enc_data,
+ &image,
+ pdata->args.noshared,
+ pdata->shm_opcode,
+ pdata->args.no_quick_subsample);
+
+ rmdBlocksFromList( &pdata->rect_root,
+ temp_brwin.rrect.x,
+ temp_brwin.rrect.y,
+ blocks_w,
+ blocks_h);
+
+ pthread_mutex_unlock(&pdata->yuv_mutex);
+ } else {
+ unsigned char *front_buff = !img_sel ? ((unsigned char*)image.ximage->data):
+ ((unsigned char*)image_back.ximage->data);
+ unsigned char *back_buff = !d_buff ? NULL : (img_sel ?
+ ((unsigned char*)image.ximage->data):
+ ((unsigned char*)image_back.ximage->data));
+
+ if (pdata->args.noshared) {
+ rmdGetZPixmap( pdata->dpy,
+ pdata->specs.root,
+ image.ximage->data,
+ temp_brwin.rrect.x,
+ temp_brwin.rrect.y,
+ temp_brwin.rrect.width,
+ temp_brwin.rrect.height);
+ } else {
+ XShmGetImage( pdata->dpy,
+ pdata->specs.root,
+ ((!img_sel) ? image.ximage : image_back.ximage),
+ temp_brwin.rrect.x,
+ temp_brwin.rrect.y,
+ AllPlanes);
+ }
+
+ pthread_mutex_lock(&pdata->yuv_mutex);
+ rmdBlocksReset(blocks_w, blocks_h);
+ rmdUpdateYuvBuffer( &pdata->enc_data->yuv,
+ front_buff,
+ back_buff,
+ 0,
+ 0,
+ temp_brwin.rrect.width,
+ temp_brwin.rrect.height,
+ pdata->args.no_quick_subsample,
+ pdata->specs.depth);
+
+ pthread_mutex_unlock(&pdata->yuv_mutex);
+ }
+
+ if (pdata->args.xfixes_cursor || pdata->args.have_dummy_cursor) {
+ int mouse_xoffset, mouse_yoffset;
+
+ //avoid segfaults
+ clip_dummy_pointer_area(&mouse_pos_abs, &temp_brwin.rrect, &mouse_pos_temp);
+ mouse_xoffset = mouse_pos_temp.x - mouse_pos_abs.x;
+ mouse_yoffset = mouse_pos_temp.y - mouse_pos_abs.y;
+
+ if ((mouse_xoffset < 0) || (mouse_xoffset > mouse_pos_abs.width))
+ mouse_xoffset = 0;
+
+ if ((mouse_yoffset < 0) || (mouse_yoffset > mouse_pos_abs.height))
+ mouse_yoffset = 0;
+
+ //draw the cursor
+ if ( (mouse_pos_temp.x >= 0) &&
+ (mouse_pos_temp.y >= 0) &&
+ (mouse_pos_temp.width > 0) &&
+ (mouse_pos_temp.height > 0)) {
+
+ if (pdata->args.xfixes_cursor) {
+ rmdXFixesPointerToYuv(
+ &pdata->enc_data->yuv,
+ ((unsigned char*)xcim->pixels),
+ mouse_pos_temp.x - temp_brwin.rrect.x,
+ mouse_pos_temp.y - temp_brwin.rrect.y,
+ mouse_pos_temp.width,
+ mouse_pos_temp.height,
+ mouse_xoffset,
+ mouse_yoffset,
+ xcim->width-mouse_pos_temp.width
+ );
+ } else {
+ rmdDummyPointerToYuv(
+ &pdata->enc_data->yuv,
+ pdata->dummy_pointer,
+ mouse_pos_temp.x - temp_brwin.rrect.x,
+ mouse_pos_temp.y - temp_brwin.rrect.y,
+ mouse_pos_temp.width,
+ mouse_pos_temp.height,
+ mouse_xoffset,
+ mouse_yoffset,
+ pdata->npxl
+ );
+ }
+
+ if (d_buff) {
+ //make previous cursor position dirty
+ //on the currently front buffer (which
+ //will be the back buffer next time it's
+ //used)
+ unsigned char *front_buff = !img_sel ?
+ ((unsigned char*)image.ximage->data) :
+ ((unsigned char*)image_back.ximage->data);
+
+ mark_buffer_area(
+ front_buff,
+ mouse_pos_temp.x - temp_brwin.rrect.x,
+ mouse_pos_temp.y - temp_brwin.rrect.y,
+ mouse_pos_temp.width,
+ mouse_pos_temp.height,
+ temp_brwin.rrect.width,
+ pdata->specs.depth
+ );
+ }
+
+ }
+
+ if (pdata->args.xfixes_cursor) {
+ XFree(xcim);
+ xcim = NULL;
+ }
+ }
+
+ if (!pdata->args.full_shots)
+ rmdClearList(&pdata->rect_root);
+
+ /* notify the encoder of the new frame */
+ pthread_mutex_lock(&pdata->img_buff_ready_mutex);
+ pdata->capture_frameno = time_frameno;
+ pthread_cond_broadcast(&pdata->image_buffer_ready);
+ pthread_mutex_unlock(&pdata->img_buff_ready_mutex);
+ }
+
+ if (!pdata->args.noframe)
+ XDestroyWindow(pdata->dpy, pdata->shaped_w);
+
+ if (!pdata->args.noshared) {
+ XShmDetach(pdata->dpy, &image.shm_info);
+ shmdt(image.shm_info.shmaddr);
+ if (d_buff) {
+ XShmDetach(pdata->dpy, &image_back.shm_info);
+ shmdt(image_back.shm_info.shmaddr);
+ }
+ }
+
+ pthread_exit(&errno);
+}
diff --git a/src/rmd_get_frame.h b/src/rmd_get_frame.h
new file mode 100644
index 0000000..162c05e
--- /dev/null
+++ b/src/rmd_get_frame.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef GET_FRAME_H
+#define GET_FRAME_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Retrieve frame form xserver, and transform to a yuv buffer,
+* either directly(full shots) or by calling UpdateImage.
+* \param pdata ProgData struct containing all program data
+*/
+void *rmdGetFrame(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_getzpixmap.c b/src/rmd_getzpixmap.c
new file mode 100644
index 0000000..43465c8
--- /dev/null
+++ b/src/rmd_getzpixmap.c
@@ -0,0 +1,117 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_getzpixmap.h"
+
+#include "rmd_types.h"
+
+
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/shmproto.h>
+#include <X11/extensions/XShm.h>
+
+
+
+int rmdGetZPixmap( Display *dpy,
+ Window root,
+ char *data,
+ int x,
+ int y,
+ int width,
+ int height) {
+
+ xGetImageReply reply;
+ xGetImageReq *request;
+ long nbytes;
+
+ LockDisplay(dpy);
+ GetReq(GetImage, request);
+ request->drawable = root;
+ request->x = x;
+ request->y = y;
+ request->width = width;
+ request->height = height;
+ request->planeMask = AllPlanes;
+ request->format = ZPixmap;
+
+ if (!_XReply(dpy, (xReply *)&reply, 0, xFalse) || !reply.length) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return 1;
+ }
+
+ nbytes = (long)reply.length << 2;
+ _XReadPad(dpy, data, nbytes);
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return 0;
+}
+
+int rmdGetZPixmapSHM( Display *dpy,
+ Window root,
+ XShmSegmentInfo *shminfo,
+ int shm_opcode,
+ char *data,
+ int x,
+ int y,
+ int width,
+ int height) {
+
+ xShmGetImageReply reply;
+ xShmGetImageReq *request = NULL;
+
+ LockDisplay(dpy);
+ GetReq(ShmGetImage, request);
+
+ request->reqType = shm_opcode;
+ request->shmReqType = X_ShmGetImage;
+ request->shmseg = shminfo->shmseg;
+
+ request->drawable = root;
+ request->x = x;
+ request->y = y;
+ request->width = width;
+ request->height = height;
+ request->planeMask = AllPlanes;
+ request->format = ZPixmap;
+ request->offset = data - shminfo->shmaddr;
+
+ if ((!_XReply(dpy, (xReply *)&reply, 0, xFalse)) || !reply.length) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return 1;
+ }
+
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return 0;
+}
diff --git a/src/rmd_getzpixmap.h b/src/rmd_getzpixmap.h
new file mode 100644
index 0000000..87ad7bb
--- /dev/null
+++ b/src/rmd_getzpixmap.h
@@ -0,0 +1,99 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef GETZPIXMAP_H
+#define GETZPIXMAP_H 1
+
+#include "rmd_types.h"
+
+#include <X11/extensions/XShm.h>
+
+/**
+* Rerieve pixmap data from xserver
+*
+* \param dpy Connection to the server
+*
+* \param root root window of the display
+*
+* \param data (preallocated)buffer to place the data
+*
+* \param x x position of the screenshot
+*
+* \param y y position of the screenshot
+*
+* \param x x position of the screenshot
+*
+* \param width width of the screenshot
+*
+* \param height height position of the screenshot
+*
+* \returns 0 on Success 1 on Failure
+*/
+int rmdGetZPixmap(Display *dpy,
+ Window root,
+ char *data,
+ int x,
+ int y,
+ int width,
+ int height);
+
+/**
+* Rerieve pixmap data from xserver through the MIT-Shm extension
+*
+* \param dpy Connection to the server
+*
+* \param root root window of the display
+*
+* \param shminfo Info for the shared memory segment
+*
+* \param shm_opcode Opcode of Shm extension
+*
+* \param data (preallocated)buffer to place the data
+*
+* \param x x position of the screenshot
+*
+* \param y y position of the screenshot
+*
+* \param x x position of the screenshot
+*
+* \param width width of the screenshot
+*
+* \param height height position of the screenshot
+*
+* \returns 0 on Success 1 on Failure
+*/
+int rmdGetZPixmapSHM(Display *dpy,
+ Window root,
+ XShmSegmentInfo *shminfo,
+ int shm_opcode,
+ char *data,
+ int x,
+ int y,
+ int width,
+ int height);
+
+
+#endif
diff --git a/src/rmd_init_encoder.c b/src/rmd_init_encoder.c
new file mode 100644
index 0000000..a0d81e6
--- /dev/null
+++ b/src/rmd_init_encoder.c
@@ -0,0 +1,346 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_init_encoder.h"
+
+#include "rmd_types.h"
+
+#include "skeleton.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+
+
+static void m_add_fishead_packet(ogg_stream_state *m_ogg_state) {
+ fishead_packet skel_fp;
+
+ skel_fp.ptime_n = skel_fp.btime_n = 0;
+ skel_fp.ptime_d = skel_fp.btime_d = 1000;
+
+ add_fishead_to_stream(m_ogg_state, &skel_fp);
+}
+
+
+static int rmdIncrementalNaming(char **name) {
+ struct stat buff;
+ char *base_name__;
+ int i = 0, fname_length = strlen(*name)-4;
+
+ base_name__ = malloc(fname_length+1);
+ strncpy(base_name__, *name, fname_length);
+ base_name__[fname_length] = '\0';
+
+ //this will go on an endless loop if you have 65536?
+ //files with the same name
+ //or it will crash and die.anyone interested in trying ?
+ while (stat(*name, &buff)==0) {
+ //create new name
+ char *tname = malloc(strlen(*name)+10);
+ char numbuf[8];
+
+ strcpy(tname, base_name__);
+ strcat(tname, "-");
+ i++;
+ snprintf( numbuf, 8, "%d", i );
+ strcat(tname, numbuf);
+ strcat(tname, ".ogv");
+ //save new name
+
+ free(*name);
+ *name = malloc(strlen(tname)+1);
+ strcpy(*name, tname);
+ free(tname);
+ }
+
+ free(base_name__);
+ return 0;
+}
+
+void rmdInitEncoder(ProgData *pdata, EncData *enc_data_t, int buffer_ready) {
+ int y0, y1, y2, fname_length;
+ ogg_stream_state m_ogg_skel;
+ ogg_page skel_og_pg;
+ fisbone_packet skel_fbv, //video fisbone packet
+ skel_fba; //audio fisbone packet
+ const char *fname;
+
+ pdata->enc_data = enc_data_t;
+
+ fname = pdata->args.filename;
+ fname_length = strlen(fname);
+ if (!(fname_length>4 && !strcasecmp(&fname[fname_length-3], "ogv"))) {
+
+ char *new_name = malloc(fname_length + 5);
+ strcpy(new_name, fname);
+ strcat(new_name, ".ogv");
+
+ free(pdata->args.filename);
+ pdata->args.filename = new_name;
+ }
+
+ if (!pdata->args.overwrite) {
+ rmdIncrementalNaming(&(pdata)->args.filename);
+ fprintf(stderr, "Output file: %s\n", pdata->args.filename);
+ }
+
+ enc_data_t->fp = fopen((pdata)->args.filename, "w");
+ if (enc_data_t->fp == NULL) {
+ fprintf(stderr, "Cannot open file %s for writting!\n", (pdata)->args.filename);
+ exit(13);
+ }
+
+ //each stream must have a unique
+ srand(time(NULL));
+ y0 = rand() + 1;
+ y1 = rand() + 1;
+ y2 = rand() + 1;
+ y2 += (y1 == y2);
+ y0 = (((y0 == y1) || (y0 == y2)) ? (y1 + y2) : y0);
+
+ //init ogg streams
+ //skeleton first
+ ogg_stream_init(&m_ogg_skel, y0);
+ m_add_fishead_packet(&m_ogg_skel);
+ if (ogg_stream_pageout(&m_ogg_skel, &skel_og_pg) != 1) {
+ fprintf (stderr, "Internal Ogg library error.\n");
+ exit (2);
+ }
+ fwrite(skel_og_pg.header, 1, skel_og_pg.header_len, enc_data_t->fp);
+ fwrite(skel_og_pg.body, 1, skel_og_pg.body_len, enc_data_t->fp);
+
+ ogg_stream_init(&enc_data_t->m_ogg_ts, y1);
+ if (!pdata->args.nosound)
+ ogg_stream_init(&enc_data_t->m_ogg_vs, y2);
+
+ theora_info_init(&enc_data_t->m_th_inf);
+ enc_data_t->m_th_inf.frame_width = pdata->brwin.rrect.width;
+ enc_data_t->m_th_inf.frame_height = pdata->brwin.rrect.height;
+ enc_data_t->m_th_inf.width = ((enc_data_t->m_th_inf.frame_width + 15) >> 4) << 4;
+ enc_data_t->m_th_inf.height = ((enc_data_t->m_th_inf.frame_height + 15) >> 4) << 4;
+ enc_data_t->m_th_inf.offset_x = 0;
+ enc_data_t->m_th_inf.offset_y = 0;
+
+ enc_data_t->m_th_inf.fps_numerator = pdata->args.fps * 100.0;
+ enc_data_t->m_th_inf.fps_denominator = 100;
+ enc_data_t->m_th_inf.aspect_numerator = 1;
+ enc_data_t->m_th_inf.aspect_denominator = 1;
+
+ enc_data_t->m_th_inf.colorspace = OC_CS_UNSPECIFIED;
+ enc_data_t->m_th_inf.pixelformat = OC_PF_420;
+
+ enc_data_t->m_th_inf.target_bitrate = pdata->args.v_bitrate;
+ enc_data_t->m_th_inf.quality = pdata->args.v_quality;
+ enc_data_t->m_th_inf.dropframes_p = 0;
+ enc_data_t->m_th_inf.quick_p = 1;
+ enc_data_t->m_th_inf.keyframe_auto_p = 1;
+ enc_data_t->m_th_inf.keyframe_frequency = 64;
+ enc_data_t->m_th_inf.keyframe_frequency_force = 64;
+ enc_data_t->m_th_inf.keyframe_data_target_bitrate = enc_data_t->m_th_inf.quality * 1.5;
+ enc_data_t->m_th_inf.keyframe_auto_threshold = 80;
+ enc_data_t->m_th_inf.keyframe_mindistance = 8;
+ enc_data_t->m_th_inf.noise_sensitivity = 1;
+ enc_data_t->m_th_inf.sharpness = 2;
+
+ if (!pdata->args.nosound) {
+ int ret;
+ vorbis_info_init(&enc_data_t->m_vo_inf);
+ ret = vorbis_encode_init_vbr( &enc_data_t->m_vo_inf,
+ pdata->args.channels,
+ pdata->args.frequency,
+ (float)pdata->args.s_quality*0.1);
+ if (ret) {
+ fprintf(stderr, "Error while setting up vorbis stream quality!\n");
+ exit(2);
+ }
+ vorbis_comment_init(&enc_data_t->m_vo_cmmnt);
+ vorbis_analysis_init(&enc_data_t->m_vo_dsp, &enc_data_t->m_vo_inf);
+ vorbis_block_init(&enc_data_t->m_vo_dsp, &enc_data_t->m_vo_block);
+ }
+
+ if (theora_encode_init(&enc_data_t->m_th_st, &enc_data_t->m_th_inf)) {
+ fprintf(stderr, "Theora encoder initialization failure.\n");
+ exit(2);
+ }
+
+ if (theora_encode_header(&enc_data_t->m_th_st, &enc_data_t->m_ogg_pckt1)) {
+ fprintf(stderr, "Theora enocde header failure.\n");
+ exit(2);
+ }
+
+ ogg_stream_packetin(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pckt1);
+ if (ogg_stream_pageout(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pg) != 1) {
+ fprintf(stderr, "Internal Ogg library error.\n");
+ exit(2);
+ }
+ fwrite(enc_data_t->m_ogg_pg.header, 1,
+ enc_data_t->m_ogg_pg.header_len,
+ enc_data_t->fp);
+ fwrite(enc_data_t->m_ogg_pg.body, 1,
+ enc_data_t->m_ogg_pg.body_len,
+ enc_data_t->fp);
+
+ theora_comment_init(&enc_data_t->m_th_cmmnt);
+ theora_comment_add_tag(&enc_data_t->m_th_cmmnt, "recordMyDesktop", VERSION);
+ theora_encode_comment(&enc_data_t->m_th_cmmnt, &enc_data_t->m_ogg_pckt1);
+ ogg_stream_packetin(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pckt1);
+ theora_encode_tables(&enc_data_t->m_th_st, &enc_data_t->m_ogg_pckt1);
+ ogg_stream_packetin(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pckt1);
+
+ if (!pdata->args.nosound) {
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+
+ vorbis_analysis_headerout( &enc_data_t->m_vo_dsp,
+ &enc_data_t->m_vo_cmmnt,
+ &header, &header_comm,
+ &header_code);
+ ogg_stream_packetin(&enc_data_t->m_ogg_vs, &header);
+ if (ogg_stream_pageout(&enc_data_t->m_ogg_vs, &enc_data_t->m_ogg_pg) != 1) {
+ fprintf(stderr, "Internal Ogg library error.\n");
+ exit(2);
+ }
+
+ fwrite(enc_data_t->m_ogg_pg.header, 1,
+ enc_data_t->m_ogg_pg.header_len,
+ enc_data_t->fp);
+
+ fwrite(enc_data_t->m_ogg_pg.body, 1,
+ enc_data_t->m_ogg_pg.body_len,
+ enc_data_t->fp);
+
+ ogg_stream_packetin(&enc_data_t->m_ogg_vs, &header_comm);
+ ogg_stream_packetin(&enc_data_t->m_ogg_vs, &header_code);
+ }
+
+ //fishbone packets go here
+ memset(&skel_fbv, 0, sizeof(skel_fbv));
+ skel_fbv.serial_no = enc_data_t->m_ogg_ts.serialno;
+ skel_fbv.nr_header_packet = 3;
+ skel_fbv.granule_rate_n = enc_data_t->m_th_inf.fps_numerator;
+ skel_fbv.granule_rate_d = enc_data_t->m_th_inf.fps_denominator;
+ skel_fbv.start_granule = 0;
+ skel_fbv.preroll = 0;
+ skel_fbv.granule_shift = theora_granule_shift(&enc_data_t->m_th_inf);
+ add_message_header_field(&skel_fbv, "Content-Type", "video/theora");
+
+ add_fisbone_to_stream(&m_ogg_skel, &skel_fbv);
+
+ if (!pdata->args.nosound) {
+
+ memset(&skel_fba, 0, sizeof(skel_fba));
+ skel_fba.serial_no = enc_data_t->m_ogg_vs.serialno;
+ skel_fba.nr_header_packet = 3;
+ skel_fba.granule_rate_n = pdata->args.frequency;
+ skel_fba.granule_rate_d = (ogg_int64_t)1;
+ skel_fba.start_granule = 0;
+ skel_fba.preroll = 2;
+ skel_fba.granule_shift = 0;
+ add_message_header_field(&skel_fba, "Content-Type", "audio/vorbis");
+
+ add_fisbone_to_stream(&m_ogg_skel, &skel_fba);
+ }
+
+ while (1) {
+ int result = ogg_stream_flush(&m_ogg_skel, &skel_og_pg);
+ if (result < 0) {
+ fprintf (stderr, "Internal Ogg library error.\n");
+ exit(2);
+ }
+
+ if (result == 0)
+ break;
+
+ fwrite(skel_og_pg.header, 1, skel_og_pg.header_len, enc_data_t->fp);
+ fwrite(skel_og_pg.body, 1, skel_og_pg.body_len, enc_data_t->fp);
+ }
+
+ while (1) {
+ int result = ogg_stream_flush(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pg);
+ if (result < 0) {
+ fprintf(stderr, "Internal Ogg library error.\n");
+ exit(2);
+ }
+
+ if (result == 0)
+ break;
+
+ fwrite( enc_data_t->m_ogg_pg.header, 1,
+ enc_data_t->m_ogg_pg.header_len,
+ enc_data_t->fp);
+ fwrite( enc_data_t->m_ogg_pg.body, 1,
+ enc_data_t->m_ogg_pg.body_len,
+ enc_data_t->fp);
+ }
+
+ if (!pdata->args.nosound) {
+ while (1) {
+ int result = ogg_stream_flush(&enc_data_t->m_ogg_vs, &enc_data_t->m_ogg_pg);
+ if (result < 0) {
+ fprintf(stderr, "Internal Ogg library error.\n");
+ exit(2);
+ }
+
+ if (result == 0)
+ break;
+
+ fwrite( enc_data_t->m_ogg_pg.header, 1,
+ enc_data_t->m_ogg_pg.header_len,
+ enc_data_t->fp);
+ fwrite( enc_data_t->m_ogg_pg.body, 1,
+ enc_data_t->m_ogg_pg.body_len,
+ enc_data_t->fp);
+ }
+ }
+
+ //skeleton eos
+ add_eos_packet_to_stream(&m_ogg_skel);
+ if (ogg_stream_flush(&m_ogg_skel, &skel_og_pg) < 0) {
+ fprintf(stderr, "Internal Ogg library error.\n");
+ exit(2);
+ }
+ fwrite(skel_og_pg.header, 1, skel_og_pg.header_len, enc_data_t->fp);
+
+ //theora buffer allocation, if any
+ if (!buffer_ready) {
+ enc_data_t->yuv.y = malloc(enc_data_t->m_th_inf.height * enc_data_t->m_th_inf.width);
+ enc_data_t->yuv.u = malloc(enc_data_t->m_th_inf.height * enc_data_t->m_th_inf.width / 4);
+ enc_data_t->yuv.v = malloc(enc_data_t->m_th_inf.height * enc_data_t->m_th_inf.width / 4);
+ enc_data_t->yuv.y_width = enc_data_t->m_th_inf.width;
+ enc_data_t->yuv.y_height = enc_data_t->m_th_inf.height;
+ enc_data_t->yuv.y_stride = enc_data_t->m_th_inf.width;
+
+ enc_data_t->yuv.uv_width = enc_data_t->m_th_inf.width / 2;
+ enc_data_t->yuv.uv_height = enc_data_t->m_th_inf.height / 2;
+ enc_data_t->yuv.uv_stride = enc_data_t->m_th_inf.width / 2;
+ }
+
+ theora_info_clear(&enc_data_t->m_th_inf);
+}
diff --git a/src/rmd_init_encoder.h b/src/rmd_init_encoder.h
new file mode 100644
index 0000000..bbf73c7
--- /dev/null
+++ b/src/rmd_init_encoder.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef INIT_ENCODER_H
+#define INIT_ENCODER_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Initialize theora,vorbis encoders, and their respective ogg streams.
+*
+* \param pdata ProgData struct containing all program data
+*
+* \param enc_data_t Encoding options
+*
+* \param buffer_ready when 1, the yuv buffer must be preallocated
+* when 0 rmdInitEncoder will alocate a new one
+*
+*/
+void rmdInitEncoder(ProgData *pdata,EncData *enc_data_t,int buffer_ready);
+
+
+#endif
diff --git a/src/rmd_initialize_data.c b/src/rmd_initialize_data.c
new file mode 100644
index 0000000..0f0a073
--- /dev/null
+++ b/src/rmd_initialize_data.c
@@ -0,0 +1,252 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_initialize_data.h"
+
+#include "rmd_cache.h"
+#include "rmd_init_encoder.h"
+#include "rmd_jack.h"
+#include "rmd_make_dummy_pointer.h"
+#include "rmd_opendev.h"
+#include "rmd_yuv_utils.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_LIBASOUND
+static void rmdFixBufferSize(snd_pcm_uframes_t *buffsize) {
+ snd_pcm_uframes_t buffsize_t = *buffsize,
+#else
+static void rmdFixBufferSize(u_int32_t *buffsize) {
+ u_int32_t buffsize_t = *buffsize,
+#endif
+ buffsize_ret = 1;
+
+ while (buffsize_t > 1) {
+ buffsize_t >>= 1;
+ buffsize_ret <<= 1;
+ }
+ fprintf(stderr, "Buffer size adjusted to %d from %d frames.\n",
+ (int)buffsize_ret, (int)*buffsize);
+}
+
+int rmdInitializeData(ProgData *pdata, EncData *enc_data, CacheData *cache_data) {
+ fprintf(stderr, "Initializing...\n");
+
+ rmdMakeMatrices();
+ if (pdata->args.have_dummy_cursor) {
+ pdata->dummy_pointer = rmdMakeDummyPointer( pdata->dpy,
+ &pdata->specs,
+ 16,
+ pdata->args.cursor_color,
+ 0,
+ &pdata->npxl);
+ pdata->dummy_p_size = 16;
+ } else
+ pdata->dummy_p_size = 0;
+
+ pthread_mutex_init(&pdata->sound_buffer_mutex, NULL);
+ pthread_mutex_init(&pdata->img_buff_ready_mutex, NULL);
+ pthread_mutex_init(&pdata->theora_lib_mutex, NULL);
+ pthread_mutex_init(&pdata->vorbis_lib_mutex, NULL);
+ pthread_mutex_init(&pdata->libogg_mutex, NULL);
+ pthread_mutex_init(&pdata->yuv_mutex, NULL);
+ pthread_mutex_init(&pdata->pause_mutex, NULL);
+ pthread_mutex_init(&pdata->time_mutex, NULL);
+ pthread_cond_init(&pdata->time_cond, NULL);
+ pthread_cond_init(&pdata->pause_cond, NULL);
+ pthread_cond_init(&pdata->image_buffer_ready, NULL);
+ pthread_cond_init(&pdata->sound_data_read, NULL);
+ pthread_cond_init(&pdata->theora_lib_clean, NULL);
+ pthread_cond_init(&pdata->vorbis_lib_clean, NULL);
+
+ pdata->th_encoding_clean = pdata->v_encoding_clean = 1;
+ pdata->rect_root = NULL;
+ pdata->avd = 0;
+ pdata->sound_buffer = NULL;
+ pdata->running = TRUE;
+ pdata->paused = FALSE;
+ pdata->aborted = FALSE;
+ pdata->pause_state_changed = FALSE;
+ pdata->frames_total = 0;
+ pdata->frames_lost = 0;
+ pdata->capture_frameno = 0;
+ pdata->time_frameno = 0;
+
+ if (!pdata->args.nosound) {
+ if (!pdata->args.use_jack) {
+ rmdFixBufferSize(&pdata->args.buffsize);
+#ifdef HAVE_LIBASOUND
+ pdata->sound_handle=rmdOpenDev( pdata->args.device,
+ &pdata->args.channels,
+ &pdata->args.frequency,
+ &pdata->args.buffsize,
+ &pdata->periodsize,
+ &pdata->periodtime,
+ &pdata->hard_pause);
+
+ pdata->sound_framesize=(snd_pcm_format_width(SND_PCM_FORMAT_S16_LE) / 8 *
+ pdata->args.channels);
+
+ if (pdata->sound_handle == NULL) {
+#else
+ pdata->sound_handle = rmdOpenDev( pdata->args.device,
+ pdata->args.channels,
+ pdata->args.frequency);
+
+ pdata->periodtime = (1000000 * pdata->args.buffsize) /
+ ((pdata->args.channels<<1) * pdata->args.frequency);
+ //when using OSS periodsize serves as an alias of buffsize
+ pdata->periodsize = pdata->args.buffsize;
+ pdata->sound_framesize = pdata->args.channels<<1;
+ if (pdata->sound_handle<0) {
+#endif
+ fprintf(stderr, "Error while opening/configuring soundcard %s\n"
+ "Try running with the --no-sound or specify a "
+ "correct device.\n",
+ pdata->args.device);
+ return 3;
+ }
+ } else {
+#ifdef HAVE_LIBJACK
+ int jack_error = 0;
+ pdata->jdata->port_names = pdata->args.jack_port_names;
+ pdata->jdata->nports = pdata->args.jack_nports;
+ pdata->jdata->ringbuffer_secs = pdata->args.jack_ringbuffer_secs;
+ pdata->jdata->sound_buffer_mutex = &pdata->sound_buffer_mutex;
+ pdata->jdata->sound_data_read = &pdata->sound_data_read;
+ pdata->jdata->capture_started = 0;
+
+ if ((jack_error = rmdStartJackClient(pdata->jdata))!=0)
+ return jack_error;
+
+ pdata->args.buffsize = pdata->jdata->buffersize;
+ pdata->periodsize = pdata->args.buffsize;
+ pdata->args.frequency = pdata->jdata->frequency;
+ pdata->args.channels = pdata->jdata->nports;
+ pdata->periodtime = 1000000 * pdata->args.buffsize /
+ pdata->args.frequency;
+ pdata->sound_framesize = sizeof(jack_default_audio_sample_t) *
+ pdata->jdata->nports;
+#else
+ fprintf(stderr, "Should not be here!\n");
+ exit(-1);
+#endif
+ }
+ }
+
+ if (pdata->args.encOnTheFly)
+ rmdInitEncoder(pdata, enc_data, 0);
+ else
+ rmdInitCacheData(pdata, enc_data, cache_data);
+
+ for (int i = 0; i < pdata->enc_data->yuv.y_width * pdata->enc_data->yuv.y_height; i++)
+ pdata->enc_data->yuv.y[i] = 0;
+
+ for (int i = 0; i < pdata->enc_data->yuv.uv_width * pdata->enc_data->yuv.uv_height; i++)
+ pdata->enc_data->yuv.v[i] = pdata->enc_data->yuv.u[i] = 127;
+
+ yblocks = malloc((pdata->enc_data->yuv.y_width / Y_UNIT_WIDTH) *
+ (pdata->enc_data->yuv.y_height / Y_UNIT_WIDTH));
+ ublocks = malloc((pdata->enc_data->yuv.y_width / Y_UNIT_WIDTH) *
+ (pdata->enc_data->yuv.y_height / Y_UNIT_WIDTH));
+ vblocks = malloc((pdata->enc_data->yuv.y_width / Y_UNIT_WIDTH) *
+ (pdata->enc_data->yuv.y_height / Y_UNIT_WIDTH));
+
+ pdata->frametime = 1000000 / pdata->args.fps;
+
+ return 0;
+
+}
+
+void rmdSetupDefaultArgs(ProgArgs *args) {
+ args->delay = 0;
+ args->windowid = 0;
+ args->x = 0;
+ args->y = 0;
+ args->width = 0;
+ args->height = 0;
+ args->rescue_path = NULL;
+ args->nosound = 0;
+ args->full_shots = 0;
+ args->follow_mouse = 0;
+ args->encOnTheFly = 0;
+ args->nowmcheck = 0;
+ args->overwrite = 0;
+ args->use_jack = 0;
+ args->noshared = 0;
+ args->no_encode = 0;
+ args->noframe = 0;
+ args->jack_nports = 0;
+ args->jack_ringbuffer_secs = 3.0;
+ args->zerocompression = 1;
+ args->no_quick_subsample = 1;
+ args->cursor_color = 1;
+ args->have_dummy_cursor = 0;
+ args->xfixes_cursor = 1;
+ args->fps = 15;
+ args->channels = 1;
+ args->frequency = 22050;
+ args->buffsize = 4096;
+ args->v_bitrate = 0;
+ args->v_quality = 63;
+ args->s_quality = 10;
+
+ if (getenv("DISPLAY") != NULL) {
+ args->display = malloc(strlen(getenv("DISPLAY")) + 1);
+ strcpy(args->display, getenv("DISPLAY"));
+ } else {
+ args->display = NULL;
+ }
+
+ memset(args->jack_port_names, 0, sizeof(args->jack_port_names));
+
+ args->device = malloc(strlen(DEFAULT_AUDIO_DEVICE) + 1);
+ strcpy(args->device, DEFAULT_AUDIO_DEVICE);
+
+ args->workdir = malloc(5);
+ strcpy(args->workdir, "/tmp");
+
+ args->pause_shortcut = malloc(15);
+ strcpy(args->pause_shortcut, "Control+Mod1+p");
+
+ args->stop_shortcut = malloc(15);
+ strcpy(args->stop_shortcut, "Control+Mod1+s");
+
+ args->filename = malloc(8);
+ strcpy(args->filename, "out.ogv");
+}
+
+void rmdCleanUp(void) {
+ free(yblocks);
+ free(ublocks);
+ free(vblocks);
+}
diff --git a/src/rmd_initialize_data.h b/src/rmd_initialize_data.h
new file mode 100644
index 0000000..a5e43dd
--- /dev/null
+++ b/src/rmd_initialize_data.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef INITIALIZE_DATA_H
+#define INITIALIZE_DATA_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* initialize lists,mutexes,image buffers, take first screenshot,
+* and anything else needed before launching the capture threads.
+*
+* \param pdata ProgData struct containing all program data
+*
+* \param enc_data reference to enc_data structure
+*
+* \param cache_data reference to cache_data structure
+*
+* \returns 0 on success, other values must cause the program to exit
+*/
+int rmdInitializeData(ProgData *pdata,
+ EncData *enc_data,
+ CacheData *cache_data);
+
+/**
+* Sets up the ProgArgs structure to default values.
+*/
+void rmdSetupDefaultArgs(ProgArgs *args);
+
+/**
+* Currently only frees some memory
+* (y,u,v blocks)
+*
+*/
+void rmdCleanUp(void);
+
+
+#endif
diff --git a/src/rmd_jack.c b/src/rmd_jack.c
new file mode 100644
index 0000000..841017f
--- /dev/null
+++ b/src/rmd_jack.c
@@ -0,0 +1,209 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_jack.h"
+
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <string.h>
+
+#ifdef HAVE_LIBJACK
+
+
+/**
+* Callback for capture through jack
+*
+* \param nframes Number of frames captured
+*
+* \param jdata_t Pointer to JackData struct containing port
+* and client information
+*
+* \returns Zero always
+*/
+static int rmdJackCapture(jack_nframes_t nframes, void *jdata_t) {
+ JackData *jdata = (JackData *)jdata_t;
+
+ if (!jdata->pdata->running || jdata->pdata->paused || !jdata->capture_started)
+ return 0;
+
+ for(int i = 0; i < jdata->nports; i++)
+ jdata->portbuf[i] = jack_port_get_buffer(jdata->ports[i], nframes);
+
+
+ pthread_mutex_lock(jdata->sound_buffer_mutex);
+//vorbis analysis buffer wants uninterleaved data
+//so we are simply placing the buffers for every channel
+//sequentially on the ringbuffer
+ for(int i = 0; i < jdata->nports; i++)
+ jack_ringbuffer_write( jdata->sound_buffer,
+ (void *)(jdata->portbuf[i]),
+ nframes *
+ sizeof(jack_default_audio_sample_t));
+
+ pthread_cond_signal(jdata->sound_data_read);
+ pthread_mutex_unlock(jdata->sound_buffer_mutex);
+
+ return 0;
+}
+
+/**
+* Register and Activate specified ports
+*
+* \param jdata_t Pointer to JackData struct containing port
+* and client information
+*
+* \returns 0 on Success, 1 on failure
+*/
+static int rmdSetupPorts(JackData *jdata) {
+ jdata->ports = malloc(sizeof(jack_port_t *) * jdata->nports);
+ jdata->portbuf = malloc(sizeof(jack_default_audio_sample_t *) * jdata->nports);
+ memset(jdata->portbuf, 0, sizeof(jack_default_audio_sample_t *) * jdata->nports);
+
+ for(int i = 0; i < jdata->nports; i++) {
+ char name[64];//recordMyDesktop:input_n<64 is enough for full name
+ char num[8];
+ strcpy(name, "input_");
+ snprintf(num, 8, "%d", i + 1);
+ strcat(name, num);
+
+ jdata->ports[i] = jack_port_register( jdata->client,
+ name,
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsInput,
+ 0);
+
+ if (!jdata->ports[i]) {
+ fprintf(stderr, "Cannot register input port \"%s\"!\n", name);
+ return 1;
+ }
+
+ if (jack_connect(jdata->client, jdata->port_names[i], jack_port_name(jdata->ports[i]))) {
+
+ fprintf(stderr, "Cannot connect input port %s to %s\n",
+ jack_port_name(jdata->ports[i]),
+ jdata->port_names[i]);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+//in case the jack server shuts down
+//the program should stop recording,
+//encode the result(if not on the fly)
+//an exit cleanly.
+static void rmdJackShutdown(void *jdata_t) {
+ JackData *jdata = (JackData *)jdata_t;
+
+ jdata->pdata->running = FALSE;
+
+ fprintf (stderr, "JACK shutdown\n");
+}
+
+int rmdStartJackClient(JackData *jdata) {
+ float ring_buffer_size = 0.0;
+ int pid;
+ char pidbuf[8];
+ char rmd_client_name[32];
+
+ //construct the jack client name
+ //which is recordMyDesktop-pid
+ //in order to allow multiple
+ //instances of recordMyDesktop
+ //to connetc to a Jack Server
+ strcpy(rmd_client_name, "recordMyDesktop-");
+ pid = getpid();
+ snprintf( pidbuf, 8, "%d", pid );
+ strcat(rmd_client_name, pidbuf);
+
+ if ((jdata->client = jack_client_new(rmd_client_name)) == 0) {
+ fprintf(stderr, "Could not create new client!\n"
+ "Make sure that Jack server is running!\n");
+ return 15;
+ }
+//in contrast to ALSA and OSS, Jack dictates frequency
+//and buffersize to the values it was launched with.
+//Supposedly jack_set_buffer_size can set the buffersize
+//but that causes some kind of halt and keeps giving me
+//zero buffers.
+//recordMyDesktop cannot handle buffer size changes.
+//FIXME
+//There is a callback for buffer size changes that I should use.
+//It will provide clean exits, instead of ?segfaults? .
+//Continuing though is not possible, with the current layout
+//(it might be in some cases, but it will certainly be the cause
+//of unpredicted problems). A clean exit is preferable
+//and any recording up to that point will be encoded and saved.
+ jdata->frequency = jack_get_sample_rate(jdata->client);
+ jdata->buffersize = jack_get_buffer_size(jdata->client);
+ ring_buffer_size = ( jdata->ringbuffer_secs*
+ jdata->frequency*
+ sizeof(jack_default_audio_sample_t)*
+ jdata->nports);
+ jdata->sound_buffer = jack_ringbuffer_create((int)(ring_buffer_size+0.5));//round up
+ jack_set_process_callback(jdata->client, rmdJackCapture, jdata);
+ jack_on_shutdown(jdata->client, rmdJackShutdown, jdata);
+
+ if (jack_activate(jdata->client)) {
+ fprintf(stderr, "cannot activate client!\n");
+ return 16;
+ }
+
+ if (rmdSetupPorts(jdata)) {
+ jack_client_close(jdata->client);
+ return 17;
+ }
+
+ return 0;
+}
+
+int rmdStopJackClient(JackData *jdata) {
+ int ret = 0;
+
+ jack_ringbuffer_free(jdata->sound_buffer);
+ if (jack_client_close(jdata->client)) {
+ fprintf(stderr, "Cannot close Jack client!\n");
+ ret = 1;
+ }
+
+/*TODO*/
+//I need to make some kind of program/thread
+//flow diagram to see where it's safe to dlclose
+//because here it causes a segfault.
+
+// if (dlclose(jdata->jack_lib_handle)) {
+// fprintf(stderr, "Cannot unload Jack library!\n");
+// ret=1;
+// }
+// else fprintf(stderr, "Unloaded Jack library.\n");
+
+ return ret;
+}
+
+#endif
diff --git a/src/rmd_jack.h b/src/rmd_jack.h
new file mode 100644
index 0000000..a216c1b
--- /dev/null
+++ b/src/rmd_jack.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_JACK_H
+#define RMD_JACK_H 1
+
+#include "rmd_types.h"
+
+
+#ifdef HAVE_LIBJACK
+
+/**
+* create and activate client,register ports
+*
+* \param jdata_t Pointer to JackData struct containing port
+* and client information
+*
+* \returns 0 on Success, error code on failure
+* (depending on where the error occured)
+*/
+int rmdStartJackClient(JackData *jdata);
+
+/**
+* Close Jack Client
+*
+* \param jdata_t Pointer to JackData struct containing port
+* and client information
+*
+* \returns 0 on Success, 1 on failure
+*/
+int rmdStopJackClient(JackData *jdata);
+
+#endif
+
+
+#endif
diff --git a/src/rmd_load_cache.c b/src/rmd_load_cache.c
new file mode 100644
index 0000000..8ca37f6
--- /dev/null
+++ b/src/rmd_load_cache.c
@@ -0,0 +1,336 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_load_cache.h"
+
+#include "rmd_cache.h"
+#include "rmd_encode_image_buffer.h"
+#include "rmd_encode_sound_buffer.h"
+#include "rmd_macro.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+#include <signal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+//The number of bytes for every
+//sub-block of the y,u and v planes.
+//Since the blocks are square
+//these are obviously the squares
+//of the widths(specified above),
+//but the definitions bellow are only
+//for convenience anyway.
+#define Y_UNIT_BYTES 0x0100
+#define UV_UNIT_BYTES 0x0040
+
+
+//The frame after retrieval.
+//Based on the Header information
+//we can read the correct amount of bytes.
+typedef struct _CachedFrame{
+ FrameHeader *header;
+ u_int32_t *YBlocks, //identifying number on the grid,
+ *UBlocks, //starting at top left
+ *VBlocks; // >> >>
+ unsigned char *YData, //pointer to data for the blocks that have changed,
+ *UData, //which have to be remapped
+ *VData; //on the buffer when reading
+}CachedFrame;
+
+
+static void rmdLoadBlock(
+ unsigned char *dest,
+ unsigned char *source,
+ int blockno,
+ int width,
+ int height,
+ int blockwidth) {
+
+ int block_i = blockno / (width / blockwidth),//place on the grid
+ block_k = blockno % (width / blockwidth);
+
+ for (int j = 0; j < blockwidth; j++)//we copy rows
+ memcpy( &dest[(block_i * width + block_k) * blockwidth + j * width],
+ &source[j * blockwidth],
+ blockwidth);
+}
+
+//returns number of bytes
+static int rmdReadZF(void * buffer, size_t size, size_t nmemb, FILE *ucfp, gzFile ifp) {
+ if ((ifp != NULL && ucfp != NULL) ||
+ (ifp == NULL && ucfp == NULL))
+ return -1;
+ else if (ucfp != NULL) {
+ return size * fread(buffer, size, nmemb, ucfp);
+ } else
+ return gzread(ifp, buffer, size * nmemb);
+}
+
+static int rmdReadFrame(CachedFrame *frame, FILE *ucfp, gzFile ifp) {
+ int index_entry_size = sizeof(u_int32_t);
+
+ if (frame->header->Ynum > 0) {
+ if (rmdReadZF( frame->YBlocks,
+ index_entry_size,
+ frame->header->Ynum,
+ ucfp,
+ ifp) != index_entry_size * frame->header->Ynum) {
+ return -1;
+ }
+ }
+
+ if (frame->header->Unum > 0) {
+ if (rmdReadZF( frame->UBlocks,
+ index_entry_size,
+ frame->header->Unum,
+ ucfp,
+ ifp) != index_entry_size * frame->header->Unum) {
+ return -1;
+ }
+ }
+
+ if (frame->header->Vnum > 0) {
+ if (rmdReadZF( frame->VBlocks,
+ index_entry_size,
+ frame->header->Vnum,
+ ucfp,
+ ifp) != index_entry_size * frame->header->Vnum) {
+ return -1;
+ }
+ }
+
+ if (frame->header->Ynum > 0) {
+ if (rmdReadZF( frame->YData,
+ Y_UNIT_BYTES,
+ frame->header->Ynum,
+ ucfp,
+ ifp) != Y_UNIT_BYTES * frame->header->Ynum) {
+ return -2;
+ }
+ }
+
+ if (frame->header->Unum > 0) {
+ if (rmdReadZF( frame->UData,
+ UV_UNIT_BYTES,
+ frame->header->Unum,
+ ucfp,
+ ifp) != UV_UNIT_BYTES * frame->header->Unum) {
+ return -2;
+ }
+ }
+
+ if (frame->header->Vnum > 0) {
+ if (rmdReadZF( frame->VData,
+ UV_UNIT_BYTES,
+ frame->header->Vnum,
+ ucfp,
+ ifp) != UV_UNIT_BYTES * frame->header->Vnum) {
+ return -2;
+ }
+ }
+
+ return 0;
+}
+
+void *rmdLoadCache(ProgData *pdata) {
+
+ yuv_buffer *yuv = &pdata->enc_data->yuv;
+ gzFile ifp = NULL;
+ FILE *ucfp = NULL;
+ FILE *afp = pdata->cache_data->afp;
+ FrameHeader fheader;
+ CachedFrame frame;
+ int nth_cache = 1,
+ audio_end = 0,
+ extra_frames = 0, //total number of duplicated frames
+ missing_frames = 0, //if this is found >0 current run will not load
+ //a frame but it will proccess the previous
+ thread_exit = 0, //0 success, -1 couldn't find files,1 couldn't remove
+ blocknum_x = pdata->enc_data->yuv.y_width / Y_UNIT_WIDTH,
+ blocknum_y = pdata->enc_data->yuv.y_height / Y_UNIT_WIDTH,
+ blockszy = Y_UNIT_BYTES,//size of y plane block in bytes
+ blockszuv = UV_UNIT_BYTES;//size of u,v plane blocks in bytes
+ signed char *sound_data = (signed char *)malloc(pdata->periodsize * pdata->sound_framesize);
+
+ u_int32_t YBlocks[(yuv->y_width * yuv->y_height) / Y_UNIT_BYTES],
+ UBlocks[(yuv->uv_width * yuv->uv_height) / UV_UNIT_BYTES],
+ VBlocks[(yuv->uv_width * yuv->uv_height) / UV_UNIT_BYTES];
+
+ // We allocate the frame that we will use
+ frame.header = &fheader;
+ frame.YBlocks = YBlocks;
+ frame.UBlocks = UBlocks;
+ frame.VBlocks = VBlocks;
+ frame.YData = malloc(yuv->y_width * yuv->y_height);
+ frame.UData = malloc(yuv->uv_width * yuv->uv_height);
+ frame.VData = malloc(yuv->uv_width * yuv->uv_height);
+
+ //and the we open our files
+ if (!pdata->args.zerocompression) {
+ ifp = gzopen(pdata->cache_data->imgdata, "rb");
+ if (ifp == NULL) {
+ thread_exit = -1;
+ pthread_exit(&thread_exit);
+ }
+ } else {
+ ucfp = fopen(pdata->cache_data->imgdata, "rb");
+ if (ucfp == NULL) {
+ thread_exit = -1;
+ pthread_exit(&thread_exit);
+ }
+ }
+
+ if (!pdata->args.nosound) {
+ afp = fopen(pdata->cache_data->audiodata, "rb");
+ if (afp == NULL) {
+ thread_exit = -1;
+ pthread_exit(&thread_exit);
+ }
+ }
+
+ //this will be used now to define if we proccess audio or video
+ //on any given loop.
+ pdata->avd = 0;
+ //If sound finishes first,we go on with the video.
+ //If video ends we will do one more run to flush audio in the ogg file
+ while (pdata->running) {
+ //video load and encoding
+ if (pdata->avd <= 0 || pdata->args.nosound || audio_end) {
+ if (missing_frames > 0) {
+ extra_frames++;
+ missing_frames--;
+ rmdSyncEncodeImageBuffer(pdata);
+ } else if (((!pdata->args.zerocompression) &&
+ (gzread(ifp, frame.header, sizeof(FrameHeader)) ==
+ sizeof(FrameHeader) )) ||
+ ((pdata->args.zerocompression) &&
+ (fread(frame.header, sizeof(FrameHeader), 1, ucfp) == 1))) {
+ //sync
+ missing_frames += frame.header->current_total -
+ (extra_frames + frame.header->frameno);
+ if (pdata->frames_total) {
+ fprintf(stdout, "\r[%d%%] ",
+ ((frame.header->frameno + extra_frames) * 100) / pdata->frames_total);
+ } else
+ fprintf(stdout, "\r[%d frames rendered] ",
+ (frame.header->frameno + extra_frames));
+ fflush(stdout);
+ if ( (frame.header->Ynum <= blocknum_x * blocknum_y) &&
+ (frame.header->Unum <= blocknum_x * blocknum_y) &&
+ (frame.header->Vnum <= blocknum_x * blocknum_y) &&
+ !rmdReadFrame( &frame,
+ (pdata->args.zerocompression ? ucfp : NULL),
+ (pdata->args.zerocompression ? NULL : ifp))
+ ) {
+
+ //load the blocks for each buffer
+ if (frame.header->Ynum)
+ for (int j = 0; j < frame.header->Ynum; j++)
+ rmdLoadBlock( yuv->y,
+ &frame.YData[j * blockszy],
+ frame.YBlocks[j],
+ yuv->y_width,
+ yuv->y_height,
+ Y_UNIT_WIDTH);
+ if (frame.header->Unum)
+ for (int j = 0; j < frame.header->Unum; j++)
+ rmdLoadBlock( yuv->u,
+ &frame.UData[j * blockszuv],
+ frame.UBlocks[j],
+ yuv->uv_width,
+ yuv->uv_height,
+ UV_UNIT_WIDTH);
+ if (frame.header->Vnum)
+ for (int j = 0; j < frame.header->Vnum; j++)
+ rmdLoadBlock( yuv->v,
+ &frame.VData[j * blockszuv],
+ frame.VBlocks[j],
+ yuv->uv_width,
+ yuv->uv_height,
+ UV_UNIT_WIDTH);
+
+ //encode. This is not made in a thread since
+ //now blocking is not a problem
+ //and this way sync problems
+ //can be avoided more easily.
+ rmdSyncEncodeImageBuffer(pdata);
+ } else {
+ raise(SIGINT);
+ continue;
+ }
+ } else {
+ if (rmdSwapCacheFilesRead( pdata->cache_data->imgdata,
+ nth_cache,
+ &ifp,
+ &ucfp)) {
+ raise(SIGINT);
+ } else {
+ fprintf(stderr, "\t[Cache File %d]", nth_cache);
+ nth_cache++;
+ }
+ continue;
+ }
+ //audio load and encoding
+ } else {
+ if (!audio_end) {
+ int nbytes = fread(sound_data, 1, pdata->periodsize*
+ pdata->sound_framesize, afp);
+ if (nbytes <= 0)
+ audio_end = 1;
+ else
+ rmdSyncEncodeSoundBuffer(pdata, sound_data);
+ }
+ }
+ }
+
+ pthread_mutex_lock(&pdata->theora_lib_mutex);
+ pdata->th_encoding_clean = 1;
+ pthread_cond_signal(&pdata->theora_lib_clean);
+ pthread_mutex_unlock(&pdata->theora_lib_mutex);
+
+ pthread_mutex_lock(&pdata->vorbis_lib_mutex);
+ pdata->v_encoding_clean = 1;
+ pthread_cond_signal(&pdata->vorbis_lib_clean);
+ pthread_mutex_unlock(&pdata->vorbis_lib_mutex);
+ fprintf(stdout, "\n");
+
+ // Clear frame
+ free(frame.YData);
+ free(frame.UData);
+ free(frame.VData);
+
+ free(sound_data);
+
+ if (!pdata->args.nosound)
+ fclose(afp);
+
+ pthread_exit(&thread_exit);
+}
diff --git a/src/rmd_load_cache.h b/src/rmd_load_cache.h
new file mode 100644
index 0000000..917f31f
--- /dev/null
+++ b/src/rmd_load_cache.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef LOAD_CACHE
+#define LOAD_CACHE 1
+
+#include "rmd_types.h"
+
+
+/**
+* Cache loading and processing thread
+*
+* \param pdata ProgData struct containing all program data
+*
+*/
+void *rmdLoadCache(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_macro.h b/src/rmd_macro.h
new file mode 100644
index 0000000..2f1ac88
--- /dev/null
+++ b/src/rmd_macro.h
@@ -0,0 +1,83 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMDMACRO_H
+#define RMDMACRO_H 1
+
+#include "config.h"
+
+#include "rmd_types.h"
+
+#include <limits.h>
+
+
+//define which way we are reading a pixmap
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define __ABYTE 3
+#define __RBYTE 2
+#define __GBYTE 1
+#define __BBYTE 0
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+#define __ABYTE 0
+#define __RBYTE 1
+#define __GBYTE 2
+#define __BBYTE 3
+
+#else
+#error Only little-endian and big-endian systems are supported
+#endif
+
+#define __RVALUE_32(tmp_val) (((tmp_val)&0x00ff0000)>>16)
+#define __GVALUE_32(tmp_val) (((tmp_val)&0x0000ff00)>>8)
+#define __BVALUE_32(tmp_val) (((tmp_val)&0x000000ff))
+
+#define __R16_MASK 0xf800
+#define __G16_MASK 0x7e0
+#define __B16_MASK 0x1f
+
+#define __RVALUE_16(tmp_val) ((((tmp_val)&__R16_MASK)>>11)*8)
+#define __GVALUE_16(tmp_val) ((((tmp_val)&__G16_MASK)>>5)*4)
+#define __BVALUE_16(tmp_val) ((((tmp_val)&__B16_MASK))*8)
+
+//xfixes pointer data are written as unsigned long
+//(even though the server returns CARD32)
+//so we need to set the step accordingly to
+//avoid problems (amd64 has 8byte ulong)
+#define RMD_ULONG_SIZE_T (sizeof(unsigned long))
+
+//The width, in bytes, of the blocks
+//on which the y,u and v planes are broken.
+//These blocks are square.
+#define Y_UNIT_WIDTH 0x0010
+#define UV_UNIT_WIDTH 0x0008
+
+#define MAX(a, b) ((a) < (b)) ? (b) : (a)
+#define MIN(a, b) ((a) > (b)) ? (b) : (a)
+
+#endif
+
diff --git a/src/rmd_make_dummy_pointer.c b/src/rmd_make_dummy_pointer.c
new file mode 100644
index 0000000..25bb027
--- /dev/null
+++ b/src/rmd_make_dummy_pointer.c
@@ -0,0 +1,94 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_make_dummy_pointer.h"
+
+#include "rmd_macro.h"
+#include "rmd_types.h"
+
+#include <X11/Xlib.h>
+#include <stdlib.h>
+
+unsigned char *rmdMakeDummyPointer( Display *display,
+ DisplaySpecs *specs,
+ int size,
+ int color,
+ int type,
+ unsigned char *npxl) {
+
+ unsigned long bpixel = XBlackPixel(display, specs->screen);
+ unsigned long wpixel = XWhitePixel(display, specs->screen);
+ int i, k, o = '.';
+ unsigned long b = color ? 'w' : 'b', w = color ? 'b' : 'w';
+ char pmask[1][16][16] = {{
+ {w,w,o,o,o,o,o,o,o,o,o,o,o,o,o,o},
+ {w,b,w,o,o,o,o,o,o,o,o,o,o,o,o,o},
+ {w,b,b,w,o,o,o,o,o,o,o,o,o,o,o,o},
+ {w,b,b,b,w,o,o,o,o,o,o,o,o,o,o,o},
+ {w,b,b,b,b,w,o,o,o,o,o,o,o,o,o,o},
+ {w,b,b,b,b,b,w,o,o,o,o,o,o,o,o,o},
+ {w,b,b,b,b,b,b,w,o,o,o,o,o,o,o,o},
+ {w,b,b,b,b,b,b,b,w,o,o,o,o,o,o,o},
+ {w,b,b,b,b,b,b,b,b,w,o,o,o,o,o,o},
+ {w,b,b,b,b,b,w,w,w,w,o,o,o,o,o,o},
+ {w,b,b,b,b,b,w,o,o,o,o,o,o,o,o,o},
+ {w,b,b,w,w,b,b,w,o,o,o,o,o,o,o,o},
+ {w,b,w,o,w,b,b,w,o,o,o,o,o,o,o,o},
+ {w,w,o,o,o,w,b,b,w,o,o,o,o,o,o,o},
+ {o,o,o,o,o,w,b,b,w,o,o,o,o,o,o,o},
+ {o,o,o,o,o,o,w,w,o,o,o,o,o,o,o,o}}
+ };
+ unsigned char *ret = malloc(size * sizeof(char[size * 4]));
+ unsigned char wp[4] = {
+ (wpixel ^ 0xff000000) >> 24,
+ (wpixel ^ 0x00ff0000) >> 16,
+ (wpixel ^ 0x0000ff00) >> 8,
+ (wpixel ^ 0x000000ff)
+ };
+ unsigned char bp[4] = {
+ (bpixel ^ 0xff000000) >> 24,
+ (bpixel ^ 0x00ff0000) >> 16,
+ (bpixel ^ 0x0000ff00) >> 8,
+ (bpixel ^ 0x000000ff)
+ };
+
+ *npxl = ((wp[0] - 1) != bp[0]) ? wp[0] - 100 : wp[0] - 102;
+ for (i = 0; i < size; i++) {
+ for (k = 0; k < size; k++) {
+ ret[(i * size + k) * 4 + __ABYTE] = (pmask[type][i][k] == 'w') ?
+ wp[0]:(pmask[type][i][k] == 'b') ? bp[0] : *npxl;
+ ret[(i * size + k) * 4 + __RBYTE] = (pmask[type][i][k] == 'w') ?
+ wp[1]:(pmask[type][i][k] == 'b') ? bp[1] : *npxl;
+ ret[(i * size + k) * 4 + __GBYTE] = (pmask[type][i][k] == 'w') ?
+ wp[2]:(pmask[type][i][k] == 'b') ? bp[2] : *npxl;
+ ret[(i * size + k) * 4 + __BBYTE] = (pmask[type][i][k] == 'w') ?
+ wp[3]:(pmask[type][i][k] == 'b') ? bp[3] : *npxl;
+ }
+ }
+
+ return ret;
+}
diff --git a/src/rmd_make_dummy_pointer.h b/src/rmd_make_dummy_pointer.h
new file mode 100644
index 0000000..3eccd42
--- /dev/null
+++ b/src/rmd_make_dummy_pointer.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef MAKE_DUMMY_POINTER_H
+#define MAKE_DUMMY_POINTER_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Create an array containing the data for the dummy pointer
+*
+* \param specs DisplaySpecs struct with
+* information about the display to be recorded
+*
+* \param size Pointer size, always square, always 16.(exists only
+* for the possibility to create more dummy cursors)
+* \param color 0 white, 1 black
+*
+* \param type Always 0.(exists only for the possibility to create
+* more dummy cursors)
+*
+* \param npxl Return of pixel value that denotes non-drawing, while
+* applying the cursor on the target image
+*
+* \returns Pointer to pixel data of the cursor
+*/
+unsigned char *rmdMakeDummyPointer(Display *display,
+ DisplaySpecs *specs,
+ int size,
+ int color,
+ int type,
+ unsigned char *npxl);
+
+
+#endif
diff --git a/src/rmd_math.c b/src/rmd_math.c
new file mode 100644
index 0000000..1cf3d29
--- /dev/null
+++ b/src/rmd_math.c
@@ -0,0 +1,33 @@
+/******************************************************************************
+* recordMyDesktop - rmd_math.c *
+*******************************************************************************
+* *
+* Copyright (C) 2008 John Varouhakis *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_math.h"
+
+#include <math.h>
+
+double rmdRoundf(double val) {
+ return val < 0.0 ? ceilf(val - .5) : floorf(val + 0.5);
+}
diff --git a/src/rmd_math.h b/src/rmd_math.h
new file mode 100644
index 0000000..87c6166
--- /dev/null
+++ b/src/rmd_math.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+* recordMyDesktop - rmd_math.h *
+*******************************************************************************
+* *
+* Copyright (C) 2008 John Varouhakis *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_MATH_H
+#define RMD_MATH_H 1
+
+/*
+ * Since roundf depends on C99, using it might make
+ * the code non-portable. rmdRoundf solves this
+ * problem, by behaving identically with roundf
+ * and being portable (floorf and ceilf, that are
+ * used in the implementation, are defined in C89)
+ *
+ * \param val Number to be rounded
+ *
+ * \returns val rounded
+ *
+ */
+double rmdRoundf( double val );
+
+#endif
+
diff --git a/src/rmd_opendev.c b/src/rmd_opendev.c
new file mode 100644
index 0000000..37fb33c
--- /dev/null
+++ b/src/rmd_opendev.c
@@ -0,0 +1,189 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_opendev.h"
+
+#ifdef HAVE_LIBASOUND
+ #include <alsa/asoundlib.h>
+#else
+ #include <sys/ioctl.h>
+ #include <sys/soundcard.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+#ifdef HAVE_LIBASOUND
+
+snd_pcm_t *rmdOpenDev( const char *pcm_dev,
+ unsigned int *channels,
+ unsigned int *frequency,
+ snd_pcm_uframes_t *buffsize,
+ snd_pcm_uframes_t *periodsize,
+ unsigned int *periodtime,
+ int *hard_pause) {
+
+ snd_pcm_t *mhandle;
+ snd_pcm_hw_params_t *hwparams;
+ unsigned int periods = 2;
+ unsigned int exactrate = *frequency;
+
+ // The compiler might warn us because the expansion starts with
+ // assert(&hwparams)
+ snd_pcm_hw_params_alloca(&hwparams);
+
+ if (snd_pcm_open(&mhandle, pcm_dev, SND_PCM_STREAM_CAPTURE, SND_PCM_ASYNC) < 0) {
+ fprintf(stderr, "Couldn't open PCM device %s\n", pcm_dev);
+ return NULL;
+ } else
+ fprintf(stderr, "Opened PCM device %s\n", pcm_dev);
+
+ if (snd_pcm_hw_params_any(mhandle, hwparams) < 0) {
+ fprintf(stderr, "Couldn't configure PCM device.\n");
+ return NULL;
+ }
+
+ if (snd_pcm_hw_params_set_access(mhandle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
+ fprintf(stderr, "Couldn't set access.\n");
+ return NULL;
+ }
+
+ if (snd_pcm_hw_params_set_format(mhandle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) {
+ fprintf(stderr, "Couldn't set format.\n");
+ return NULL;
+ }
+
+ if (snd_pcm_hw_params_set_rate_near(mhandle, hwparams, &exactrate, 0) < 0) {
+ fprintf(stderr, "Couldn't set frequency.\n");
+ return NULL;
+ }
+
+ if (*frequency != exactrate) {
+ fprintf(stderr, "Playback frequency %dHz is not available...\n"
+ "Using %dHz instead.\n",
+ *frequency, exactrate);
+
+ *frequency = exactrate;
+ }
+
+ if (snd_pcm_hw_params_set_channels_near(mhandle, hwparams, channels) < 0) {
+ fprintf(stderr, "Couldn't set channels number.\n");
+ return NULL;
+ }
+
+ if (*channels>2) {
+ fprintf(stderr, "Channels number should be 1(mono) or 2(stereo).\n");
+ return NULL;
+ }
+
+ if (snd_pcm_hw_params_set_periods_near(mhandle, hwparams, &periods, 0) < 0) {
+ fprintf(stderr, "Couldn't set periods.\n");
+ return NULL;
+ }
+
+ if (snd_pcm_hw_params_set_buffer_size_near(mhandle, hwparams, buffsize) < 0) {
+ fprintf(stderr, "Couldn't set buffer size.\n");
+ return NULL;
+ }
+
+ if (snd_pcm_hw_params(mhandle, hwparams) < 0) {
+ fprintf(stderr, "Couldn't set hardware parameters.\n");
+ return NULL;
+ }
+
+ if (hard_pause != NULL) {
+ if (!snd_pcm_hw_params_can_pause(hwparams))
+ *hard_pause = 1;
+ }
+
+ if (periodsize != NULL)
+ snd_pcm_hw_params_get_period_size(hwparams, periodsize, 0);
+
+ if (periodtime != NULL)
+ snd_pcm_hw_params_get_period_time(hwparams, periodtime, 0);
+
+ fprintf(stderr,
+ "Recording on device %s is set to:\n%d channels at %dHz\n",
+ pcm_dev, *channels, *frequency);
+
+ snd_pcm_prepare(mhandle);
+
+ return mhandle;
+}
+
+#else
+
+int rmdOpenDev(const char *pcm_dev, unsigned int channels, unsigned int frequency) {
+ int fd ;
+ unsigned int value;
+
+ fd = open(pcm_dev, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (ioctl(fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
+ fprintf(stderr, "Couldn't get audio format list\n");
+ return -1;
+ }
+
+ if (value & AFMT_S16_LE) {
+ value = AFMT_S16_LE;
+ } else if (value & AFMT_S16_BE) {
+ value = AFMT_S16_BE;
+ } else {
+ fprintf(stderr, "Soundcard doesn't support signed 16-bit-data\n");
+ return -1;
+ }
+
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &value) < 0) {
+ fprintf(stderr, "Couldn't set audio format\n" );
+ return -1;
+ }
+
+ value = channels;
+ if (ioctl(fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
+ fprintf(stderr, "Cannot set the number of channels\n" );
+ return -1;
+ }
+
+ value = frequency;
+ if (ioctl(fd, SNDCTL_DSP_SPEED, &value) < 0) {
+ fprintf(stderr, "Couldn't set audio frequency\n" );
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK) < 0) {
+ fprintf(stderr, "Couldn't set audio blocking mode\n" );
+ return -1;
+ }
+
+ return fd;
+}
+#endif
diff --git a/src/rmd_opendev.h b/src/rmd_opendev.h
new file mode 100644
index 0000000..06dfc64
--- /dev/null
+++ b/src/rmd_opendev.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef OPENDEV_H
+#define OPENDEV_H 1
+
+#include "rmd_types.h"
+
+
+#ifdef HAVE_LIBASOUND
+/**
+* Try to open (alsa) sound device, with the desired parameters,
+* and place the obtained ones on their place
+*
+* \param pcm_dev name of the device
+*
+* \param channels desired number of channels
+* (gets modified with the acieved value)
+*
+* \param frequency desired frequency(gets modified with the acieved value)
+*
+* \param buffsize Size of buffer
+*
+* \param periodsize Size of a period(can be NULL)
+*
+* \param periodtime Duration of a period(can be NULL)
+*
+* \param hardpause Set to 1 when the device has to be stopped during pause
+* and to 0 when it supports pausing
+* (can be NULL)
+*
+* \returns snd_pcm_t handle on success, NULL on failure
+*/
+snd_pcm_t *rmdOpenDev(const char *pcm_dev,
+ unsigned int *channels,
+ unsigned int *frequency,
+ snd_pcm_uframes_t *buffsize,
+ snd_pcm_uframes_t *periodsize,
+ unsigned int *periodtime,
+ int *hardpause);
+#else
+/**
+* Try to open (OSS) sound device, with the desired parameters.
+*
+*
+* \param pcm_dev name of the device
+*
+* \param channels desired number of channels
+*
+* \param frequency desired frequency
+*
+*
+* \returns file descriptor of open device,-1 on failure
+*/
+int rmdOpenDev(const char *pcm_dev,
+ unsigned int channels,
+ unsigned int frequency);
+#endif
+
+
+#endif
diff --git a/src/rmd_parseargs.c b/src/rmd_parseargs.c
new file mode 100644
index 0000000..12d2cd4
--- /dev/null
+++ b/src/rmd_parseargs.c
@@ -0,0 +1,511 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* Copyright (C) 2009 Martin Nordholts *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_parseargs.h"
+
+#include "rmd_types.h"
+
+#include <popt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#ifdef HAVE_LIBJACK
+#define RMD_LIBJACK_STATUS "Enabled"
+#define RMD_USE_JACK_EXTRA_FLAG 0
+#else
+#define RMD_LIBJACK_STATUS "Disabled"
+#define RMD_USE_JACK_EXTRA_FLAG POPT_ARGFLAG_DOC_HIDDEN
+#endif
+
+#ifdef HAVE_LIBASOUND
+#define RMD_LIBASOUND_STATUS "ALSA"
+#else
+#define RMD_LIBASOUND_STATUS "OSS"
+#endif
+
+#define RMD_ARG_DELAY 1
+#define RMD_ARG_DUMMY_CURSOR 2
+#define RMD_ARG_USE_JACK 3
+
+#define RMD_OPTION_TABLE(name, table) \
+ { NULL, '\0', \
+ POPT_ARG_INCLUDE_TABLE, (table), \
+ 0, (name), NULL }
+
+
+static void rmdPrintAndExit(poptContext con, enum poptCallbackReason reason, const struct poptOption *opt, const char *arg, const void *data);
+static boolean rmdValidateArguments(const ProgArgs *args);
+
+
+boolean rmdParseArgs(int argc, char **argv, ProgArgs *arg_return) {
+ poptContext popt_context = NULL;
+ boolean no_cursor = FALSE;
+ boolean quick_subsampling = FALSE;
+ boolean compress_cache = FALSE;
+ boolean success = TRUE;
+ int arg_id = 0;
+
+ // Setup the option tables
+ struct poptOption generic_options[] = {
+ { NULL, '\0',
+ POPT_ARG_CALLBACK, (void *)rmdPrintAndExit, 0,
+ NULL, NULL },
+
+ { "help", 'h',
+ POPT_ARG_NONE, NULL, 0,
+ "Print this help and exit.",
+ NULL },
+
+ { "version", '\0',
+ POPT_ARG_NONE, NULL, 0,
+ "Print program version and exit.",
+ NULL },
+
+ { "print-config", '\0',
+ POPT_ARG_NONE, NULL, 0,
+ "Print info about options selected during compilation and exit.",
+ NULL },
+
+ POPT_TABLEEND };
+
+ struct poptOption image_options[] = {
+ { "windowid", '\0',
+ POPT_ARG_INT, &arg_return->windowid, 0,
+ "id of window to be recorded.",
+ "id_of_window" },
+
+ { "display", '\0',
+ POPT_ARG_STRING, &arg_return->display, 0,
+ "Display to connect to.",
+ "DISPLAY" },
+
+ { "x", 'x',
+ POPT_ARG_INT, &arg_return->x, 0,
+ "Offset in x direction.",
+ "N>=0" },
+
+ { "y", 'y',
+ POPT_ARG_INT, &arg_return->y, 0,
+ "Offset in y direction.",
+ "N>=0" },
+
+ { "width", '\0',
+ POPT_ARG_INT, &arg_return->width, 0,
+ "Width of recorded window.",
+ "N>0" },
+
+ { "height", '\0',
+ POPT_ARG_INT, &arg_return->height, 0,
+ "Height of recorded window.",
+ "N>0" },
+
+ { "dummy-cursor", '\0',
+ POPT_ARG_STRING, NULL, RMD_ARG_DUMMY_CURSOR,
+ "Color of the dummy cursor [black|white]",
+ "color" },
+
+ { "no-cursor", '\0',
+ POPT_ARG_NONE, &no_cursor, 0,
+ "Disable drawing of the cursor.",
+ NULL },
+
+ { "no-shared", '\0',
+ POPT_ARG_NONE, &arg_return->noshared, 0,
+ "Disable usage of MIT-shared memory extension(Not Recommended!).",
+ NULL },
+
+ { "full-shots", '\0',
+ POPT_ARG_NONE, &arg_return->full_shots, 0,
+ "Take full screenshot at every frame(Not recomended!).",
+ NULL },
+
+ { "follow-mouse", '\0',
+ POPT_ARG_NONE, &arg_return->follow_mouse, 0,
+ "Makes the capture area follow the mouse cursor. Autoenables --full-shots.",
+ NULL },
+
+ { "quick-subsampling", '\0',
+ POPT_ARG_NONE, &quick_subsampling, 0,
+ "Do subsampling of the chroma planes by discarding, not averaging.",
+ NULL },
+
+ { "fps", '\0',
+ POPT_ARG_FLOAT, &arg_return->fps, 0,
+ "A positive number denoting desired framerate.",
+ "N(number>0.0)" },
+
+ POPT_TABLEEND };
+
+ struct poptOption sound_options[] = {
+ { "channels", '\0',
+ POPT_ARG_INT, &arg_return->channels, 0,
+ "A positive number denoting desired sound channels in recording.",
+ "N" },
+
+ { "freq", '\0',
+ POPT_ARG_INT, &arg_return->frequency, 0,
+ "A positive number denoting desired sound frequency.",
+ "N" },
+
+ { "buffer-size", '\0',
+ POPT_ARG_INT, &arg_return->buffsize, 0,
+ "A positive number denoting the desired sound buffer size (in frames, when using ALSA or OSS)",
+ "N" },
+
+ { "ring-buffer-size", '\0',
+ POPT_ARG_FLOAT, &arg_return->jack_ringbuffer_secs, 0,
+ "A float number denoting the desired ring buffer size (in seconds, when using JACK only).",
+ "N" },
+
+ { "device", '\0',
+ POPT_ARG_STRING, &arg_return->device, 0,
+ "Sound device(default " DEFAULT_AUDIO_DEVICE ").",
+ "SOUND_DEVICE" },
+
+ { "use-jack", '\0',
+ POPT_ARG_STRING | RMD_USE_JACK_EXTRA_FLAG, &arg_return->x, RMD_ARG_USE_JACK,
+ "Record audio from the specified list of space-separated jack ports.",
+ "port1 port2... portn" },
+
+ { "no-sound", '\0',
+ POPT_ARG_NONE, &arg_return->nosound, 0,
+ "Do not record sound.",
+ NULL },
+
+ POPT_TABLEEND };
+
+ struct poptOption encoding_options[] = {
+ { "on-the-fly-encoding", '\0',
+ POPT_ARG_NONE, &arg_return->encOnTheFly, 0,
+ "Encode the audio-video data, while recording.",
+ NULL },
+
+ { "v_quality", '\0',
+ POPT_ARG_INT, &arg_return->v_quality, 0,
+ "A number from 0 to 63 for desired encoded video quality(default 63).",
+ "n" },
+
+ { "v_bitrate", '\0',
+ POPT_ARG_INT, &arg_return->v_bitrate, 0,
+ "A number from 0 to 2000000 for desired encoded video bitrate(default 0).",
+ "n" },
+
+ { "s_quality", '\0',
+ POPT_ARG_INT, &arg_return->s_quality, 0,
+ "Desired audio quality(-1 to 10).",
+ "n" },
+
+ POPT_TABLEEND };
+
+ struct poptOption misc_options[] = {
+ { "rescue", '\0',
+ POPT_ARG_STRING, &arg_return->rescue_path, 0,
+ "Encode data from a previous, crashed, session.",
+ "path_to_data" },
+
+ { "no-encode", '\0',
+ POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, &arg_return->no_encode, 0,
+ "Do not encode any data after recording is complete. This is instead done manually afterwards with --rescue.",
+ NULL },
+
+ { "no-wm-check", '\0',
+ POPT_ARG_NONE, &arg_return->nowmcheck, 0,
+ "Do not try to detect the window manager(and set options according to it)",
+ NULL },
+
+ { "no-frame", '\0',
+ POPT_ARG_NONE, &arg_return->noframe, 0,
+ "Don not show the frame that visualizes the recorded area.",
+ NULL },
+
+ { "pause-shortcut", '\0',
+ POPT_ARG_STRING, &arg_return->pause_shortcut, 0,
+ "Shortcut that will be used for (un)pausing (default Control+Mod1+p).",
+ "MOD+KEY" },
+
+ { "stop-shortcut", '\0',
+ POPT_ARG_STRING, &arg_return->stop_shortcut, 0,
+ "Shortcut that will be used to stop the recording (default Control+Mod1+s).",
+ "MOD+KEY" },
+
+ { "compress-cache", '\0',
+ POPT_ARG_NONE, &compress_cache, 0,
+ "Image data are cached with light compression.",
+ NULL },
+
+ { "workdir", '\0',
+ POPT_ARG_STRING, &arg_return->workdir, 0,
+ "Location where a temporary directory will be created to hold project files(default $HOME).",
+ "DIR" },
+
+ { "delay", '\0',
+ POPT_ARG_STRING, &arg_return->delay, RMD_ARG_DELAY,
+ "Number of secs(default), minutes or hours before capture starts(number can be float)",
+ "n[H|h|M|m]" },
+
+ { "overwrite", '\0',
+ POPT_ARG_NONE, &arg_return->overwrite, 0,
+ "If there is already a file with the same name, delete it (default is to add a number postfix to the new one).",
+ NULL },
+
+ { "output", 'o',
+ POPT_ARG_STRING, &arg_return->filename, 0,
+ "Name of recorded video(default out.ogv).",
+ "filename" },
+
+ POPT_TABLEEND };
+
+ struct poptOption rmd_args[] = {
+ RMD_OPTION_TABLE("Generic Options", generic_options),
+ RMD_OPTION_TABLE("Image Options", image_options),
+ RMD_OPTION_TABLE("Sound Options", sound_options),
+ RMD_OPTION_TABLE("Encoding Options", encoding_options),
+ RMD_OPTION_TABLE("Misc Options", misc_options),
+ POPT_TABLEEND };
+
+
+ // Some trivial setup
+ popt_context = poptGetContext(NULL, argc, (const char **)argv, rmd_args, 0);
+ poptSetOtherOptionHelp(popt_context, "[OPTIONS]^filename");
+
+
+ // Parse the arguments
+ while ((arg_id = poptGetNextOpt(popt_context)) > 0) {
+ char *arg = poptGetOptArg(popt_context);
+
+ // Most arguments are handled completely by libpopt but we
+ // handle some by ourself, namely those in this switch case
+ switch (arg_id) {
+ case RMD_ARG_DELAY:
+ {
+ float num = atof(arg);
+
+ if (num > 0.0) {
+ int k;
+ for (k = 0; k < strlen(arg); k++) {
+ if ((arg[k] == 'M') || (arg[k] == 'm')) {
+ num *= 60.0;
+ break;
+ }
+ else if ((arg[k] == 'H') || (arg[k] == 'h')) {
+ num *= 3600.0;
+ break;
+ }
+ }
+ arg_return->delay = (int) num;
+ } else {
+ fprintf(stderr,
+ "Argument Usage: --delay n[H|h|M|m]\n"
+ "where n is a float number\n");
+ success = FALSE;
+ }
+ }
+ break;
+
+ case RMD_ARG_DUMMY_CURSOR:
+ {
+ if (!strcmp(arg, "white"))
+ arg_return->cursor_color = 0;
+ else if (!strcmp(arg, "black"))
+ arg_return->cursor_color = 1;
+ else {
+ fprintf(stderr,
+ "Argument Usage:\n"
+ " --dummy-cursor [black|white]\n");
+ success = FALSE;
+ }
+ arg_return->have_dummy_cursor = TRUE;
+ arg_return->xfixes_cursor = FALSE;
+ }
+ break;
+
+ case RMD_ARG_USE_JACK:
+ {
+ arg_return->jack_nports = 0;
+
+ while (arg) {
+
+ arg_return->jack_nports++;
+
+ arg_return->jack_port_names[arg_return->jack_nports - 1] = malloc(strlen(arg) + 1);
+ strcpy(arg_return->jack_port_names[arg_return->jack_nports - 1], arg);
+
+ arg = poptGetOptArg(popt_context);
+ }
+
+ if (arg_return->jack_nports > 0) {
+ arg_return->use_jack = 1;
+ } else {
+ fprintf(stderr,
+ "Argument Usage: --use-jack port1 port2... portn\n");
+ success = FALSE;
+ }
+
+ if (RMD_USE_JACK_EXTRA_FLAG == POPT_ARGFLAG_DOC_HIDDEN) {
+ fprintf(stderr,
+ "Warning, will ignore --use-jack flags, no libjack support in build.\n");
+ }
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (arg_id == -1) {
+ // Parsing is complete, perform final adjustments
+ if (no_cursor)
+ arg_return->xfixes_cursor = FALSE;
+
+ if (quick_subsampling)
+ arg_return->no_quick_subsample = FALSE;
+
+ if (compress_cache)
+ arg_return->zerocompression = FALSE;
+
+ if (arg_return->follow_mouse)
+ arg_return->full_shots = TRUE;
+
+ if (!arg_return->filename)
+ arg_return->filename = strdup(poptGetArg(popt_context));
+ } else {
+ // Parsing error, say what went wrong
+ const char *str = poptBadOption(popt_context, 0);
+
+ success = FALSE;
+
+ fprintf(stderr,
+ "Error when parsing `%s': ", str);
+
+ switch (arg_id) {
+ case POPT_ERROR_NOARG:
+ fprintf(stderr, "Missing argument\n");
+ break;
+
+ case POPT_ERROR_BADNUMBER:
+ fprintf(stderr, "Bad number\n");
+ break;
+
+ default:
+ fprintf(stderr, "libpopt error: %d\n", arg_id);
+ break;
+ }
+ }
+
+ // Make sure argument ranges are valid
+ success = success && rmdValidateArguments(arg_return);
+
+ // Clean up
+ poptFreeContext(popt_context);
+
+ return success;
+}
+
+static boolean rmdValidateArguments(const ProgArgs *args)
+{
+ boolean success = TRUE;
+
+ if (args->x < 0) {
+ fprintf(stdout, "-x must not be less than 0.\n");
+ success = FALSE;
+ }
+ if (args->y < 0) {
+ fprintf(stdout, "-y must not be less than 0.\n");
+ success = FALSE;
+ }
+ if (args->width < 0) {
+ fprintf(stdout, "--width must be larger than 0.\n");
+ success = FALSE;
+ }
+ if (args->height < 0) {
+ fprintf(stdout, "--height must be larger than 0.\n");
+ success = FALSE;
+ }
+ if (args->fps <= 0) {
+ fprintf(stdout, "--fps must be larger than 0.\n");
+ success = FALSE;
+ }
+ if (args->v_quality < 0 || args->v_quality > 63) {
+ fprintf(stdout, "--v_quality must be within the inclusive range [0-63].\n");
+ success = FALSE;
+ }
+ if (args->v_bitrate < 0 || args->v_quality > 2000000) {
+ fprintf(stdout, "--v_bitrate must be within the inclusive range [0-2000000].\n");
+ success = FALSE;
+ }
+ if (args->frequency <= 0) {
+ fprintf(stdout, "--frequency must be larger than 0.\n");
+ success = FALSE;
+ }
+ if (args->channels <= 0) {
+ fprintf(stdout, "--channels must be larger than 0.\n");
+ success = FALSE;
+ }
+ if (args->buffsize <= 0) {
+ fprintf(stdout, "--buffer-size must be larger than 0.\n");
+ success = FALSE;
+ }
+ if (args->jack_ringbuffer_secs <= 0) {
+ fprintf(stdout, "--jack-buffer-size must be larger than 0.\n");
+ success = FALSE;
+ }
+
+ return success;
+}
+
+static void rmdPrintAndExit( poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg,
+ const void *data)
+{
+ if (strcmp(opt->longName, "version") == 0) {
+ fprintf(stderr, "recordMyDesktop v%s\n\n", VERSION);
+ } else if (strcmp(opt->longName, "help") == 0) {
+ poptPrintHelp(con, stdout, 0);
+ fprintf(stdout,
+ "\n"
+ "\tIf no other options are specified, filename can be given without the -o switch.\n"
+ "\n"
+ "\n");
+ } else if (strcmp(opt->longName, "print-config") == 0) {
+ fprintf(stdout,
+ "\n"
+ "recordMyDesktop was compiled with the following options:\n"
+ "\n"
+ "Jack:\t\t\t" RMD_LIBJACK_STATUS "\n"
+ "Default Audio Backend:\t" RMD_LIBASOUND_STATUS "\n"
+ "\n"
+ "\n");
+ }
+
+ exit(0);
+}
diff --git a/src/rmd_parseargs.h b/src/rmd_parseargs.h
new file mode 100644
index 0000000..42f2bac
--- /dev/null
+++ b/src/rmd_parseargs.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef PARSEARGS_H
+#define PARSEARGS_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Fill ProgArgs struct with arguments entered at execution
+*
+* \param argc argc as entered from main
+*
+* \param argv argv as entered from main
+*
+* \param arg_return ProgArgs struct to be filled with the options
+*
+* \returns 0 on Success 1 on Failure
+*/
+boolean rmdParseArgs(int argc, char **argv, ProgArgs *arg_return);
+
+
+#endif
diff --git a/src/rmd_poll_events.c b/src/rmd_poll_events.c
new file mode 100644
index 0000000..49f2478
--- /dev/null
+++ b/src/rmd_poll_events.c
@@ -0,0 +1,245 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_poll_events.h"
+
+#include "rmd_frame.h"
+#include "rmd_macro.h"
+#include "rmd_rectinsert.h"
+#include "rmd_types.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/Xdamage.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <pthread.h>
+
+
+static int clip_event_area(XDamageNotifyEvent *e, XRectangle *cliprect, XRectangle *res)
+{
+
+#if 0
+ printf("got area %x,%x %x,%x\n",
+ e->area.x,
+ e->area.y,
+ e->area.width,
+ e->area.height);
+#endif
+
+ if ( e->area.x <= cliprect->x &&
+ e->area.y <= cliprect->y &&
+ e->area.width >= cliprect->width &&
+ e->area.height >= cliprect->height) {
+
+ /* area completely covers cliprect, cliprect becomes the area */
+ res->x = cliprect->x;
+ res->y = cliprect->y;
+ res->width = cliprect->width;
+ res->height = cliprect->height;
+
+ } else if ( e->area.x + e->area.width < cliprect->x ||
+ e->area.x + e->area.width > cliprect->x + cliprect->width ||
+ e->area.y + e->area.height < cliprect->y ||
+ e->area.y + e->area.height > cliprect->y + cliprect->height) {
+
+ /* area has at least one axis with zero overlap, so they can't overlap */
+ return 0;
+
+ } else {
+
+ /* areas partially overlap */
+ res->x = MAX(e->area.x, cliprect->x);
+ res->width = MIN(e->area.x + e->area.width, cliprect->x + cliprect->width) - res->x;
+
+ res->y = MAX(e->area.y, cliprect->y);
+ res->height = MIN(e->area.y + e->area.height, cliprect->y + cliprect->height) - res->y;
+
+ if (!res->width || !res->height)
+ return 0;
+ }
+
+#if 0
+ printf("clipped to %x,%x %x,%x\n",
+ res->x,
+ res->y,
+ res->width,
+ res->height);
+#endif
+
+ return 1;
+}
+
+
+/* Try align xrect to even boundaries relative to cliprect,
+ * this is done for the UV routines which operate at 2x2 rgb pixel granularity.
+ */
+static void uv_align(XRectangle *cliprect, XRectangle *xrect) {
+ XRectangle rel;
+
+ rel.x = xrect->x - cliprect->x;
+ rel.y = xrect->y - cliprect->y;
+
+ if (rel.x % 2) {
+ rel.x -= 1;
+ rel.width = xrect->width + 1;
+ } else {
+ rel.width = xrect->width;
+ }
+
+ if (rel.y % 2) {
+ rel.y -= 1;
+ rel.height = xrect->height + 1;
+ } else {
+ rel.height = xrect->height;
+ }
+
+ /* XXX: note when cliprect prevents an even width, we can't do anything
+ * about it. rmd could force even-sized recording windows to prevent
+ * this, but that would be kind of annoying. For now it's assumed
+ * the UV routines will handle the exception - which might temporarily
+ * mean they just lop the odd row/column off the edge.
+ */
+ if (rel.width % 2 && rel.width + rel.x < cliprect->x + cliprect->width)
+ rel.width++;
+
+ if (rel.height % 2 && rel.height + rel.y < cliprect->y + cliprect->height)
+ rel.height++;
+}
+
+
+void rmdInitEventsPolling(ProgData *pdata) {
+ Window root_return,
+ parent_return,
+ *children;
+ unsigned int i,
+ nchildren;
+
+ XSelectInput (pdata->dpy, pdata->specs.root, SubstructureNotifyMask);
+
+ if (!pdata->args.full_shots) {
+ XQueryTree ( pdata->dpy,
+ pdata->specs.root,
+ &root_return,
+ &parent_return,
+ &children,
+ &nchildren);
+
+ for (i = 0; i < nchildren; i++) {
+ XWindowAttributes attribs;
+ if (XGetWindowAttributes (pdata->dpy, children[i], &attribs)) {
+ if (!attribs.override_redirect && attribs.depth == pdata->specs.depth)
+ XDamageCreate( pdata->dpy,
+ children[i],
+ XDamageReportRawRectangles);
+ }
+ }
+ XFree(children);
+ XDamageCreate( pdata->dpy,
+ pdata->specs.root,
+ XDamageReportRawRectangles);
+ }
+}
+
+
+void rmdEventLoop(ProgData *pdata) {
+ int inserts = 0;
+
+ XEvent event;
+
+ while (XPending(pdata->dpy)) {
+ XNextEvent(pdata->dpy, &event);
+ if (event.type == KeyPress) {
+ XKeyEvent *e = (XKeyEvent *)(&event);
+ if (e->keycode == pdata->pause_key.key) {
+ int i = 0;
+ int found = 0;
+ for (i = 0; i < pdata->pause_key.modnum; i++) {
+ if (pdata->pause_key.mask[i] == e->state) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ raise(SIGUSR1);
+ continue;
+ }
+ }
+ if (e->keycode == pdata->stop_key.key) {
+ int i = 0;
+ int found = 0;
+ for (i = 0; i < pdata->stop_key.modnum; i++) {
+ if (pdata->stop_key.mask[i] == e->state) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ raise(SIGINT);
+ continue;
+ }
+ }
+ } else if (event.type == Expose) {
+
+ if (event.xexpose.count != 0)
+ continue;
+ else if (!pdata->args.noframe) {
+ rmdDrawFrame( pdata->dpy,
+ pdata->specs.screen,
+ pdata->shaped_w,
+ pdata->brwin.rrect.width,
+ pdata->brwin.rrect.height);
+ }
+
+ } else if (!pdata->args.full_shots) {
+ if (event.type == MapNotify ) {
+ XWindowAttributes attribs;
+
+ if (!((XMapEvent *)(&event))->override_redirect&&
+ XGetWindowAttributes( pdata->dpy,
+ event.xcreatewindow.window,
+ &attribs)) {
+
+ if (!attribs.override_redirect && attribs.depth == pdata->specs.depth)
+ XDamageCreate( pdata->dpy,
+ event.xcreatewindow.window,
+ XDamageReportRawRectangles);
+ }
+ } else if (event.type == pdata->damage_event + XDamageNotify ) {
+ XDamageNotifyEvent *e = (XDamageNotifyEvent *)&event;
+ XRectangle xrect;
+
+ if (clip_event_area(e, &pdata->brwin.rrect, &xrect)) {
+ uv_align(&pdata->brwin.rrect, &xrect);
+ inserts += rmdRectInsert(&pdata->rect_root, &xrect);
+ }
+ }
+ }
+ }
+}
diff --git a/src/rmd_poll_events.h b/src/rmd_poll_events.h
new file mode 100644
index 0000000..a1902cb
--- /dev/null
+++ b/src/rmd_poll_events.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef POLL_EVENTS_H
+#define POLL_EVENTS_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Start listening to damage and substructure notify events
+* (needed before rmdEventLoop call)
+* \param pdata ProgData struct containing all program data
+*/
+void rmdInitEventsPolling(ProgData *pdata);
+
+/**
+* Loop calling XNextEvent.Retrieve and place on
+* list damage events that arive, create damage for new windows
+* and pickup key events for shortcuts.
+* \param pdata ProgData struct containing all program data
+*/
+void rmdEventLoop(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_queryextensions.c b/src/rmd_queryextensions.c
new file mode 100644
index 0000000..4484866
--- /dev/null
+++ b/src/rmd_queryextensions.c
@@ -0,0 +1,88 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_queryextensions.h"
+
+#include "rmd_types.h"
+
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/Xdamage.h>
+#include <stdlib.h>
+
+
+
+void rmdQueryExtensions( Display *dpy,
+ ProgArgs *args,
+ int *damage_event,
+ int *damage_error,
+ int *shm_opcode) {
+ int xf_event_basep,
+ xf_error_basep,
+ shm_event_base,
+ shm_error_base,
+ shape_event_base,
+ shape_error_base;
+
+ if ( !args->full_shots &&
+ !XDamageQueryExtension(dpy, damage_event, damage_error)) {
+
+ fprintf(stderr, "XDamage extension not found!!!\n"
+ "Try again using the --full-shots option, though\n"
+ "enabling XDamage is highly recommended,\n"
+ "for performance reasons.\n");
+ exit(4);
+ }
+
+ if ( !args->noshared &&
+ !XQueryExtension(dpy, "MIT-SHM", shm_opcode, &shm_event_base, &shm_error_base)) {
+
+ args->noshared = 1;
+ fprintf(stderr, "Shared Memory extension not present!\n"
+ "Try again using the --no-shared option\n");
+ exit(5);
+ }
+
+ if ( args->xfixes_cursor &&
+ XFixesQueryExtension(dpy, &xf_event_basep, &xf_error_basep) == False) {
+
+ args->xfixes_cursor = 0;
+ fprintf(stderr, "Xfixes extension not present!\n"
+ "Please run with the -dummy-cursor or"
+ " --no-cursor option.\n");
+ exit(6);
+ }
+
+ if ( !args->noframe &&
+ !XShapeQueryExtension(dpy, &shape_event_base, &shape_error_base)) {
+
+ fprintf(stderr, "XShape Not Found!!!\n"
+ "Frame won't be available.\n");
+
+ args->noframe = 1;
+ }
+}
diff --git a/src/rmd_queryextensions.h b/src/rmd_queryextensions.h
new file mode 100644
index 0000000..432fa6c
--- /dev/null
+++ b/src/rmd_queryextensions.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef QUERYEXTENSIONS_H
+#define QUERYEXTENSIONS_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Check if needed extensions are present
+*
+* \param dpy Connection to the server
+*
+* \param args ProgArgs struct containing the user-set options
+*
+* \param damage_event gets filled with damage event number
+*
+* \param damage_error gets filled with damage error number
+*
+* \note Can be an exit point if extensions are not found
+*/
+void rmdQueryExtensions(Display *dpy,
+ ProgArgs *args,
+ int *damage_event,
+ int *damage_error,
+ int *shm_opcode);
+
+
+#endif
diff --git a/src/rmd_rectinsert.c b/src/rmd_rectinsert.c
new file mode 100644
index 0000000..2d2fd8c
--- /dev/null
+++ b/src/rmd_rectinsert.c
@@ -0,0 +1,480 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_rectinsert.h"
+
+#include "rmd_types.h"
+
+#include <stdlib.h>
+
+
+/**
+* Collide two rectangles and dictate most sane action for insertion,
+* as well as provide the updated rectangle(s)
+* \param xrect1 resident rectangle
+*
+* \param xrect2 New rectangle
+*
+* \param xrect_return Pointer to rectangles to be inserted
+*
+* \param nrects number of entries in xrect_return
+*
+* \retval 0 No collision
+*
+* \retval 1 xrect1 is covered by xrect2
+*
+* \retval 2 xrect2 is covered by xrect1
+*
+* \retval -1 xrect1 was broken (new is picked up in xrect_return)
+*
+* \retval -2 xrect2 was broken (new is picked up in xrect_return)
+*
+* \retval -10 Grouping the two rects is possible
+*
+*/
+static int rmdCollideRects( const XRectangle *xrect1,
+ const XRectangle *xrect2,
+ XRectangle xrect_return[],
+ int *nrects) {
+ if ((xrect1->x>=xrect2->x)&&
+ (xrect1->x+xrect1->width<=xrect2->x+xrect2->width)&&
+ (xrect1->y>=xrect2->y)&&
+ (xrect1->y+xrect1->height<=xrect2->y+xrect2->height)) {
+ //1 fits in 2
+ *nrects=0;
+ return 1;
+ } else if ((xrect2->x>=xrect1->x)&&
+ (xrect2->x+xrect2->width<=xrect1->x+xrect1->width)&&
+ (xrect2->y>=xrect1->y)&&
+ (xrect2->y+xrect2->height<=xrect1->y+xrect1->height)) {
+ //2 fits in 1
+ *nrects=0;
+ return 2;
+ } else if ((xrect1->x+xrect1->width<xrect2->x)||
+ (xrect2->x+xrect2->width<xrect1->x)||
+ (xrect1->y+xrect1->height<xrect2->y)||
+ (xrect2->y+xrect2->height<xrect1->y)) {
+ //no collision
+ *nrects=0;
+ return 0;
+ } else {
+//overlapping points are considered enclosed
+//this happens because libxdamage may generate many events for one change
+//and some of them may be in the the exact same region
+//so identical rects would be considered not colliding
+//in order though to avoid endless recursion on the rmdRectInsert
+//function should always start at the next element(which is logical since
+//if any rect makes it to a points none of it's part collides with previous
+//nodes on the list, too)
+ int x1[2]={xrect1->x,xrect1->x+xrect1->width};
+ int y1[2]={xrect1->y,xrect1->y+xrect1->height};
+ int x2[2]={xrect2->x,xrect2->x+xrect2->width};
+ int y2[2]={xrect2->y,xrect2->y+xrect2->height};
+ int enclosed[2][4],tot1,tot2;
+ enclosed[0][0]=(((x1[0]>=x2[0])&&(x1[0]<=x2[1])&&
+ (y1[0]>=y2[0])&&(y1[0]<=y2[1]))?1:0);
+ enclosed[0][1]=(((x1[1]>=x2[0])&&(x1[1]<=x2[1])&&
+ (y1[0]>=y2[0])&&(y1[0]<=y2[1]))?1:0);
+ enclosed[0][2]=(((x1[0]>=x2[0])&&(x1[0]<=x2[1])&&
+ (y1[1]>=y2[0])&&(y1[1]<=y2[1]))?1:0);
+ enclosed[0][3]=(((x1[1]>=x2[0])&&(x1[1]<=x2[1])&&
+ (y1[1]>=y2[0])&&(y1[1]<=y2[1]))?1:0);
+ enclosed[1][0]=(((x2[0]>=x1[0])&&(x2[0]<=x1[1])&&
+ (y2[0]>=y1[0])&&(y2[0]<=y1[1]))?1:0);
+ enclosed[1][1]=(((x2[1]>=x1[0])&&(x2[1]<=x1[1])&&
+ (y2[0]>=y1[0])&&(y2[0]<=y1[1]))?1:0);
+ enclosed[1][2]=(((x2[0]>=x1[0])&&(x2[0]<=x1[1])&&
+ (y2[1]>=y1[0])&&(y2[1]<=y1[1]))?1:0);
+ enclosed[1][3]=(((x2[1]>=x1[0])&&(x2[1]<=x1[1])&&
+ (y2[1]>=y1[0])&&(y2[1]<=y1[1]))?1:0);
+ tot1=enclosed[0][0]+enclosed[0][1]+enclosed[0][2]+enclosed[0][3];
+ tot2=enclosed[1][0]+enclosed[1][1]+enclosed[1][2]+enclosed[1][3];
+ if ((tot1==2)&&(tot2==2)) {//same width or height, which is the best case
+ //group
+ if ((enclosed[1][0]&&enclosed[1][1])&&
+ (xrect1->width==xrect2->width)) {
+ *nrects=1;
+ xrect_return[0].x = xrect1->x;
+ xrect_return[0].y = xrect1->y;
+ xrect_return[0].width = xrect1->width;
+ xrect_return[0].height = xrect2->height + xrect2->y - xrect1->y;
+ return -10;
+ } else if ((enclosed[1][0]&&enclosed[1][2])&&
+ (xrect1->height==xrect2->height)) {
+ *nrects=1;
+ xrect_return[0].x = xrect1->x;
+ xrect_return[0].y = xrect1->y;
+ xrect_return[0].width = xrect2->width + xrect2->x - xrect1->x;
+ xrect_return[0].height = xrect1->height;
+ return -10;
+ } else if ((enclosed[1][3]&&enclosed[1][1])&&
+ (xrect1->height==xrect2->height)) {
+ *nrects=1;
+ xrect_return[0].x = xrect2->x;
+ xrect_return[0].y = xrect2->y;
+ xrect_return[0].width = xrect1->width + xrect1->x - xrect2->x;
+ xrect_return[0].height = xrect2->height;
+ return -10;
+ } else if ((enclosed[1][3]&&enclosed[1][2])&&
+ (xrect1->width==xrect2->width)) {
+ *nrects=1;
+ xrect_return[0].x = xrect2->x;
+ xrect_return[0].y = xrect2->y;
+ xrect_return[0].width = xrect2->width;
+ xrect_return[0].height = xrect1->height + xrect1->y - xrect2->y;
+ return -10;
+ }
+ //if control reaches here therewasn't a group and we go on
+ }
+
+ if (tot2==2) {
+ //break rect2
+ xrect_return[0].x = xrect2->x;
+ xrect_return[0].y = xrect2->y;
+ xrect_return[0].width = xrect2->width;
+ xrect_return[0].height = xrect2->height;
+ *nrects=1;
+ if (enclosed[1][0]&&enclosed[1][1]) {
+ xrect_return[0].y = y1[1];
+ xrect_return[0].height -= y1[1] - y2[0];
+ } else if (enclosed[1][0]&&enclosed[1][2]) {
+ xrect_return[0].x = x1[1];
+ xrect_return[0].width -= x1[1] - x2[0];
+ } else if (enclosed[1][3]&&enclosed[1][1])
+ xrect_return[0].width -= x2[1] - x1[0];
+ else if (enclosed[1][3]&&enclosed[1][2])
+ xrect_return[0].height -= y2[1] - y1[0];
+ return -2;
+ } else if (tot1==2) {
+ //if the first one breaks(which is already inserted)
+ //then we reenter the part that was left and the one
+ //that was to be inserted
+ xrect_return[1].x = xrect2->x;
+ xrect_return[1].y = xrect2->y;
+ xrect_return[1].width = xrect2->width;
+ xrect_return[1].height = xrect2->height;
+ xrect_return[0].x = xrect1->x;
+ xrect_return[0].y = xrect1->y;
+ xrect_return[0].width = xrect1->width;
+ xrect_return[0].height = xrect1->height;
+ *nrects=1;
+ if (enclosed[0][0]&&enclosed[0][1]) {
+ xrect_return[0].y = y2[1];
+ xrect_return[0].height -= y2[1] - y1[0];
+ } else if (enclosed[0][0]&&enclosed[0][2]) {
+ xrect_return[0].x = x2[1];
+ xrect_return[0].width -= x2[1] - x1[0];
+ } else if (enclosed[0][3]&&enclosed[0][1])
+ xrect_return[0].width -= x1[1] - x2[0];
+ else if (enclosed[0][3]&&enclosed[0][2])
+ xrect_return[0].height -= y1[1] - y2[0];
+ return -1;
+
+ } else if (tot2==1) { //in which case there is also tot1==1
+ //but we rather not break that
+ //break rect2 in two
+ *nrects=2;
+ if (enclosed[1][0]) {
+//first
+ xrect_return[0].x = x1[1];
+ xrect_return[0].y = y2[0];
+ xrect_return[0].width = xrect2->width - x1[1] + x2[0];
+ xrect_return[0].height = xrect2->height;
+//second
+ xrect_return[1].x = x2[0];
+ xrect_return[1].y = y1[1];
+ xrect_return[1].width = x1[1] - x2[0];
+ xrect_return[1].height = xrect2->height - y1[1] + y2[0];
+ } else if (enclosed[1][1]) {
+//first
+ xrect_return[0].x = x2[0];
+ xrect_return[0].y = y2[0];
+ xrect_return[0].width = xrect2->width - x2[1] + x1[0];
+ xrect_return[0].height = xrect2->height;
+//second
+ xrect_return[1].x = x1[0];
+ xrect_return[1].y = y1[1];
+ xrect_return[1].width = x2[1] - x1[0];
+ xrect_return[1].height = xrect2->height - y1[1] + y2[0];
+ } else if (enclosed[1][2]) {
+//first(same as [1][0])
+ xrect_return[0].x = x1[1];
+ xrect_return[0].y = y2[0];
+ xrect_return[0].width = xrect2->width - x1[1] + x2[0];
+ xrect_return[0].height = xrect2->height;
+//second
+ xrect_return[1].x = x2[0];
+ xrect_return[1].y = y2[0];
+ xrect_return[1].width = x1[1] - x2[0];
+ xrect_return[1].height = xrect2->height - y2[1] + y1[0];
+ } else if (enclosed[1][3]) {
+//first(same as [1][1])
+ xrect_return[0].x = x2[0];
+ xrect_return[0].y = y2[0];
+ xrect_return[0].width = xrect2->width - x2[1] + x1[0];
+ xrect_return[0].height = xrect2->height;
+//second
+ xrect_return[1].x = x1[0];
+ xrect_return[1].y = y2[0];
+ xrect_return[1].width = x2[1] - x1[0];
+ xrect_return[1].height = xrect2->height - y2[1] + y1[0];
+ }
+ return -2;
+ } else {//polygons collide but no point of one is in the other
+ //so we just keep the two parts of rect2 that are outside rect1
+
+ //break rect2 in two
+ //two cases:
+ //rect2 crossing vertically rect1
+ //and rect2 crossing horizontally rect1
+ //The proper one can be found by simply checking x,y positions
+ *nrects=2;
+ if (xrect2->y<xrect1->y) {
+ //common
+ xrect_return[0].x = xrect_return[1].x = x2[0];
+ xrect_return[0].width = xrect_return[1].width = xrect2->width;
+
+ xrect_return[0].y = y2[0];
+ xrect_return[0].height = xrect2->height - y2[1] + y1[0];
+ xrect_return[1].y = y1[1];
+ xrect_return[1].height = y2[1] - y1[1];
+ } else {
+ //common
+ xrect_return[0].y = xrect_return[1].y = y2[0];
+ xrect_return[0].height = xrect_return[1].height = xrect2->height;
+
+ xrect_return[0].x = x2[0];
+ xrect_return[0].width = xrect2->width - x2[1] + x1[0];
+ xrect_return[1].x = x1[1];
+ xrect_return[1].width = x2[1] - x1[1];
+ }
+ return -2;
+ }
+ }
+}
+
+int rmdRectInsert(RectArea **root, const XRectangle *xrect) {
+
+ int total_insertions = 0;
+ RectArea *temp = NULL, *newnode = (RectArea *)malloc(sizeof(RectArea));
+
+ newnode->rect.x = xrect->x;
+ newnode->rect.y = xrect->y;
+ newnode->rect.width = xrect->width;
+ newnode->rect.height = xrect->height;
+ newnode->prev = newnode->next = NULL;
+
+ if (*root == NULL) {
+ *root = newnode;
+ total_insertions = 1;
+ } else {
+ XRectangle xrect_return[2];
+ int nrects = 0, insert_ok = 1, i = 0;
+
+ temp = *root;
+
+ while (insert_ok) { //if something is broken list does not procceed
+ //(except on -1 collres case)
+
+ int collres = rmdCollideRects(&temp->rect, xrect, &xrect_return[0], &nrects);
+ if ((!collres))
+ insert_ok = 1;
+ else {
+ insert_ok=0;
+ switch (collres) {
+ case 1://remove current node,reinsert new one
+ total_insertions--;
+ if (temp->prev!=NULL) {//no root
+ if (temp->next!=NULL) {//
+ RectArea *temp1=temp->next;
+ temp->prev->next=temp->next;
+ temp->next->prev=temp->prev;
+ free(temp);
+ if ((xrect->width>0)&&(xrect->height>0))
+ total_insertions+=rmdRectInsert(&temp1,xrect);
+ } else {
+ temp->prev->next=newnode;
+ newnode->prev=temp->prev;
+ total_insertions++;
+ free(temp);
+ }
+ } else {//root
+ if ((*root)->next!=NULL) {
+ (*root)=(*root)->next;
+ (*root)->prev=NULL;
+ if ((xrect->width>0)&&(xrect->height>0))
+ total_insertions+=rmdRectInsert(root,xrect);
+ } else if ((xrect->width>0)&&(xrect->height>0)) {
+ *root=newnode;
+ total_insertions++;
+ } else {
+ *root=NULL;
+ total_insertions++;
+ }
+ free(temp);
+ }
+ break;
+ case 2://done,area is already covered
+ free(newnode);
+ break;
+ case -1://current node is broken and reinserted
+ //(in same pos)
+ //newnode is also reinserted
+ if (xrect_return[0].width > 0 &&
+ xrect_return[0].height > 0) {
+ temp->rect.x = xrect_return[0].x;
+ temp->rect.y = xrect_return[0].y;
+ temp->rect.width = xrect_return[0].width;
+ temp->rect.height = xrect_return[0].height;
+ if (temp->next==NULL) {
+ temp->next=newnode;
+ newnode->prev=temp;
+ total_insertions++;
+ } else {
+ insert_ok=1;
+ }
+ } else {//it might happen that the old and now broken node
+ //is of zero width or height
+ //(so it isn't reinserted)
+ if ((temp->prev==NULL)&&(temp->next!=NULL)) {
+ *root=(*root)->next;
+ (*root)->prev=NULL;
+ } else if ((temp->next==NULL)&&(temp->prev!=NULL)) {
+ temp->prev->next=newnode;
+ newnode->prev=temp->prev;
+ } else if ((temp->next==NULL)&&(temp->prev==NULL))
+ (*root)=newnode; else {
+ total_insertions--;
+ temp->next->prev=temp->prev;
+ temp->prev->next=temp->next;
+ total_insertions+=rmdRectInsert(&temp->next, xrect);
+ }
+ free(temp);
+ }
+ break;
+ case -2://new is broken and reinserted
+ if (temp->next==NULL) {
+ total_insertions+=nrects;
+ newnode->rect.x = xrect_return[0].x;
+ newnode->rect.y = xrect_return[0].y;
+ newnode->rect.width = xrect_return[0].width;
+ newnode->rect.height = xrect_return[0].height;
+ temp->next=newnode;
+ newnode->prev=temp;
+ if (nrects>1) {
+ RectArea *newnode1=
+ (RectArea *)malloc(sizeof(RectArea));
+
+ newnode1->rect.x = xrect_return[1].x;
+ newnode1->rect.y = xrect_return[1].y;
+ newnode1->rect.width = xrect_return[1].width;
+ newnode1->rect.height = xrect_return[1].height;
+ newnode->next=newnode1;
+ newnode1->prev=newnode;
+ newnode1->next=NULL;
+ }
+ } else {
+ for(i=0;i<nrects;i++) {
+ if (xrect_return[i].width > 0 &&
+ xrect_return[i].height > 0)
+ total_insertions+=
+ rmdRectInsert(&temp->next, &xrect_return[i]);
+ }
+ }
+ break;
+ case -10://grouped
+ if (temp->prev==NULL) {
+ if (temp->next==NULL) {//empty list
+ newnode->rect.x = xrect_return[0].x;
+ newnode->rect.y = xrect_return[0].y;
+ newnode->rect.width = xrect_return[0].width;
+ newnode->rect.height = xrect_return[0].height;
+
+ *root=newnode;
+ free(temp);
+ } else {
+ total_insertions--;
+ *root=temp->next;
+ (*root)->prev=NULL;
+ free(temp);
+ if (xrect_return[0].width > 0 &&
+ xrect_return[0].height > 0)
+ total_insertions+=
+ rmdRectInsert(root, &xrect_return[0]);
+ }
+ } else if (temp->next==NULL) {//last, enter anyway
+ newnode->rect.x = xrect_return[0].x;
+ newnode->rect.y = xrect_return[0].y;
+ newnode->rect.width = xrect_return[0].width;
+ newnode->rect.height = xrect_return[0].height;
+ temp->prev->next=newnode;
+ newnode->prev=temp->prev;
+ free(temp);
+ } else {//remove node and reinsert, starting where we were
+ total_insertions--;
+ RectArea *temp1=temp->next;
+ temp->prev->next=temp->next;
+ temp->next->prev=temp->prev;
+ free(temp);
+ if (xrect_return[0].width > 0 &&
+ xrect_return[0].height > 0)
+ total_insertions+=
+ rmdRectInsert(&temp1, &xrect_return[0]);
+ }
+ break;
+ }
+ }
+ if (insert_ok) {
+ if (temp->next==NULL) {
+ temp->next=newnode;
+ newnode->prev=temp;
+ total_insertions++;
+ break;
+ } else {
+ temp=temp->next;
+ }
+ } else {
+ break;
+ }
+ };
+ }
+ return total_insertions;
+}
+
+void rmdClearList(RectArea **root) {
+
+ RectArea *temp;
+ temp=*root;
+ if (temp!=NULL) {
+ while (temp->next!=NULL) {
+ temp=temp->next;
+ free(temp->prev);
+ }
+ free(temp);
+ *root=NULL;
+ }
+}
diff --git a/src/rmd_rectinsert.h b/src/rmd_rectinsert.h
new file mode 100644
index 0000000..d9d0a9c
--- /dev/null
+++ b/src/rmd_rectinsert.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RECTINSERT_H
+#define RECTINSERT_H 1
+
+
+#include "rmd_types.h"
+
+
+/**
+* Insert a new rectangle on the list, making sure it doesn't overlap
+* with the existing ones
+* \param root Root entry of the list
+*
+* \param xrect New area to be inserted
+*
+* \returns Number of insertions during operation
+*
+* \note This function is reentrant and recursive. The number
+* of insertions takes this into account.
+*/
+int rmdRectInsert(RectArea **root, const XRectangle *xrect);
+
+/**
+* Clean up a list of areas marked for update.
+* \param root Root entry of the list
+*/
+void rmdClearList(RectArea **root);
+
+
+#endif
diff --git a/src/rmd_register_callbacks.c b/src/rmd_register_callbacks.c
new file mode 100644
index 0000000..33e04dd
--- /dev/null
+++ b/src/rmd_register_callbacks.c
@@ -0,0 +1,86 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_register_callbacks.h"
+
+#include "rmd_types.h"
+
+#include <signal.h>
+
+
+// There seem to be no way of passing user data to the signal handler,
+// so hack around not being able to pass ProgData to them
+static int *pdata_running = NULL;
+static int *pdata_paused = NULL;
+static int *pdata_aborted = NULL;
+static int *pdata_pause_state_changed = NULL;
+
+
+static void rmdSetPaused(int signum) {
+
+ *pdata_pause_state_changed = TRUE;
+}
+
+static void rmdSetRunning(int signum) {
+
+ if (!*pdata_paused) {
+
+ *pdata_running = FALSE;
+
+ if (signum == SIGABRT) {
+ *pdata_aborted = TRUE;
+ }
+ }
+}
+
+void rmdRegisterCallbacks(ProgData *pdata) {
+
+ struct sigaction pause_act;
+ struct sigaction end_act;
+
+ // Is there some way to pass pdata to the signal handlers?
+ pdata_running = &pdata->running;
+ pdata_paused = &pdata->paused;
+ pdata_aborted = &pdata->aborted;
+ pdata_pause_state_changed = &pdata->pause_state_changed;
+
+ // Setup pause_act
+ sigfillset(&pause_act.sa_mask);
+ pause_act.sa_flags = SA_RESTART;
+ pause_act.sa_handler = rmdSetPaused;
+
+ sigaction(SIGUSR1, &pause_act, NULL);
+
+ // Setup end_act
+ sigfillset(&end_act.sa_mask);
+ end_act.sa_flags = SA_RESTART;
+ end_act.sa_handler = rmdSetRunning;
+
+ sigaction(SIGINT, &end_act, NULL);
+ sigaction(SIGTERM, &end_act, NULL);
+ sigaction(SIGABRT, &end_act, NULL);
+}
diff --git a/src/rmd_register_callbacks.h b/src/rmd_register_callbacks.h
new file mode 100644
index 0000000..abe8783
--- /dev/null
+++ b/src/rmd_register_callbacks.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef REGISTER_CALLBACKS_H
+#define REGISTER_CALLBACKS_H 1
+
+
+#include "rmd_types.h"
+
+
+/**
+* Set up all callbacks and signal handlers
+* \param pdata ProgData struct containing all program data
+*/
+void rmdRegisterCallbacks(ProgData *prog_data);
+
+
+#endif
diff --git a/src/rmd_rescue.c b/src/rmd_rescue.c
new file mode 100644
index 0000000..84bcf4b
--- /dev/null
+++ b/src/rmd_rescue.c
@@ -0,0 +1,124 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_rescue.h"
+
+#include "rmd_yuv_utils.h"
+#include "rmd_encode_cache.h"
+#include "rmd_initialize_data.h"
+#include "rmd_register_callbacks.h"
+#include "rmd_specsfile.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+
+int rmdRescue(const char *path) {
+ unsigned short width, height;
+ ProgData pdata;
+ EncData enc_data;
+ CacheData cache_data;
+
+ rmdSetupDefaultArgs(&pdata.args);
+
+ pdata.enc_data = &enc_data;
+ pdata.cache_data = &cache_data;
+
+ //projname
+ cache_data.projname = malloc(strlen(path) + 2);
+ strcpy(cache_data.projname, path);
+ strcat(cache_data.projname, "/");//having two of these doesn't hurt...
+ //image data
+ cache_data.imgdata = malloc(strlen(cache_data.projname) + 11);
+ strcpy(cache_data.imgdata, cache_data.projname);
+ strcat(cache_data.imgdata, "img.out");
+ //audio data
+ cache_data.audiodata = malloc(strlen(cache_data.projname) + 10);
+ strcpy(cache_data.audiodata, cache_data.projname);
+ strcat(cache_data.audiodata, "audio.pcm");
+ //specsfile
+ cache_data.specsfile = malloc(strlen(cache_data.projname) + 10);
+ strcpy(cache_data.specsfile, cache_data.projname);
+ strcat(cache_data.specsfile, "specs.txt");
+
+ if (rmdReadSpecsFile(&pdata))
+ return 1;
+
+ width = ((pdata.brwin.rrect.width + 15) >> 4) << 4;
+ height = ((pdata.brwin.rrect.height + 15) >> 4) << 4;
+
+ enc_data.yuv.y = malloc(height * width);
+ enc_data.yuv.u = malloc(height * width / 4);
+ enc_data.yuv.v = malloc(height * width / 4);
+ enc_data.yuv.y_width = width;
+ enc_data.yuv.y_height = height;
+ enc_data.yuv.y_stride = width;
+
+ enc_data.yuv.uv_width = width / 2;
+ enc_data.yuv.uv_height = height / 2;
+ enc_data.yuv.uv_stride = width / 2;
+
+ for (int i = 0; i < (enc_data.yuv.y_width * enc_data.yuv.y_height); i++)
+ enc_data.yuv.y[i] = 0;
+
+ for (int i = 0; i < (enc_data.yuv.uv_width * enc_data.yuv.uv_height); i++)
+ enc_data.yuv.v[i] = enc_data.yuv.u[i] = 127;
+
+ yblocks = malloc(sizeof(*yblocks) * (enc_data.yuv.y_width / Y_UNIT_WIDTH) *
+ (enc_data.yuv.y_height / Y_UNIT_WIDTH));
+ ublocks = malloc(sizeof(*ublocks) * (enc_data.yuv.y_width / Y_UNIT_WIDTH) *
+ (enc_data.yuv.y_height / Y_UNIT_WIDTH));
+ vblocks = malloc(sizeof(*vblocks) * (enc_data.yuv.y_width / Y_UNIT_WIDTH) *
+ (enc_data.yuv.y_height / Y_UNIT_WIDTH));
+
+ pdata.frametime = 1000000 / pdata.args.fps;
+
+ pthread_mutex_init(&pdata.theora_lib_mutex, NULL);
+ pthread_mutex_init(&pdata.vorbis_lib_mutex, NULL);
+ pthread_mutex_init(&pdata.libogg_mutex, NULL);
+ pthread_cond_init(&pdata.theora_lib_clean, NULL);
+ pthread_cond_init(&pdata.vorbis_lib_clean, NULL);
+ pdata.th_encoding_clean = pdata.v_encoding_clean = 1;
+ pdata.avd = 0;
+ pdata.sound_buffer = NULL;
+ pdata.running = TRUE;
+ pdata.aborted = FALSE;
+
+ rmdRegisterCallbacks(&pdata);
+ fprintf(stderr, "Restoring %s!!!\n", path);
+
+ rmdEncodeCache(&pdata);
+
+ fprintf(stderr, "Done!!!\n");
+ fprintf(stderr, "Goodbye!\n");
+ rmdCleanUp();
+
+ return 0;
+}
diff --git a/src/rmd_rescue.h b/src/rmd_rescue.h
new file mode 100644
index 0000000..2fbf2da
--- /dev/null
+++ b/src/rmd_rescue.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_RESCUE_H
+#define RMD_RESCUE_H 1
+
+/*
+ * Rescue a previous recording, found in
+ * the given path.
+ *
+ * \param path Path to the cache folder.
+ *
+ * \returns 0 on Success, 1 on failure
+ *
+ */
+int rmdRescue(const char *path);
+
+
+#endif
diff --git a/src/rmd_setbrwindow.c b/src/rmd_setbrwindow.c
new file mode 100644
index 0000000..9ad30b8
--- /dev/null
+++ b/src/rmd_setbrwindow.c
@@ -0,0 +1,115 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_setbrwindow.h"
+
+#include "rmd_types.h"
+
+
+boolean rmdSetBRWindow( Display *dpy,
+ BRWindow *brwin,
+ DisplaySpecs *specs,
+ ProgArgs *args) {
+
+ //before we start recording we have to make sure the ranges are valid
+ if (args->windowid == 0) {//root window
+ //first set it up
+ brwin->windowid = specs->root;
+ brwin->winrect.x = 0;
+ brwin->winrect.y = 0;
+ brwin->winrect.width = specs->width;
+ brwin->winrect.height = specs->height;
+
+ brwin->rrect.x = args->x;
+ brwin->rrect.y = args->y;
+ brwin->rrect.width = args->width ? args->width : brwin->winrect.width - args->x;
+ brwin->rrect.height = args->height ? args->height : brwin->winrect.height - args->y;
+
+ //and then check validity
+ if ( brwin->rrect.x + brwin->rrect.width > specs->width ||
+ brwin->rrect.y + brwin->rrect.height > specs->height) {
+
+ fprintf(stderr, "Window size specification out of bounds!"
+ "(current resolution:%dx%d)\n",
+ specs->width, specs->height);
+ return FALSE;
+ }
+ } else {
+ int transl_x, transl_y;
+ Window wchid;
+ XWindowAttributes attribs;
+
+ XGetWindowAttributes(dpy, args->windowid, &attribs);
+
+ if (attribs.map_state == IsUnviewable || attribs.map_state == IsUnmapped) {
+ fprintf(stderr, "Window must be mapped and visible!\n");
+ return FALSE;
+ }
+
+ XTranslateCoordinates( dpy,
+ specs->root,
+ args->windowid,
+ attribs.x,
+ attribs.y,
+ &transl_x,
+ &transl_y,
+ &wchid);
+
+ brwin->winrect.x = attribs.x - transl_x;
+ brwin->winrect.y = attribs.y - transl_y;
+ brwin->winrect.width = attribs.width;
+ brwin->winrect.height = attribs.height;
+
+ /* XXX FIXME: this check is partial at best, surely windows can be off the low
+ * sides of the screen too...
+ */
+ if ( brwin->winrect.x + brwin->winrect.width > specs->width ||
+ brwin->winrect.y + brwin->winrect.height > specs->height) {
+
+ fprintf(stderr,"Window must be on visible screen area!\n");
+ return FALSE;
+ }
+
+ brwin->rrect.x = brwin->winrect.x + args->x;
+ brwin->rrect.y = brwin->winrect.y + args->y;
+ brwin->rrect.width = args->width ? args->width : brwin->winrect.width - args->x;
+ brwin->rrect.height = args->height ? args->height : brwin->winrect.height - args->y;
+
+ if ( args->x + brwin->rrect.width > brwin->winrect.width ||
+ args->y + brwin->rrect.height > brwin->winrect.height) {
+ fprintf(stderr, "Specified Area is larger than window!\n");
+ return FALSE;
+ }
+ }
+
+ fprintf(stderr, "Initial recording window is set to:\n"
+ "X:%d Y:%d Width:%d Height:%d\n",
+ brwin->rrect.x, brwin->rrect.y,
+ brwin->rrect.width, brwin->rrect.height);
+
+ return TRUE;
+}
diff --git a/src/rmd_setbrwindow.h b/src/rmd_setbrwindow.h
new file mode 100644
index 0000000..e036856
--- /dev/null
+++ b/src/rmd_setbrwindow.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef SETBRWINDOW_H
+#define SETBRWINDOW_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Check and align window size
+*
+* \param dpy Connection to the server
+*
+* \param brwin BRWindow struct contaning the initial and final window
+*
+* \param specs DisplaySpecs struct with
+* information about the display to be recorded
+*
+* \param args ProgArgs struct containing the user-set options
+*
+* \returns 0 on Success 1 on Failure
+*/
+int rmdSetBRWindow(Display *dpy,
+ BRWindow *brwin,
+ DisplaySpecs *specs,
+ ProgArgs *args);
+
+
+#endif
diff --git a/src/rmd_shortcuts.c b/src/rmd_shortcuts.c
new file mode 100644
index 0000000..e1e07f5
--- /dev/null
+++ b/src/rmd_shortcuts.c
@@ -0,0 +1,142 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_shortcuts.h"
+
+#include "rmd_types.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <X11/keysym.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int rmdRegisterShortcut( Display *dpy,
+ Window root,
+ const char *shortcut,
+ HotKey *hotkey) {
+
+ int keycode = 0, i, j;
+ KeySym key = 0;
+ unsigned int modifier_mask = 0, numlock_mask = 0;
+ char *keystr = NULL;
+
+ if (strstr(shortcut, "Shift"))
+ modifier_mask = modifier_mask | ShiftMask;
+ if (strstr(shortcut, "Control"))
+ modifier_mask = modifier_mask | ControlMask;
+ if (strstr(shortcut, "Mod1"))
+ modifier_mask = modifier_mask | Mod1Mask;
+ if (strstr(shortcut, "Mod2"))
+ modifier_mask = modifier_mask | Mod2Mask;
+ if (strstr(shortcut, "Mod3"))
+ modifier_mask = modifier_mask | Mod3Mask;
+ if (strstr(shortcut, "Mod4"))
+ modifier_mask = modifier_mask | Mod4Mask;
+ if (strstr(shortcut, "Mod5"))
+ modifier_mask = modifier_mask | Mod5Mask;
+
+ //we register the shortcut on the root
+ //window, which means on every keypress event,
+ //so I think it's neccessary to have at least one
+ //modifier.
+ if (modifier_mask == 0)
+ return 1;
+ if ((keystr = rindex(shortcut, '+')) != NULL) {
+ keystr++;
+ if ((key = XStringToKeysym(keystr)) == NoSymbol)
+ return 1;
+ else
+ keycode = XKeysymToKeycode(dpy, key);
+ } else
+ return 1;
+
+
+ /* Key grabbing stuff taken from tilda who took it from yeahconsole
+ * who took it from evilwm */
+
+ {
+ KeyCode numlock = XKeysymToKeycode(dpy, XK_Num_Lock);
+ XModifierKeymap *modmap = XGetModifierMapping(dpy);
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < modmap->max_keypermod; j++) {
+ if (modmap->modifiermap[i * modmap->max_keypermod + j] == numlock)
+ numlock_mask = (1 << i);
+ }
+ }
+ XFreeModifiermap(modmap);
+ }
+
+ hotkey->modnum = 0;
+ hotkey->key = keycode;
+
+ XGrabKey( dpy,
+ keycode,
+ modifier_mask,
+ root,
+ True,
+ GrabModeAsync,
+ GrabModeAsync);
+ hotkey->mask[0] = modifier_mask;
+ hotkey->modnum++;
+
+ XGrabKey( dpy,
+ keycode,
+ LockMask | modifier_mask,
+ root,
+ True,
+ GrabModeAsync,
+ GrabModeAsync);
+ hotkey->mask[1] = LockMask | modifier_mask;
+ hotkey->modnum++;
+
+ if (numlock_mask) {
+ XGrabKey( dpy,
+ keycode,
+ numlock_mask | modifier_mask,
+ root,
+ True,
+ GrabModeAsync,
+ GrabModeAsync);
+ hotkey->mask[2] = numlock_mask | modifier_mask;
+ hotkey->modnum++;
+
+ XGrabKey( dpy,
+ keycode,
+ numlock_mask | LockMask | modifier_mask,
+ root,
+ True,
+ GrabModeAsync,
+ GrabModeAsync);
+ hotkey->mask[3] = numlock_mask | LockMask | modifier_mask;
+ hotkey->modnum++;
+ }
+
+ return 0;
+}
diff --git a/src/rmd_shortcuts.h b/src/rmd_shortcuts.h
new file mode 100644
index 0000000..a0dc2af
--- /dev/null
+++ b/src/rmd_shortcuts.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef SHORTCUTS_H
+#define CHORTCUTS_H 1
+
+#include "rmd_types.h"
+
+
+/*
+ * Check a shortcut combination and if valid,
+ * register it, on the root window.
+ *
+ * \param dpy Connection to the X Server
+ *
+ * \param root Root window id
+ *
+ * \param shortcut String represantation of the shortcut
+ *
+ * \param HotKey Pre-allocated struct that is filled with the
+ * modifiers and the keycode, for checks on incoming
+ * keypress events. Left untouched if the call fails.
+ *
+ *
+ * \returns 0 on Success, 1 on Failure.
+ *
+ */
+int rmdRegisterShortcut(Display *dpy,
+ Window root,
+ const char *shortcut,
+ HotKey *hotkey);
+
+
+#endif
diff --git a/src/rmd_specsfile.c b/src/rmd_specsfile.c
new file mode 100644
index 0000000..50542a8
--- /dev/null
+++ b/src/rmd_specsfile.c
@@ -0,0 +1,151 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_specsfile.h"
+
+#include "rmd_types.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+int rmdWriteSpecsFile(ProgData *pdata) {
+
+ FILE *fp;
+
+ fp=fopen(pdata->cache_data->specsfile,"wb");
+ if (!fp)
+ return 1;
+
+ fprintf(fp,"recordMyDesktop = %s\n",VERSION);
+ fprintf(fp,"Width = %hu\n",pdata->brwin.rrect.width);
+ fprintf(fp,"Height = %hu\n",pdata->brwin.rrect.height);
+ fprintf(fp,"Filename = %s\n",pdata->args.filename);
+ fprintf(fp,"FPS = %f\n",pdata->args.fps);
+ fprintf(fp,"NoSound = %d\n",pdata->args.nosound);
+ fprintf(fp,"Frequency = %d\n",pdata->args.frequency);
+ fprintf(fp,"Channels = %d\n",pdata->args.channels);
+ fprintf(fp,"BufferSize = %lu\n",(unsigned long)pdata->args.buffsize);
+ fprintf(fp,"SoundFrameSize = %d\n",pdata->sound_framesize);
+ fprintf(fp,"PeriodSize = %lu\n",(unsigned long)pdata->periodsize);
+ fprintf(fp,"UsedJack = %d\n",pdata->args.use_jack);
+ fprintf(fp,"v_bitrate = %d\n",pdata->args.v_bitrate);
+ fprintf(fp,"v_quality = %d\n",pdata->args.v_quality);
+ fprintf(fp,"s_quality = %d\n",pdata->args.s_quality);
+ fprintf(fp,"ZeroCompression = %d\n",pdata->args.zerocompression);
+
+ fclose(fp);
+
+ return 0;
+}
+
+int rmdReadSpecsFile(ProgData *pdata) {
+ char Cached_Version[256];
+ FILE *fp;
+
+ fp=fopen(pdata->cache_data->specsfile,"rb");
+ if (!fp)
+ return 1;
+
+ free(pdata->args.filename);
+ pdata->args.filename=malloc(512);
+
+ //Take that single-point-of-exit advocates !!!
+ //15 points of exit, just for your pleasure.
+ //Also, Vi(m) rules, emacs sucks.
+
+ if (fscanf(fp,"recordMyDesktop = %s\n",Cached_Version)!=1) {
+ fprintf(stderr,"Error reading VERSION attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"Width = %hu\n",&pdata->brwin.rrect.width)!=1) {
+ fprintf(stderr,"Error reading Width attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"Height = %hu\n",&pdata->brwin.rrect.height)!=1) {
+ fprintf(stderr,"Error reading Height attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"Filename = %s\n",pdata->args.filename)!=1) {
+ fprintf(stderr,"Error reading Filename attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"FPS = %f\n",&pdata->args.fps)!=1) {
+ fprintf(stderr,"Error reading FPS attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"NoSound = %d\n",&pdata->args.nosound)!=1) {
+ fprintf(stderr,"Error reading NoSound attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"Frequency = %d\n",&pdata->args.frequency)!=1) {
+ fprintf(stderr,"Error reading Frequency attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"Channels = %d\n",&pdata->args.channels)!=1) {
+ fprintf(stderr,"Error reading Channels attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"BufferSize = %lu\n",
+ (unsigned long *)&pdata->args.buffsize)!=1) {
+ fprintf(stderr,"Error reading BufferSize attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"SoundFrameSize = %d\n",&pdata->sound_framesize)!=1) {
+ fprintf(stderr,"Error reading SoundFrameSize attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"PeriodSize = %lu\n",
+ (unsigned long *)&pdata->periodsize)!=1) {
+ fprintf(stderr,"Error reading PeriodSize attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"UsedJack = %u\n",&pdata->args.use_jack)!=1) {
+ fprintf(stderr,"Error reading UsedJack attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"v_bitrate = %d\n",&pdata->args.v_bitrate)!=1) {
+ fprintf(stderr,"Error reading v_bitrate attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"v_quality = %d\n",&pdata->args.v_quality)!=1) {
+ fprintf(stderr,"Error reading v_quality attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"s_quality = %d\n",&pdata->args.s_quality)!=1) {
+ fprintf(stderr,"Error reading s_quality attribute!!!\n");
+ return 1;
+ }
+ if (fscanf(fp,"ZeroCompression = %d\n",&pdata->args.zerocompression)!=1) {
+ fprintf(stderr,"Error reading ZeroCompression attribute!!!\n");
+ return 1;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
diff --git a/src/rmd_specsfile.h b/src/rmd_specsfile.h
new file mode 100644
index 0000000..67cbd46
--- /dev/null
+++ b/src/rmd_specsfile.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef SPECSFILE_H
+#define SPECSFILE_H 1
+
+#include "rmd_types.h"
+
+
+/*
+ * Create a text file that holds the required
+ * capture attributes, in the cache directory.
+ *
+ * \param pdata ProgData struct containing all program data
+ *
+ * \returns 0 on Success, 1 on failure
+ *
+ */
+int rmdWriteSpecsFile(ProgData *pdata);
+
+
+
+/*
+ * Read the text file that holds the required
+ * capture attributes, in the cache directory.
+ *
+ * \param pdata ProgData struct that will be fille
+ * with all program data
+ *
+ * \returns 0 on Success, 1 on failure
+ *
+ */
+int rmdReadSpecsFile(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_threads.c b/src/rmd_threads.c
new file mode 100644
index 0000000..cb81f84
--- /dev/null
+++ b/src/rmd_threads.c
@@ -0,0 +1,169 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_threads.h"
+
+#include "rmd_cache_audio.h"
+#include "rmd_cache_frame.h"
+#include "rmd_capture_sound.h"
+#include "rmd_encode_image_buffer.h"
+#include "rmd_encode_sound_buffer.h"
+#include "rmd_flush_to_ogg.h"
+#include "rmd_get_frame.h"
+#include "rmd_jack.h"
+#include "rmd_register_callbacks.h"
+#include "rmd_timer.h"
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void rmdThreads(ProgData *pdata) {
+ pthread_t image_capture_t,
+ image_encode_t,
+ image_cache_t,
+ sound_capture_t,
+ sound_encode_t,
+ sound_cache_t,
+ flush_to_ogg_t,
+ timer_t;
+
+ if (pdata->args.delay > 0) {
+ fprintf(stderr, "Will sleep for %d seconds now.\n", pdata->args.delay);
+ sleep(pdata->args.delay);
+ }
+
+ /*start threads*/
+ pthread_create( &image_capture_t,
+ NULL,
+ (void *)rmdGetFrame,
+ (void *)pdata);
+
+ if (pdata->args.encOnTheFly)
+ pthread_create( &image_encode_t,
+ NULL,
+ (void *)rmdEncodeImageBuffer,
+ (void *)pdata);
+ else
+ pthread_create( &image_cache_t,
+ NULL,
+ (void *)rmdCacheImageBuffer,
+ (void *)pdata);
+
+ if (!pdata->args.nosound) {
+ if (!pdata->args.use_jack)
+ pthread_create( &sound_capture_t,
+ NULL,
+ (void *)rmdCaptureSound,
+ (void *)pdata);
+
+ if (pdata->args.encOnTheFly)
+ pthread_create( &sound_encode_t,
+ NULL,
+ (void *)rmdEncodeSoundBuffer,
+ (void *)pdata);
+ else
+ pthread_create( &sound_cache_t,
+ NULL,
+ (void *)rmdCacheSoundBuffer,
+ (void *)pdata);
+ }
+
+ if (pdata->args.encOnTheFly)
+ pthread_create( &flush_to_ogg_t,
+ NULL,
+ (void *)rmdFlushToOgg,
+ (void *)pdata);
+
+ rmdRegisterCallbacks(pdata);
+ pdata->timer_alive = 1;
+ pthread_create( &timer_t,
+ NULL,
+ (void *)rmdTimer,
+ (void *)pdata);
+ fprintf(stderr,"Capturing!\n");
+
+#ifdef HAVE_LIBJACK
+ if (pdata->args.use_jack)
+ pdata->jdata->capture_started = 1;
+#endif
+ //wait all threads to finish
+
+ pthread_join(image_capture_t, NULL);
+ fprintf(stderr,"Shutting down.");
+ //if no damage events have been received the thread will get stuck
+ pthread_mutex_lock(&pdata->theora_lib_mutex);
+ while (!pdata->th_encoding_clean) {
+ puts("waiting for th_enc");
+ pthread_cond_signal(&pdata->image_buffer_ready);
+ pthread_mutex_unlock(&pdata->theora_lib_mutex);
+ usleep(10000);
+ pthread_mutex_lock(&pdata->theora_lib_mutex);
+ }
+ pthread_mutex_unlock(&pdata->theora_lib_mutex);
+
+ if (pdata->args.encOnTheFly)
+ pthread_join(image_encode_t, NULL);
+ else
+ pthread_join(image_cache_t, NULL);
+ fprintf(stderr,".");
+
+ if (!pdata->args.nosound) {
+#ifdef HAVE_LIBJACK
+ if (pdata->args.use_jack)
+ rmdStopJackClient(pdata->jdata);
+#endif
+ if (!pdata->args.use_jack)
+ pthread_join(sound_capture_t,NULL);
+
+ fprintf(stderr,".");
+ pthread_mutex_lock(&pdata->vorbis_lib_mutex);
+ while (!pdata->v_encoding_clean) {
+ puts("waiting for v_enc");
+ pthread_cond_signal(&pdata->sound_data_read);
+ pthread_mutex_unlock(&pdata->vorbis_lib_mutex);
+ usleep(10000);
+ pthread_mutex_lock(&pdata->vorbis_lib_mutex);
+ }
+ pthread_mutex_unlock(&pdata->vorbis_lib_mutex);
+
+ if (pdata->args.encOnTheFly)
+ pthread_join(sound_encode_t, NULL);
+ else
+ pthread_join(sound_cache_t, NULL);
+ } else
+ fprintf(stderr,"..");
+
+ fprintf(stderr,".");
+
+ //Now that we are done with recording we cancel the timer
+ pdata->timer_alive = 0;
+ pthread_join(timer_t,NULL);
+}
diff --git a/src/rmd_threads.h b/src/rmd_threads.h
new file mode 100644
index 0000000..e2c103a
--- /dev/null
+++ b/src/rmd_threads.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMDTHREADS_H
+#define RMDTHREADS_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Launch and wait capture threads.
+* Also creates and waits the encoding threads when
+* encode-on-the-fly is enabled.
+*
+* \param pdata ProgData struct containing all program data
+*/
+void rmdThreads(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_timer.c b/src/rmd_timer.c
new file mode 100644
index 0000000..b8161ca
--- /dev/null
+++ b/src/rmd_timer.c
@@ -0,0 +1,81 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_timer.h"
+
+#include "rmd_types.h"
+
+#include <pthread.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+
+void *rmdTimer(ProgData *pdata){
+ long unsigned int secs_tw=1/pdata->args.fps;
+ long unsigned int usecs_tw=(1000000)/pdata->args.fps- secs_tw*1000000;
+
+ while (pdata->timer_alive) {
+
+ pthread_mutex_lock(&pdata->pause_mutex);
+ if (pdata->pause_state_changed) {
+ pdata->pause_state_changed = FALSE;
+
+ if (!pdata->paused) {
+ pdata->paused = TRUE;
+ fprintf(stderr,"STATE:PAUSED\n");fflush(stderr);
+ } else{
+ pdata->paused = FALSE;
+ fprintf(stderr,"STATE:RECORDING\n");fflush(stderr);
+ pthread_cond_broadcast(&pdata->pause_cond);
+ }
+ }
+
+ if (!pdata->paused) {
+ pthread_mutex_unlock(&pdata->pause_mutex);
+
+ /* FIXME TODO: detect dropped frames by delta between {time,capture}_frameno */
+ pdata->frames_total++;
+ } else
+ pthread_mutex_unlock(&pdata->pause_mutex);
+
+ pthread_mutex_lock(&pdata->time_mutex);
+ pdata->time_frameno++;
+ pthread_mutex_unlock(&pdata->time_mutex);
+ pthread_cond_signal(&pdata->time_cond);
+
+ /* FIXME use nanosleep */
+ if (secs_tw)
+ sleep(secs_tw);
+
+ usleep(usecs_tw);
+ }
+
+ pthread_exit(&errno);
+}
diff --git a/src/rmd_timer.h b/src/rmd_timer.h
new file mode 100644
index 0000000..d9292d9
--- /dev/null
+++ b/src/rmd_timer.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_TIMER_H
+#define RMD_TIMER_H 1
+
+#include "rmd_types.h"
+
+
+/**
+* Loop ,signal timer cond var,sleep-\
+* ^ |
+* |________________________________/
+*
+*
+* \param pdata ProgData struct containing all program data
+*/
+void *rmdTimer(ProgData *pdata);
+
+
+#endif
diff --git a/src/rmd_types.h b/src/rmd_types.h
new file mode 100644
index 0000000..6018955
--- /dev/null
+++ b/src/rmd_types.h
@@ -0,0 +1,366 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMDTYPES_H
+#define RMDTYPES_H 1
+
+#include "config.h"
+
+#include <stdio.h>
+#include <pthread.h>
+#include <zlib.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+#include <theora/theora.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisenc.h>
+#include <ogg/ogg.h>
+
+#ifdef HAVE_LIBASOUND
+ #include <alsa/asoundlib.h>
+
+ #define DEFAULT_AUDIO_DEVICE "hw:0,0"
+#else
+ #include <sys/ioctl.h>
+ #include <sys/soundcard.h>
+
+ #define DEFAULT_AUDIO_DEVICE "/dev/dsp"
+#endif
+
+#ifdef HAVE_LIBJACK
+ #include <jack/jack.h>
+ #include <jack/ringbuffer.h>
+#endif
+
+
+//this type exists only
+//for comparing the planes at caching.
+//u_int64_t mught not be available everywhere.
+//The performance gain comes from casting the unsigned char
+//buffers to this type before comparing the two blocks.
+//This is made possible by the fact that blocks
+//for the Y plane are 16 bytes in width and blocks
+//for the U,V planes are 8 bytes in width
+#ifdef HAVE_U_INT64_T
+typedef u_int64_t cmp_int_t;
+#else
+typedef u_int32_t cmp_int_t;
+#endif
+
+//type of pixel proccessing for the Cb,Cr planes
+//when converting from full rgb to 4:2:2 Ycbcr
+enum{
+ __PXL_DISCARD, //only select 1 pixel in every block of four
+ __PXL_AVERAGE //calculate the average of all four pixels
+};
+
+// Boolean type
+typedef int boolean;
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#define RMD_MAX_JACK_PORTS (100)
+
+// Forward declarations
+typedef struct _ProgData ProgData;
+
+typedef struct _Image {
+ XImage *ximage;
+ XShmSegmentInfo shm_info;
+}Image;
+
+typedef struct _DisplaySpecs{ //this struct holds some basic information
+ int screen; //about the display,needed mostly for
+ unsigned int width, height; //validity checks at startup
+ Window root;
+ Visual *visual;
+ GC gc;
+ int depth;
+}DisplaySpecs;
+
+typedef struct _RectArea{ //an area that has been damaged gets stored
+ XRectangle rect; //in a list comprised of structs of this type
+ struct _RectArea *prev,*next;
+}RectArea;
+
+typedef struct _BRWindow{ //'basic recorded window' specs
+ XRectangle winrect; //window attributes
+ XRectangle rrect; //recorded window rect in screen space coordinates
+ Window windowid; //id
+}BRWindow;
+
+//defaults in the following comment lines may be out of sync with reality
+//check the SetupDefaultArgs() function further bellow
+typedef struct _ProgArgs{
+ int delay; //start up delay
+ Window windowid; //window to record(default root)
+ char *display; //display to connect(default :0)
+ int x,y; //x,y offset(default 0,0)
+ int width,height; //defaults to window width and height
+ char *filename; //output file(default out.[ogg|*])
+ int cursor_color; //black or white=>1 or 0
+ int have_dummy_cursor; //disable/enable drawing of the dummy cursor
+ int xfixes_cursor; //disable/enable drawing of a cursor obtained
+ //through the xfixes extension
+ float fps; //desired framerate(default 15)
+ unsigned int frequency; //desired frequency (default 22050)
+ unsigned int channels; //no of channels(default 2)
+ char *device; //default sound device
+#ifdef HAVE_LIBASOUND
+ snd_pcm_uframes_t buffsize; //buffer size(in frames) for sound capturing
+#else
+ u_int32_t buffsize;
+#endif
+ const char* rescue_path;
+ int nosound; //do not record sound(default 0)
+ int noshared; //do not use shared memory extension(default 0)
+ int nowmcheck; //do not check if there's a 3d comp window manager
+ //(which changes full-shots and with-shared to 1)
+ int full_shots; //do not poll damage, take full screenshots
+ int follow_mouse; //capture area follows the mouse(fullshots auto enabled)
+ int no_encode; //do not encode or delete the temporary files(debug opt)
+ int no_quick_subsample; //average pixels in chroma planes
+ int v_bitrate,v_quality,s_quality; //video bitrate,video-sound quality
+ int encOnTheFly; //encode while recording, no caching(default 0)
+ char *workdir; //directory to be used for cache files(default $HOME)
+ char *pause_shortcut; //pause/unpause shortcut sequence(Control+Alt+p)
+ char *stop_shortcut; //stop shortcut sequence(Control+Alt+s)
+ int noframe; //don't draw a frame around the recording area
+ int zerocompression; //image data are always flushed uncompressed
+ int overwrite; //overwite a previously existing file
+ //(do not add a .number postfix)
+ int use_jack; //record audio with jack
+ unsigned int jack_nports;
+ char *jack_port_names[RMD_MAX_JACK_PORTS];
+ float jack_ringbuffer_secs;
+} ProgArgs;
+
+//this struct holds anything related to encoding AND
+//writting out to file.
+typedef struct _EncData{
+ ogg_stream_state m_ogg_ts; //theora
+ ogg_stream_state m_ogg_vs; //vorbis
+ ogg_page m_ogg_pg; //this could be avoided since
+ // it is used only while initializing
+ ogg_packet m_ogg_pckt1; //theora stream
+ ogg_packet m_ogg_pckt2; //vorbis stream
+//theora data
+ theora_state m_th_st;
+ theora_info m_th_inf;
+ theora_comment m_th_cmmnt;
+ yuv_buffer yuv;
+//vorbis data
+ vorbis_info m_vo_inf;
+ vorbis_comment m_vo_cmmnt;
+ vorbis_dsp_state m_vo_dsp;
+ vorbis_block m_vo_block;
+//our file
+ FILE *fp;
+} EncData;
+
+//this struct will hold a few basic
+//information, needed for caching the frames.
+typedef struct _CacheData{
+ char *workdir, //The directory were the project
+ //will be stored, while recording.
+ //Since this will take a lot of space, the user must be
+ //able to change the location.
+ *projname, //This is the name of the folder that
+ //will hold the project.
+ //It is rMD-session-%d where %d is the pid
+ //of the current proccess.
+ //This way, running two instances
+ //will not create problems
+ //and also, a frontend can identify
+ //leftovers from a possible crash
+ //and delete them
+ *specsfile, //workdir+projname+specs.txt
+ *imgdata, //workdir+projname+img.out.gz
+ *audiodata; //workdir+projname+audio.pcm
+
+ gzFile ifp; //image data file pointer
+ FILE *uncifp; //uncompressed image data file pointer
+
+ FILE *afp; //audio data file pointer
+
+}CacheData;
+
+//sound buffer
+//sound keeps coming so we que it in this list
+//which we then traverse
+typedef struct _SndBuffer{
+ signed char *data;
+ struct _SndBuffer *next;
+}SndBuffer;
+
+#ifdef HAVE_LIBJACK
+typedef struct _JackData{
+ ProgData *pdata; //pointer to prog data
+ jack_client_t *client;
+ unsigned int buffersize, //buffer size for every port in frames.
+ frequency, //samplerate with which jack server was started.
+ nports; //number of ports.
+ float ringbuffer_secs;
+ char **port_names; //names of ports(as specified in args).
+ jack_port_t **ports; //connections to thes ports.
+ jack_default_audio_sample_t
+ **portbuf; //retrieval of audio buffers.
+ pthread_mutex_t *sound_buffer_mutex; //mutex and cond_var
+ pthread_cond_t *sound_data_read; //in the pdata struct
+ jack_ringbuffer_t *sound_buffer; //data exchange happens through this
+ int capture_started; //used to hold recording in the beginning
+}JackData;
+#endif
+
+typedef struct _HotKey{ //Hold info about the shortcuts
+ int modnum; //modnum is the number of modifier masks
+ unsigned int mask[4]; //that should be checked (the initial
+ int key; //user requested modifier plus it's
+}HotKey; //combinations with LockMask and NumLockMask).
+
+//this structure holds any data related to the program
+//It's usage is mostly to be given as an argument to the
+//threads,so they will have access to the program data, avoiding
+//at the same time usage of any globals.
+struct _ProgData {
+/**recordMyDesktop specific structs*/
+ ProgArgs args; //the program arguments
+ DisplaySpecs specs; //Display specific information
+ BRWindow brwin; //recording window
+ RectArea *rect_root; //the interchanging list roots for storing
+ //the changed regions
+ SndBuffer *sound_buffer;
+ EncData *enc_data;
+ CacheData *cache_data;
+ HotKey pause_key, //Shortcuts
+ stop_key;
+#ifdef HAVE_LIBJACK
+ JackData *jdata;
+#endif
+/**X related info*/
+ Display *dpy; //curtrent display
+/** Mutexes*/
+ pthread_mutex_t sound_buffer_mutex,
+ img_buff_ready_mutex,
+ theora_lib_mutex, //serializes access to th_encoding_clean w/theora_lib_clean
+ vorbis_lib_mutex, //serializes acces to v_encoding_clean w/vorbis_lib_clean
+ libogg_mutex, //libogg is not thread safe,
+ yuv_mutex; //this might not be needed since we only have
+ //one read-only and one write-only thread
+ //also on previous versions,
+ //y component was looped separately
+ //and then u and v so this was needed
+ //to avoid wrong coloring to render
+ //Currently this mutex only prevents
+ //the cursor from flickering
+/**Condition Variables*/
+ pthread_cond_t time_cond, //this gets a signal by the handler
+ //whenever it's time to get a screenshot
+ pause_cond, //this is blocks execution,
+ //when program is paused
+ sound_data_read, //a buffer is ready for proccessing
+ image_buffer_ready, //image encoding finished
+ theora_lib_clean, //the flush_ogg thread cannot
+ //procceed to creating last
+ vorbis_lib_clean; //packages until these two libs
+ //are no longer used, by other threads
+/**Buffers,Flags and other vars*/
+ unsigned char *dummy_pointer, //a dummy pointer to be drawn
+ //in every frame
+ //data is casted to unsigned for
+ //later use in YUV buffer
+ npxl; //this is the no pixel convention
+ //when drawing the dummy pointer
+ unsigned int periodtime,//time that a sound buffer lasts (microsecs)
+ frametime; //time that a frame lasts (microsecs)
+ Window shaped_w; //frame
+ int damage_event, //damage event base code
+ damage_error, //damage error base code
+ shm_opcode, //MIT-Shm opcode
+ dummy_p_size, //dummy pointer size,initially 16x16,always square
+ th_encoding_clean, //thread exit inidcator
+ v_encoding_clean, // >> >>
+ timer_alive, //determines loop of timer thread
+ hard_pause, //if sound device doesn't support pause
+ //we have to close and reopen
+ avd, //syncronization among audio and video
+ sound_framesize; //size of each sound frame
+
+ /** Progam state vars */
+ boolean running; //1 while the program is capturing/paused/encoding
+ boolean paused; //1 while the program is paused
+ boolean aborted; //1 if we should abort
+ boolean pause_state_changed; //1 if pause state changed
+
+ //the following values are of no effect
+ //but they might be usefull later for profiling
+ unsigned int frames_total, //frames calculated by total time expirations
+ frames_lost; //the value of shame
+
+ /* timer advances time_frameno, getframe copies time_frameno to capture_frameno
+ * access to both is serialized by time_{mutex,cond}
+ */
+ unsigned int time_frameno, capture_frameno;
+
+ pthread_mutex_t pause_mutex;
+ pthread_mutex_t time_mutex;
+
+#ifdef HAVE_LIBASOUND
+ snd_pcm_t *sound_handle;
+ snd_pcm_uframes_t periodsize;
+#else
+ int sound_handle;
+ u_int32_t periodsize;
+#endif
+};
+
+
+//This is the header of every frame.
+//Reconstruction will be correct only if made on
+//the same platform.
+
+//We need the total number of blocks
+//for each plane.
+
+//The number of the frame compared to the
+//number of time expirations at the time of
+//caching, will enable us to make up for lost frames.
+
+
+typedef struct _FrameHeader{
+ char frame_prefix[4]; //always FRAM
+ u_int32_t frameno, //number of frame(cached frames)
+ current_total; //number of frames that should have been
+ //taken at time of caching this one
+ u_int32_t Ynum, //number of changed blocks in the Y plane
+ Unum, //number of changed blocks in the U plane
+ Vnum; //number of changed blocks in the V plane
+}FrameHeader;
+
+#endif
+
diff --git a/src/rmd_update_image.c b/src/rmd_update_image.c
new file mode 100644
index 0000000..343e1bb
--- /dev/null
+++ b/src/rmd_update_image.c
@@ -0,0 +1,95 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_update_image.h"
+
+#include "rmd_getzpixmap.h"
+#include "rmd_yuv_utils.h"
+#include "rmd_types.h"
+
+#include <X11/Xlibint.h>
+#include <X11/extensions/shmproto.h>
+#include <X11/extensions/XShm.h>
+
+#include <assert.h>
+
+
+void rmdUpdateImage( Display * dpy,
+ yuv_buffer *yuv,
+ DisplaySpecs *specs,
+ RectArea **root,
+ BRWindow *brwin,
+ EncData *enc,
+ Image *image,
+ int noshmem,
+ int shm_opcode,
+ int no_quick_subsample){
+
+ RectArea *temp;
+
+ for (temp = *root; temp; temp = temp->next) {
+
+ /* sanity check the clipping, nothing on the reclist
+ * should go outside rrect
+ */
+ assert(temp->rect.x >= brwin->rrect.x);
+ assert(temp->rect.y >= brwin->rrect.y);
+ assert(temp->rect.x + temp->rect.width <= brwin->rrect.x + brwin->rrect.width);
+ assert(temp->rect.y + temp->rect.height <= brwin->rrect.y + brwin->rrect.height);
+
+ if (noshmem) {
+ rmdGetZPixmap( dpy,
+ specs->root,
+ image->ximage->data,
+ temp->rect.x,
+ temp->rect.y,
+ temp->rect.width,
+ temp->rect.height);
+ } else {
+ rmdGetZPixmapSHM( dpy,
+ specs->root,
+ &image->shm_info,
+ shm_opcode,
+ image->ximage->data,
+ temp->rect.x,
+ temp->rect.y,
+ temp->rect.width,
+ temp->rect.height);
+ }
+ rmdUpdateYuvBuffer(
+ yuv,
+ (unsigned char *)image->ximage->data,
+ NULL,
+ temp->rect.x - brwin->rrect.x,
+ temp->rect.y - brwin->rrect.y,
+ temp->rect.width,
+ temp->rect.height,
+ no_quick_subsample,
+ specs->depth
+ );
+ }
+}
diff --git a/src/rmd_update_image.h b/src/rmd_update_image.h
new file mode 100644
index 0000000..3057f18
--- /dev/null
+++ b/src/rmd_update_image.h
@@ -0,0 +1,70 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef UPDATE_IMAGE_H
+#define UPDATE_IMAGE_H 1
+
+#include "rmd_types.h"
+
+#include <X11/extensions/XShm.h>
+
+/**
+* Retrieve and apply all changes, if xdamage is used.
+*
+* \param dpy Connection to the server
+*
+* \param yuv yuv_buffer that is to be modified
+*
+* \param specs DisplaySpecs struct with
+* information about the display to be recorded
+*
+* \param root Root entry of the list with damaged areas
+*
+* \param brwin BRWindow struct contaning the recording window specs
+*
+* \param enc Encoding options
+*
+* \param datatemp Buffer for pixel data to be
+* retrieved before placed on the yuv buffer
+*
+* \param noshmem don't use MIT_Shm extension
+*
+* \param no_quick_subsample Don't do quick subsampling
+*
+*/
+void rmdUpdateImage(Display * dpy,
+ yuv_buffer *yuv,
+ DisplaySpecs *specs,
+ RectArea **root,
+ BRWindow *brwin,
+ EncData *enc,
+ Image *image,
+ int noshmem,
+ int shm_opcode,
+ int no_quick_subsample);
+
+
+#endif
diff --git a/src/rmd_wm_check.c b/src/rmd_wm_check.c
new file mode 100644
index 0000000..4934425
--- /dev/null
+++ b/src/rmd_wm_check.c
@@ -0,0 +1,76 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_wm_check.h"
+
+#include "rmd_types.h"
+
+#include <X11/Xatom.h>
+
+
+char *rmdWMCheck(Display *dpy,Window root) {
+ Window *wm_child=NULL;
+ Atom nwm_atom,
+ utf8_string,
+ wm_name_atom,
+ rt;
+ unsigned long nbytes, nitems;
+
+ char *wm_name_str=NULL;
+ int fmt;
+
+ utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
+
+ nwm_atom =XInternAtom(dpy,"_NET_SUPPORTING_WM_CHECK",True);
+ wm_name_atom =XInternAtom(dpy,"_NET_WM_NAME",True);
+
+ if (nwm_atom!=None && wm_name_atom!=None) {
+
+ if (XGetWindowProperty( dpy,root,nwm_atom,0,100,
+ False,XA_WINDOW,
+ &rt,&fmt,&nitems, &nbytes,
+ (unsigned char **)((void*)&wm_child))
+ != Success ) {
+ fprintf(stderr,"Error while trying to get a"
+ " window to identify the window manager.\n");
+ }
+
+ if ((wm_child == NULL)||
+ (XGetWindowProperty(dpy,*wm_child,wm_name_atom,0,100,
+ False,utf8_string,&rt,
+ &fmt,&nitems, &nbytes,
+ (unsigned char **)((void*)&wm_name_str))
+ !=Success)) {
+ fprintf(stderr,"Warning!!!\nYour window manager appears"
+ " to be non-compliant!\n");
+ }
+ }
+ fprintf(stderr, "Your window manager appears to be %s\n\n",
+ ((wm_name_str!=NULL)?wm_name_str:"Unknown"));
+
+ return wm_name_str;
+}
diff --git a/src/rmd_wm_check.h b/src/rmd_wm_check.h
new file mode 100644
index 0000000..12f33dd
--- /dev/null
+++ b/src/rmd_wm_check.h
@@ -0,0 +1,45 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef WM_CHECK_H
+#define WM_CHECK_H 1
+
+#include "rmd_types.h"
+
+
+/**
+*Check current running window manager.
+*
+* \param dpy Connection to the server
+*
+* \param root root window of the display
+*
+* \returns Window manager name
+*/
+char *rmdWMCheck(Display *dpy,Window root);
+
+
+#endif
diff --git a/src/rmd_wm_is_compositing.c b/src/rmd_wm_is_compositing.c
new file mode 100644
index 0000000..bc43282
--- /dev/null
+++ b/src/rmd_wm_is_compositing.c
@@ -0,0 +1,61 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_wm_is_compositing.h"
+
+#include "rmd_wm_check.h"
+#include "rmd_types.h"
+
+#include <X11/Xatom.h>
+
+#include <stdlib.h>
+
+
+boolean rmdWMIsCompositing( Display *dpy, int screen ) {
+ Window win;
+ Atom atom;
+ char buf[32];
+ char *window_manager=rmdWMCheck( dpy, RootWindow( dpy, screen ) );
+
+ //If the wm name is queried successfully the wm is compliant (source
+ //http://standards.freedesktop.org/wm-spec/1.4/ar01s03.html#id2568282 )
+ //in which case we will also free() the allcoated string.
+
+ if (window_manager == NULL)
+ return FALSE;
+ else
+ free(window_manager);
+
+ snprintf( buf, sizeof(buf), "_NET_WM_CM_S%d", screen);
+ atom = XInternAtom(dpy, buf, True);
+ if (atom == None)
+ return FALSE;
+
+ win = XGetSelectionOwner(dpy, atom);
+
+ return win != None;
+}
diff --git a/src/rmd_wm_is_compositing.h b/src/rmd_wm_is_compositing.h
new file mode 100644
index 0000000..7f152ac
--- /dev/null
+++ b/src/rmd_wm_is_compositing.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef RMD_WM_IS_COMPOSITING_H
+#define RMD_WM_IS_COMPOSITING_H 1
+
+#include "rmd_types.h"
+
+
+/**
+*Check if the window manager is compositing (duh!).
+*
+* \param dpy Connection to the server
+*
+* \param screen screen number/id that the window manager runs on
+*
+* \returns TRUE if compositing, false otherwise or when
+* the window manager doesn't support the required
+* freedesktop.org hints for the test to be done
+* succesfully.
+*/
+
+boolean rmdWMIsCompositing( Display *dpy, int screen) ;
+
+#endif
diff --git a/src/rmd_yuv_utils.c b/src/rmd_yuv_utils.c
new file mode 100644
index 0000000..1f163ae
--- /dev/null
+++ b/src/rmd_yuv_utils.c
@@ -0,0 +1,517 @@
+/******************************************************************************
+* recordMyDesktop - rmd_yuv_utils.c *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* Copyright (C) 2008 Luca Bonavita *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_yuv_utils.h"
+
+#include "rmd_math.h"
+
+
+// Keep these global (for performance reasons I assume).
+unsigned char Yr[256], Yg[256], Yb[256],
+ Ur[256], Ug[256], UbVr[256],
+ Vg[256], Vb[256];
+
+// FIXME: These globals are modified in other source files! We keep
+// thsee here for now. These are the cache blocks. They need to be
+// accesible in the dbuf macros
+unsigned char *yblocks,
+ *ublocks,
+ *vblocks;
+
+void rmdMakeMatrices (void) {
+ int i;
+
+ /* assuming 8-bit precision */
+ float Yscale = 219.0, Yoffset = 16.0;
+ float Cscale = 224.0, Coffset = 128.0;
+ float RGBscale = 255.0;
+
+ float r, g, b;
+ float yr, yg, yb;
+ float ur, ug, ub;
+ float vg, vb; /* vr intentionally missing */
+
+ /* as for ITU-R BT-601-6 specifications: */
+ r = 0.299;
+ b = 0.114;
+ g = 1.0 - r - b;
+
+ /* as a note, here are the coefficients
+ as for ITU-R BT-709 specifications:
+ r=0.2126; b=0.0722; g=1.0-r-b; */
+
+ yr = r * Yscale / RGBscale;
+ yg = g * Yscale / RGBscale;
+ yb = b * Yscale / RGBscale;
+ ur = ( -0.5 * r / ( 1 - b ) ) * Cscale / RGBscale;
+ ug = ( -0.5 * g / ( 1 - b ) ) * Cscale / RGBscale;
+ ub = ( 0.5 * Cscale / RGBscale);
+ /* vr = ub so UbVr = ub*i = vr*i */
+ vg = ( -0.5 * g / ( 1 - r ) ) * Cscale / RGBscale;
+ vb = ( -0.5 * b / ( 1 - r ) ) * Cscale / RGBscale;
+
+ for (i = 0; i < 256; i++) {
+ Yr[i] = (unsigned char) rmdRoundf( Yoffset + yr * i );
+ Yg[i] = (unsigned char) rmdRoundf( yg * i );
+ Yb[i] = (unsigned char) rmdRoundf( yb * i );
+
+ Ur[i] = (unsigned char) rmdRoundf( Coffset + ur * i );
+ Ug[i] = (unsigned char) rmdRoundf( ug * i );
+ UbVr[i] = (unsigned char) rmdRoundf( ub * i );
+
+ Vg[i] = (unsigned char) rmdRoundf( vg * i );
+ Vb[i] = (unsigned char) rmdRoundf( Coffset + vb * i );
+ }
+}
+
+static inline int blocknum(int xv, int yv, int widthv, int blocksize)
+{
+ return ((yv/blocksize) * (widthv/blocksize) + (xv/blocksize));
+}
+
+/* These at least make some sense as macros since they need duplication for
+ * the multiple depths, so I've just moved and reformatted them here for now.
+ */
+
+#define UPDATE_Y_PLANE( data, \
+ x_tm, \
+ y_tm, \
+ width_tm, \
+ height_tm, \
+ yuv, \
+ __depth__) { \
+ \
+ register unsigned char *yuv_Y = (yuv)->y + x_tm + y_tm * (yuv)->y_stride, \
+ *_yr = Yr, *_yg = Yg, *_yb = Yb; \
+ register u_int##__depth__##_t *datapi = (u_int##__depth__##_t *)data; \
+ \
+ for (int k = 0; k < height_tm; k++) { \
+ for (int i = 0; i < width_tm; i++) { \
+ register u_int##__depth__##_t t_val = *datapi; \
+ \
+ *yuv_Y = _yr[__RVALUE_##__depth__(t_val)] + \
+ _yg[__GVALUE_##__depth__(t_val)] + \
+ _yb[__BVALUE_##__depth__(t_val)]; \
+ datapi++; \
+ yuv_Y++; \
+ } \
+ \
+ yuv_Y += (yuv)->y_stride - width_tm; \
+ } \
+}
+
+//when adding the r values, we go beyond
+//the (16 bit)range of the t_val variable, but we are performing
+//32 bit arithmetics, so there's no problem.
+//(This note is useless, I'm just adding because
+//the addition of the A components in CALC_TVAL_AVG_32,
+//now removed as uneeded, produced an overflow which would have caused
+//color distrtion, where it one of the R,G or B components)
+#define CALC_TVAL_AVG_16(t_val, datapi, datapi_next) { \
+ register u_int16_t t1, t2, t3, t4; \
+ \
+ t1 = *datapi; \
+ t2 = *(datapi + 1); \
+ t3 = *datapi_next; \
+ t4 = *(datapi_next + 1); \
+ \
+ t_val = ((((t1 & __R16_MASK) + (t2 & __R16_MASK) + \
+ (t3 & __R16_MASK) + (t4 & __R16_MASK)) / 4) & __R16_MASK) + \
+ ((((t1 & __G16_MASK) + (t2 & __G16_MASK)+ \
+ (t3 & __G16_MASK) + (t4 & __G16_MASK)) / 4) & __G16_MASK) + \
+ ((((t1 & __B16_MASK) + (t2 & __B16_MASK) + \
+ (t3 & __B16_MASK) + (t4 & __B16_MASK)) / 4) & __B16_MASK); \
+}
+
+//the 4 most significant bytes represent the A component which
+//does not need to be added on t_val, as it is always unused
+#define CALC_TVAL_AVG_32(t_val, datapi, datapi_next) { \
+ register unsigned int t1, t2, t3, t4; \
+ \
+ t1 = *datapi; \
+ t2 = *(datapi + 1); \
+ t3 = *datapi_next; \
+ t4 = *(datapi_next + 1); \
+ \
+ t_val = ((((t1 & 0x00ff0000) + (t2 & 0x00ff0000) + \
+ (t3 & 0x00ff0000) + (t4 & 0x00ff0000)) / 4) & 0x00ff0000) + \
+ ((((t1 & 0x0000ff00) + (t2 & 0x0000ff00) + \
+ (t3 & 0x0000ff00) + (t4 & 0x0000ff00)) / 4) & 0x0000ff00) + \
+ ((((t1 & 0x000000ff) + (t2 & 0x000000ff) + \
+ (t3 & 0x000000ff) + (t4 & 0x000000ff)) / 4) & 0x000000ff); \
+}
+
+#define UPDATE_A_UV_PIXEL( yuv_U, \
+ yuv_V, \
+ t_val, \
+ datapi, \
+ datapi_next, \
+ _ur,_ug,_ubvr,_vg,_vb, \
+ sampling, \
+ __depth__) \
+ \
+ if (sampling == __PXL_AVERAGE) { \
+ CALC_TVAL_AVG_##__depth__(t_val, datapi, datapi_next) \
+ } else \
+ t_val = *(datapi); \
+ \
+ *(yuv_U) = _ur[__RVALUE_##__depth__(t_val)] + \
+ _ug[__GVALUE_##__depth__(t_val)] + \
+ _ubvr[__BVALUE_##__depth__(t_val)]; \
+ \
+ *(yuv_V) = _ubvr[__RVALUE_##__depth__(t_val)] + \
+ _vg[__GVALUE_##__depth__(t_val)] + \
+ _vb[__BVALUE_##__depth__(t_val)];
+
+#define UPDATE_UV_PLANES( data, \
+ x_tm, \
+ y_tm, \
+ width_tm, \
+ height_tm, \
+ yuv, \
+ sampling, \
+ __depth__) { \
+ \
+ register u_int##__depth__##_t t_val; \
+ register unsigned char *yuv_U = (yuv)->u + x_tm / 2 + \
+ (y_tm * (yuv)->uv_width) / 2, \
+ *yuv_V = (yuv)->v + x_tm / 2 + \
+ (y_tm * (yuv)->uv_width) / 2, \
+ *_ur = Ur, *_ug = Ug, *_ubvr = UbVr, \
+ *_vg = Vg, *_vb = Vb; \
+ register u_int##__depth__##_t *datapi = (u_int##__depth__##_t *)data, \
+ *datapi_next = NULL; \
+ int w_odd = width_tm % 2, h_odd = height_tm % 2; \
+ \
+ if (sampling == __PXL_AVERAGE) \
+ datapi_next = datapi + width_tm; \
+ \
+ for (int k = 0; k < height_tm - h_odd; k += 2) { \
+ for (int i = 0; i < width_tm - w_odd; i += 2) { \
+ UPDATE_A_UV_PIXEL( yuv_U, \
+ yuv_V, \
+ t_val, \
+ datapi, \
+ datapi_next, \
+ _ur, _ug, _ubvr, _vg, _vb, \
+ sampling, \
+ __depth__); \
+ \
+ datapi += 2; \
+ if (sampling == __PXL_AVERAGE) \
+ datapi_next += 2; \
+ yuv_U++; \
+ yuv_V++; \
+ } \
+ \
+ yuv_U += ((yuv)->y_stride - (width_tm - w_odd * 2)) >> 1; \
+ yuv_V += ((yuv)->y_stride - (width_tm - w_odd * 2)) >> 1; \
+ \
+ datapi += width_tm + w_odd; \
+ if (sampling == __PXL_AVERAGE) \
+ datapi_next += width_tm + w_odd; \
+ } \
+}
+
+#define UPDATE_Y_PLANE_DBUF( data, \
+ data_back, \
+ x_tm, \
+ y_tm, \
+ width_tm, \
+ height_tm, \
+ yuv, \
+ __depth__) { \
+ \
+ register u_int##__depth__##_t t_val; \
+ register unsigned char *yuv_Y = (yuv)->y + x_tm + y_tm * (yuv)->y_stride, \
+ *_yr = Yr, *_yg = Yg, *_yb = Yb; \
+ register u_int##__depth__##_t *datapi = (u_int##__depth__##_t *)data, \
+ *datapi_back = (u_int##__depth__##_t *)data_back; \
+ \
+ for (int k = 0; k < height_tm; k++) { \
+ for (int i = 0; i < width_tm; i++) { \
+ if (*datapi != *datapi_back) { \
+ t_val = *datapi; \
+ *yuv_Y = _yr[__RVALUE_##__depth__(t_val)] + \
+ _yg[__GVALUE_##__depth__(t_val)] + \
+ _yb[__BVALUE_##__depth__(t_val)]; \
+ yblocks[blocknum(x_tm + i, y_tm + k, (yuv)->y_width, Y_UNIT_WIDTH)] = 1;\
+ } \
+ datapi++; \
+ datapi_back++; \
+ yuv_Y++; \
+ } \
+ yuv_Y += (yuv)->y_stride - width_tm; \
+ } \
+}
+
+#define UPDATE_UV_PLANES_DBUF( data, \
+ data_back, \
+ x_tm, \
+ y_tm, \
+ width_tm, \
+ height_tm, \
+ yuv, \
+ sampling, \
+ __depth__) { \
+ \
+ register u_int##__depth__##_t t_val; \
+ register unsigned char *yuv_U = (yuv)->u + x_tm / 2 + \
+ (y_tm * (yuv)->uv_width) / 2, \
+ *yuv_V = (yuv)->v + x_tm / 2 + \
+ (y_tm * (yuv)->uv_width) / 2, \
+ *_ur = Ur, *_ug = Ug, *_ubvr = UbVr, \
+ *_vg = Vg, *_vb = Vb; \
+ \
+ register u_int##__depth__##_t *datapi = (u_int##__depth__##_t *)data, \
+ *datapi_next = NULL, \
+ *datapi_back = (u_int##__depth__##_t *)data_back, \
+ *datapi_back_next = NULL; \
+ int w_odd = width_tm % 2, h_odd = height_tm % 2; \
+ \
+ if (sampling == __PXL_AVERAGE) { \
+ datapi_next = datapi + width_tm; \
+ datapi_back_next = datapi_back + width_tm; \
+ \
+ for (int k = 0; k < height_tm - h_odd; k += 2) { \
+ for (int i = 0; i < width_tm - w_odd; i += 2) { \
+ if ( (*datapi != *datapi_back || \
+ (*(datapi + 1) != *(datapi_back + 1)) || \
+ (*datapi_next != *datapi_back_next) || \
+ (*(datapi_next + 1) != *(datapi_back_next + 1)))) { \
+ \
+ UPDATE_A_UV_PIXEL( yuv_U, \
+ yuv_V, \
+ t_val, \
+ datapi, \
+ datapi_next, \
+ _ur,_ug,_ubvr,_vg,_vb, \
+ sampling, \
+ __depth__); \
+ \
+ ublocks[blocknum(x_tm + i, y_tm + k, (yuv)->y_width, Y_UNIT_WIDTH)] = 1; \
+ vblocks[blocknum(x_tm + i, y_tm + k, (yuv)->y_width, Y_UNIT_WIDTH)] = 1; \
+ } \
+ \
+ datapi += 2; \
+ datapi_back += 2; \
+ datapi_next += 2; \
+ datapi_back_next += 2; \
+ \
+ yuv_U++; \
+ yuv_V++; \
+ } \
+ \
+ yuv_U += ((yuv)->y_stride - (width_tm - w_odd * 2)) >> 1; \
+ yuv_V += ((yuv)->y_stride - (width_tm - w_odd * 2)) >> 1; \
+ \
+ datapi += width_tm + w_odd; \
+ datapi_back += width_tm + w_odd; \
+ datapi_next += width_tm + w_odd; \
+ datapi_back_next += width_tm + w_odd; \
+ } \
+ } else { \
+ for (int k = 0; k < height_tm - h_odd; k += 2) { \
+ for (int i = 0; i < width_tm - w_odd; i += 2) { \
+ if ((*datapi != *datapi_back)) { \
+ UPDATE_A_UV_PIXEL( yuv_U, \
+ yuv_V, \
+ t_val, \
+ datapi, \
+ datapi_next, \
+ _ur, _ug, _ubvr, _vg, _vb, \
+ sampling, \
+ __depth__); \
+ \
+ ublocks[blocknum(x_tm + i, y_tm + k, (yuv)->y_width, Y_UNIT_WIDTH)] = 1; \
+ vblocks[blocknum(x_tm + i, y_tm + k, (yuv)->y_width, Y_UNIT_WIDTH)] = 1; \
+ } \
+ \
+ datapi += 2; \
+ datapi_back += 2; \
+ \
+ yuv_U++; \
+ yuv_V++; \
+ } \
+ \
+ yuv_U += ((yuv)->y_stride - (width_tm - w_odd * 2)) >> 1; \
+ yuv_V += ((yuv)->y_stride - (width_tm - w_odd * 2)) >> 1; \
+ \
+ datapi += width_tm + w_odd; \
+ datapi_back += width_tm + w_odd; \
+ } \
+ } \
+}
+
+void rmdUpdateYuvBuffer( yuv_buffer *yuv,
+ unsigned char *data,
+ unsigned char *data_back,
+ int x_tm,
+ int y_tm,
+ int width_tm,
+ int height_tm,
+ int sampling,
+ int depth) {
+
+ if (data_back == NULL) {
+ switch (depth) {
+ case 24:
+ case 32:
+ UPDATE_Y_PLANE(data, x_tm, y_tm, width_tm, height_tm, yuv, 32);
+ UPDATE_UV_PLANES(data, x_tm, y_tm, width_tm, height_tm, yuv, sampling, 32);
+ break;
+ case 16:
+ UPDATE_Y_PLANE(data, x_tm, y_tm, width_tm, height_tm, yuv, 16);
+ UPDATE_UV_PLANES(data, x_tm, y_tm, width_tm, height_tm, yuv, sampling, 16);
+ break;
+ default:
+ assert(0);
+ }
+ } else {
+ switch (depth) {
+ case 24:
+ case 32:
+ UPDATE_Y_PLANE_DBUF(data, data_back, x_tm, y_tm, width_tm, height_tm, yuv, 32);
+ UPDATE_UV_PLANES_DBUF(data, data_back, x_tm, y_tm, width_tm, height_tm, yuv, sampling, 32);
+ break;
+ case 16:
+ UPDATE_Y_PLANE_DBUF(data, data_back, x_tm, y_tm, width_tm, height_tm, yuv, 16);
+ UPDATE_UV_PLANES_DBUF(data, data_back, x_tm, y_tm, width_tm, height_tm, yuv, sampling, 16);
+ break;
+ default:
+ assert(0);
+ }
+ }
+}
+
+void rmdDummyPointerToYuv( yuv_buffer *yuv,
+ unsigned char *data_tm,
+ int x_tm,
+ int y_tm,
+ int width_tm,
+ int height_tm,
+ int x_offset,
+ int y_offset,
+ unsigned char no_pixel) {
+
+ int i, k, j = 0;
+ int x_2 = x_tm / 2, y_2 = y_tm / 2, y_width_2 = yuv->y_width/2;
+
+ for (k = y_offset; k < y_offset + height_tm; k++) {
+ for (i = x_offset; i < x_offset + width_tm; i++) {
+ j = k * 16 + i;
+
+ if (data_tm[j * 4] != no_pixel) {
+ yuv->y[x_tm + (i - x_offset) + ((k - y_offset) + y_tm) * yuv->y_width] =
+ Yr[data_tm[j * 4 + __RBYTE]] +
+ Yg[data_tm[j * 4 + __GBYTE]] +
+ Yb[data_tm[j * 4 + __BBYTE]];
+
+ if ((k % 2) && (i % 2)) {
+ yuv->u[x_2 + (i - x_offset) / 2 + ((k - y_offset) / 2 + y_2) * y_width_2] =
+ Ur[data_tm[(k * width_tm + i) * 4 + __RBYTE]] +
+ Ug[data_tm[(k * width_tm + i) * 4 + __GBYTE]] +
+ UbVr[data_tm[(k * width_tm + i) * 4 + __BBYTE]];
+ yuv->v[x_2 + (i - x_offset) / 2 + ((k - y_offset) / 2 + y_2) * y_width_2] =
+ UbVr[data_tm[(k * width_tm + i) * 4 + __RBYTE]] +
+ Vg[data_tm[(k * width_tm + i) * 4 + __GBYTE]] +
+ Vb[data_tm[(k * width_tm + i) * 4 + __BBYTE]] ;
+ }
+ }
+ }
+ }
+
+}
+
+static inline unsigned char avg_4_pixels( unsigned char *data_array,
+ int width_img,
+ int k_tm,
+ int i_tm,
+ int offset)
+{
+
+ return ((data_array[(k_tm*width_img+i_tm)*RMD_ULONG_SIZE_T+offset]+
+ data_array[((k_tm-1)*width_img+i_tm)*RMD_ULONG_SIZE_T+offset]+
+ data_array[(k_tm*width_img+i_tm-1)*RMD_ULONG_SIZE_T+offset]+
+ data_array[((k_tm-1)*width_img+i_tm-1)*RMD_ULONG_SIZE_T+offset])/4);
+}
+
+void rmdXFixesPointerToYuv( yuv_buffer *yuv,
+ unsigned char *data_tm,
+ int x_tm,
+ int y_tm,
+ int width_tm,
+ int height_tm,
+ int x_offset,
+ int y_offset,
+ int column_discard_stride) {
+
+ unsigned char avg0, avg1, avg2, avg3;
+ int x_2 = x_tm / 2, y_2 = y_tm / 2;
+
+ for (int k = y_offset; k < y_offset + height_tm; k++) {
+ for (int i = x_offset;i < x_offset + width_tm; i++) {
+ int j = k * (width_tm + column_discard_stride) + i;
+
+ yuv->y[x_tm + (i - x_offset) + (k + y_tm - y_offset) * yuv->y_width] =
+ (yuv->y[x_tm + (i - x_offset) + (k - y_offset + y_tm) * yuv->y_width] *
+ (UCHAR_MAX - data_tm[(j * RMD_ULONG_SIZE_T) + __ABYTE]) +
+ ( ( Yr[data_tm[(j * RMD_ULONG_SIZE_T) + __RBYTE]] +
+ Yg[data_tm[(j * RMD_ULONG_SIZE_T) + __GBYTE]] +
+ Yb[data_tm[(j * RMD_ULONG_SIZE_T) + __BBYTE]] ) %
+ ( UCHAR_MAX + 1 ) ) *
+ data_tm[(j * RMD_ULONG_SIZE_T) + __ABYTE]) / UCHAR_MAX;
+
+ if ((k % 2) && (i % 2)) {
+ int idx = x_2 + (i - x_offset) / 2 + ((k - y_offset) / 2 + y_2) * yuv->uv_width;
+
+ avg3 = avg_4_pixels( data_tm,
+ (width_tm + column_discard_stride),
+ k, i, __ABYTE);
+ avg2 = avg_4_pixels( data_tm,
+ (width_tm + column_discard_stride),
+ k, i, __RBYTE);
+ avg1 = avg_4_pixels( data_tm,
+ (width_tm + column_discard_stride),
+ k, i, __GBYTE);
+ avg0 = avg_4_pixels( data_tm,
+ (width_tm + column_discard_stride),
+ k, i, __BBYTE);
+
+ yuv->u[idx] =
+ (yuv->u[idx] * (UCHAR_MAX - avg3) +
+ ((Ur[avg2] + Ug[avg1] + UbVr[avg0]) % (UCHAR_MAX + 1))
+ * avg3) / UCHAR_MAX;
+
+ yuv->v[idx]=
+ (yuv->u[idx] * (UCHAR_MAX - avg3) +
+ ((UbVr[avg2] + Vg[avg1] + Vb[avg0]) % (UCHAR_MAX + 1))
+ * avg3) / UCHAR_MAX;
+ }
+ }
+ }
+}
diff --git a/src/rmd_yuv_utils.h b/src/rmd_yuv_utils.h
new file mode 100644
index 0000000..f92471a
--- /dev/null
+++ b/src/rmd_yuv_utils.h
@@ -0,0 +1,83 @@
+/******************************************************************************
+* recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2006,2007,2008 John Varouhakis *
+* *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+* *
+* *
+* For further information contact me at johnvarouhakis@gmail.com *
+******************************************************************************/
+
+#ifndef YUV_UTILS_H
+#define YUV_UTILS_H 1
+
+#include "rmd_macro.h"
+#include "rmd_types.h"
+
+
+// The macros work directly on this data (for performance reasons I
+// suppose) so we keep this global
+extern unsigned char Yr[256], Yg[256], Yb[256],
+ Ur[256], Ug[256], UbVr[256],
+ Vg[256], Vb[256];
+
+
+// We keep these global for now. FIXME: Isolate them.
+extern unsigned char *yblocks,
+ *ublocks,
+ *vblocks;
+
+
+/**
+* Fill Yr,Yg,Yb,Ur,Ug.Ub,Vr,Vg,Vb arrays(globals) with values.
+*/
+void rmdMakeMatrices(void);
+
+/* update yuv from data and optionally data_back */
+void rmdUpdateYuvBuffer( yuv_buffer *yuv,
+ unsigned char *data,
+ unsigned char *data_back,
+ int x_tm,
+ int y_tm,
+ int width_tm,
+ int height_tm,
+ int sampling_type,
+ int color_depth);
+
+void rmdDummyPointerToYuv( yuv_buffer *yuv,
+ unsigned char *data_tm,
+ int x_tm,
+ int y_tm,
+ int width_tm,
+ int height_tm,
+ int x_offset,
+ int y_offset,
+ unsigned char no_pixel);
+
+void rmdXFixesPointerToYuv( yuv_buffer *yuv,
+ unsigned char *data_tm,
+ int x_tm,
+ int y_tm,
+ int width_tm,
+ int height_tm,
+ int x_offset,
+ int y_offset,
+ int column_discard_stride);
+
+#endif
diff --git a/src/skeleton.c b/src/skeleton.c
new file mode 100644
index 0000000..77567db
--- /dev/null
+++ b/src/skeleton.c
@@ -0,0 +1,296 @@
+/*
+ * skeleton.c
+ * author: Tahseen Mohammad
+ */
+
+/* This file depends on WORDS_BIGENDIAN being defined to 1 if the host
+ * processor stores words with the most significant byte first (like Motorola
+ * and SPARC, unlike Intel and VAX).
+ * On little endian systems, WORDS_BIGENDIAN should be undefined.
+ *
+ * When using GNU Autotools, the correct value will be written into config.h
+ * if the autoconf macro AC_C_BIGENDIAN is called in configure.ac.
+ */
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <ogg/ogg.h>
+
+#include "skeleton.h"
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+static unsigned short
+_le_16 (unsigned short s)
+{
+ unsigned short ret=s;
+#ifdef WORDS_BIGENDIAN
+ ret = (s>>8) & 0x00ffU;
+ ret += (s<<8) & 0xff00U;
+#endif
+ return ret;
+}
+
+static ogg_uint32_t
+_le_32 (ogg_uint32_t i)
+{
+ ogg_uint32_t ret=i;
+#ifdef WORDS_BIGENDIAN
+ ret = (i>>24);
+ ret += (i>>8) & 0x0000ff00;
+ ret += (i<<8) & 0x00ff0000;
+ ret += (i<<24);
+#endif
+ return ret;
+}
+
+static ogg_int64_t
+_le_64 (ogg_int64_t l)
+{
+ ogg_int64_t ret=l;
+ unsigned char *ucptr = (unsigned char *)&ret;
+#ifdef WORDS_BIGENDIAN
+ unsigned char temp;
+
+ temp = ucptr [0] ;
+ ucptr [0] = ucptr [7] ;
+ ucptr [7] = temp ;
+
+ temp = ucptr [1] ;
+ ucptr [1] = ucptr [6] ;
+ ucptr [6] = temp ;
+
+ temp = ucptr [2] ;
+ ucptr [2] = ucptr [5] ;
+ ucptr [5] = temp ;
+
+ temp = ucptr [3] ;
+ ucptr [3] = ucptr [4] ;
+ ucptr [4] = temp ;
+
+#endif
+ return (*(ogg_int64_t *)ucptr);
+}
+
+/* write an ogg_page to a file pointer */
+int write_ogg_page_to_file(ogg_page *og, FILE *out) {
+ int written;
+
+ written = fwrite(og->header,1, og->header_len, out);
+ if (written > 0)
+ written += fwrite(og->body,1, og->body_len, out);
+
+ return written;
+}
+
+int add_message_header_field(fisbone_packet *fp,
+ char *header_key,
+ char *header_value) {
+
+ /* size of both key and value + ': ' + CRLF */
+ int this_message_size = strlen(header_key) + strlen(header_value) + 4;
+ if (fp->message_header_fields == NULL) {
+ fp->message_header_fields = _ogg_calloc(this_message_size+1, sizeof(char));
+ } else {
+ int new_size = (fp->current_header_size + this_message_size+1) * sizeof(char);
+ fp->message_header_fields = _ogg_realloc(fp->message_header_fields, new_size);
+ }
+ snprintf(fp->message_header_fields + fp->current_header_size,
+ this_message_size+1,
+ "%s: %s\r\n",
+ header_key,
+ header_value);
+ fp->current_header_size += this_message_size;
+
+ return 0;
+}
+
+/* create a ogg_packet from a fishead_packet structure */
+int ogg_from_fishead(fishead_packet *fp,ogg_packet *op) {
+
+ if (!fp || !op) return -1;
+
+ memset(op, 0, sizeof(*op));
+ op->packet = _ogg_calloc(FISHEAD_SIZE, sizeof(unsigned char));
+ if (!op->packet) return -1;
+
+ memset(op->packet, 0, FISHEAD_SIZE);
+
+ memcpy (op->packet, FISHEAD_IDENTIFIER, 8); /* identifier */
+ *((ogg_uint16_t*)(op->packet+8)) = _le_16 (SKELETON_VERSION_MAJOR); /* version major */
+ *((ogg_uint16_t*)(op->packet+10)) = _le_16 (SKELETON_VERSION_MINOR); /* version minor */
+ *((ogg_int64_t*)(op->packet+12)) = _le_64 (fp->ptime_n); /* presentationtime numerator */
+ *((ogg_int64_t*)(op->packet+20)) = _le_64 (fp->ptime_d); /* presentationtime denominator */
+ *((ogg_int64_t*)(op->packet+28)) = _le_64 (fp->btime_n); /* basetime numerator */
+ *((ogg_int64_t*)(op->packet+36)) = _le_64 (fp->btime_d); /* basetime denominator */
+ /* TODO: UTC time, set to zero for now */
+
+ op->b_o_s = 1; /* its the first packet of the stream */
+ op->e_o_s = 0; /* its not the last packet of the stream */
+ op->bytes = FISHEAD_SIZE; /* length of the packet in bytes */
+
+ return 0;
+}
+
+/* create a ogg_packet from a fisbone_packet structure.
+ * call this method after the fisbone_packet is filled and all message header fields are added
+ * by calling add_message_header_field method.
+ */
+int ogg_from_fisbone(fisbone_packet *fp,ogg_packet *op) {
+
+ int packet_size;
+
+ if (!fp || !op) return -1;
+
+ packet_size = FISBONE_SIZE + fp->current_header_size;
+
+ memset (op, 0, sizeof (*op));
+ op->packet = _ogg_calloc (packet_size, sizeof(unsigned char));
+ if (!op->packet) return -1;
+
+ memset (op->packet, 0, packet_size);
+ memcpy (op->packet, FISBONE_IDENTIFIER, 8); /* identifier */
+ *((ogg_uint32_t*)(op->packet+8)) = _le_32 (FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
+ *((ogg_uint32_t*)(op->packet+12)) = _le_32 (fp->serial_no); /* serialno of the respective stream */
+ *((ogg_uint32_t*)(op->packet+16)) = _le_32 (fp->nr_header_packet); /* number of header packets */
+ *((ogg_int64_t*)(op->packet+20)) = _le_64 (fp->granule_rate_n); /* granulrate numerator */
+ *((ogg_int64_t*)(op->packet+28)) = _le_64 (fp->granule_rate_d); /* granulrate denominator */
+ *((ogg_int64_t*)(op->packet+36)) = _le_64 (fp->start_granule); /* start granule */
+ *((ogg_uint32_t*)(op->packet+44)) = _le_32 (fp->preroll); /* preroll, for theora its 0 */
+ *(op->packet+48) = fp->granule_shift; /* granule shift */
+ if (fp->message_header_fields)
+ memcpy((op->packet+FISBONE_SIZE), fp->message_header_fields, fp->current_header_size);
+
+
+ op->b_o_s = 0;
+ op->e_o_s = 0;
+ op->bytes = packet_size; /* size of the packet in bytes */
+
+ return 0;
+}
+
+/* fills up a fishead_packet from memory */
+static int fishead_from_data (const unsigned char * data, int len, fishead_packet *fp) {
+ if (!data) return -1;
+
+ if (memcmp(data, FISHEAD_IDENTIFIER, 8))
+ return -1;
+
+ fp->version_major = _le_16 (*((ogg_uint16_t*)(data+8))); /* version major */
+ fp->version_minor = _le_16 (*((ogg_uint16_t*)(data+10))); /* version minor */
+ fp->ptime_n = _le_64 (*((ogg_int64_t*)(data+12))); /* presentationtime numerator */
+ fp->ptime_d = _le_64 (*((ogg_int64_t*)(data+20))); /* presentationtime denominator */
+ fp->btime_n = _le_64 (*((ogg_int64_t*)(data+28))); /* basetime numerator */
+ fp->btime_d = _le_64 (*((ogg_int64_t*)(data+36))); /* basetime denominator */
+ memcpy(fp->UTC, data+44, 20);
+
+ return 0;
+}
+
+/* fills up a fishead_packet from a fishead ogg_packet of a skeleton bistream */
+int fishead_from_ogg (ogg_packet *op, fishead_packet *fp) {
+ return fishead_from_data (op->packet, op->bytes, fp);
+}
+
+/* fills up a fishead_packet from a fishead ogg_page of a skeleton bistream */
+int fishead_from_ogg_page (const ogg_page *og, fishead_packet *fp) {
+ return fishead_from_data (og->body, og->body_len, fp);
+}
+
+/* fills up a fisbone_packet from memory */
+static int fisbone_from_data (const unsigned char * data, int len, fisbone_packet *fp) {
+
+ if (!fp) return -1;
+
+ if (memcmp(data, FISBONE_IDENTIFIER, 8))
+ return -1;
+
+ fp->serial_no = _le_32 (*((ogg_uint32_t*)(data+12))); /* serialno of the stream represented by this fisbone packet */
+ fp->nr_header_packet = _le_32 (*((ogg_uint32_t*)(data+16))); /* number of header packets */
+ fp->granule_rate_n = _le_64 (*((ogg_int64_t*)(data+20))); /* granulrate numerator */
+ fp->granule_rate_d = _le_64 (*((ogg_int64_t*)(data+28))); /* granulrate denominator */
+ fp->start_granule = _le_64 (*((ogg_int64_t*)(data+36))); /* start granule */
+ fp->preroll = _le_32 (*((ogg_uint32_t*)(data+44))); /* preroll, for theora its 0 */
+ fp->granule_shift = *(data+48); /* granule shift */
+ fp->current_header_size = len - FISBONE_SIZE;
+ fp->message_header_fields = _ogg_calloc(fp->current_header_size+1, sizeof(char));
+ if (!fp->message_header_fields) return -1;
+ memcpy(fp->message_header_fields, data+FISBONE_SIZE, fp->current_header_size);
+
+ return 0;
+}
+
+/* fills up a fisbone_packet from a fisbone ogg_packet of a skeleton bitstream */
+int fisbone_from_ogg (ogg_packet *op, fisbone_packet *fp) {
+ return fisbone_from_data (op->packet, op->bytes, fp);
+}
+
+/* fills up a fisbone_packet from a fisbone ogg_page of a skeleton bistream */
+int fisbone_from_ogg_page (const ogg_page *og, fisbone_packet *fp) {
+ return fisbone_from_data (og->body, og->body_len, fp);
+}
+
+int fisbone_clear(fisbone_packet *fp)
+{
+ if (!fp) return -1;
+ _ogg_free(fp->message_header_fields);
+ return 0;
+}
+
+int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp) {
+
+ ogg_packet op;
+ int ret;
+
+ ret = ogg_from_fishead(fp, &op);
+ if (ret<0) return ret;
+ ogg_stream_packetin(os, &op);
+ _ogg_free(op.packet);
+
+ return 0;
+}
+
+int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp) {
+
+ ogg_packet op;
+ int ret;
+
+ ret = ogg_from_fisbone(fp, &op);
+ if (ret<0) return ret;
+ ogg_stream_packetin(os, &op);
+ _ogg_free(op.packet);
+
+ return 0;
+}
+
+int add_eos_packet_to_stream(ogg_stream_state *os) {
+
+ ogg_packet op;
+
+ memset (&op, 0, sizeof(op));
+ op.e_o_s = 1;
+ ogg_stream_packetin(os, &op);
+
+ return 0;
+}
+
+int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out) {
+
+ ogg_page og;
+ int result;
+
+ while((result = ogg_stream_flush(os, &og)))
+ {
+ if(!result) break;
+ result = write_ogg_page_to_file(&og, out);
+ if(result != og.header_len + og.body_len)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/skeleton.h b/src/skeleton.h
new file mode 100644
index 0000000..d088e3e
--- /dev/null
+++ b/src/skeleton.h
@@ -0,0 +1,78 @@
+/*
+ * skeleton.h
+ * author: Tahseen Mohammad
+ */
+
+#ifndef _SKELETON_H
+#define _SKELETON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <ogg/ogg.h>
+
+#define SKELETON_VERSION_MAJOR 3
+#define SKELETON_VERSION_MINOR 0
+#define FISHEAD_IDENTIFIER "fishead\0"
+#define FISBONE_IDENTIFIER "fisbone\0"
+#define FISHEAD_SIZE 64
+#define FISBONE_SIZE 52
+#define FISBONE_MESSAGE_HEADER_OFFSET 44
+
+/* fishead_packet holds a fishead header packet. */
+typedef struct {
+ ogg_uint16_t version_major; /* skeleton version major */
+ ogg_uint16_t version_minor; /* skeleton version minor */
+ /* Start time of the presentation
+ * For a new stream presentationtime & basetime is same. */
+ ogg_int64_t ptime_n; /* presentation time numerator */
+ ogg_int64_t ptime_d; /* presentation time denominator */
+ ogg_int64_t btime_n; /* basetime numerator */
+ ogg_int64_t btime_d; /* basetime denominator */
+ /* will holds the time of origin of the stream, a 20 bit field. */
+ unsigned char UTC[20];
+} fishead_packet;
+
+/* fisbone_packet holds a fisbone header packet. */
+typedef struct {
+ ogg_uint32_t serial_no; /* serial no of the corresponding stream */
+ ogg_uint32_t nr_header_packet; /* number of header packets */
+ /* granule rate is the temporal resolution of the logical bitstream */
+ ogg_int64_t granule_rate_n; /* granule rate numerator */
+ ogg_int64_t granule_rate_d; /* granule rate denominator */
+ ogg_int64_t start_granule; /* start granule value */
+ ogg_uint32_t preroll; /* preroll */
+ unsigned char granule_shift; /* 1 byte value holding the granule shift */
+ char *message_header_fields; /* holds all the message header fields */
+ /* current total size of the message header fields, for realloc purpose, initially zero */
+ ogg_uint32_t current_header_size;
+} fisbone_packet;
+
+extern int write_ogg_page_to_file(ogg_page *og, FILE *out);
+extern int add_message_header_field(fisbone_packet *fp, char *header_key, char *header_value);
+/* remember to deallocate the returned ogg_packet properly */
+extern int ogg_from_fishead(fishead_packet *fp,ogg_packet *op);
+extern int ogg_from_fisbone(fisbone_packet *fp,ogg_packet *op);
+extern int fisbone_clear(fisbone_packet *fp);
+extern int fishead_from_ogg(ogg_packet *op,fishead_packet *fp);
+extern int fisbone_from_ogg(ogg_packet *op,fisbone_packet *fp);
+extern int fishead_from_ogg_page(const ogg_page *og,fishead_packet *fp);
+extern int fisbone_from_ogg_page(const ogg_page *og,fisbone_packet *fp);
+extern int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp);
+extern int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp);
+extern int add_eos_packet_to_stream(ogg_stream_state *os);
+extern int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SKELETON_H */
+
+
+
+
+
+
diff --git a/src/test-rectinsert-data.c b/src/test-rectinsert-data.c
new file mode 100644
index 0000000..9540a5f
--- /dev/null
+++ b/src/test-rectinsert-data.c
@@ -0,0 +1,382 @@
+/******************************************************************************
+* for recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2008 Martin Nordholts *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+******************************************************************************/
+
+#include "config.h"
+#include "test-rectinsert-data.h"
+
+#include "test-rectinsert-types.h"
+
+
+// Defines to increase readability of test data
+#define O FALSE
+#define E TRUE
+
+
+RectInsertTestdataEntry rectinsert_test_data[] = {
+
+ // Test #1
+
+ { "Put a rect in place",
+
+ { 2, 2, 2, 2 },
+
+ { O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #2
+
+ { "Put a rect in place and make sure it gets an even size and position",
+
+ { 15, 2, 4, 3 },
+
+ { O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #3
+
+ { "Put a new rect within an existing rect",
+
+ { 14, 2, 4, 2 },
+
+ { O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #4
+
+ { "Put a new rect over an existing rect and make sure it beocomes even",
+
+ { 1, 1, 3, 3 },
+
+ { E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+
+ // Test #5
+
+ { "Put a new rect that partly covers an existing rect",
+
+ { 10, 4, 6, 6 },
+
+ { E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #6
+
+ { "Put new small heighted but wide rect over an existing rect area",
+
+ { 0, 8, 20, 2 },
+
+ { E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,O,O,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #7
+
+ { "Put new small widthed but high rect over two existing rect areas",
+
+ { 2, 2, 2, 18 },
+
+ { E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #8
+
+ { "Prepare for test 10",
+
+ { 8, 14, 4, 4 },
+
+ { E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,O,O,O,O,
+
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #9
+
+ { "Prepare for test 10 (again)",
+
+ { 16, 14, 2, 2 },
+
+ { E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,E,E,O,O,
+
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,E,E,O,O,
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,E,E, E,E,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O } },
+
+
+ // Test #10
+
+ { "Put a rect that covers two separate rects",
+
+ { 6, 12, 14, 8 },
+
+ { E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ E,E,E,E,O, O,O,O,O,O, O,O,O,O,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, E,E,E,E,E, E,O,O,O,O,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,O,O,O,O, O,O,O,O,O, O,O,O,O,O,
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ O,O,E,E,O, O,E,E,E,E, E,E,E,E,E, E,E,E,E,E } },
+
+
+ // Test #11
+
+ { "Cover the whole area",
+
+ { 0, 0, 20, 20 },
+
+ { E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E,
+ E,E,E,E,E, E,E,E,E,E, E,E,E,E,E, E,E,E,E,E } },
+
+
+ // Mark end of tests
+
+ { NULL, }
+};
+
diff --git a/src/test-rectinsert-data.h b/src/test-rectinsert-data.h
new file mode 100644
index 0000000..7957b30
--- /dev/null
+++ b/src/test-rectinsert-data.h
@@ -0,0 +1,30 @@
+/******************************************************************************
+* for recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2008 Martin Nordholts *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+******************************************************************************/
+
+#ifndef TEST_RECTINSERT_DATA_H
+#define TEST_RECTINSERT_DATA_H
+
+#include "test-rectinsert-types.h"
+
+extern RectInsertTestdataEntry rectinsert_test_data[];
+
+#endif /* TEST_RECTINSERT_DATA_H */
diff --git a/src/test-rectinsert-types.h b/src/test-rectinsert-types.h
new file mode 100644
index 0000000..b39322a
--- /dev/null
+++ b/src/test-rectinsert-types.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+* for recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2008 Martin Nordholts *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+******************************************************************************/
+
+#ifndef TEST_RECTINSERT_TYPES_H
+#define TEST_RECTINSERT_TYPES_H
+
+
+#include "rmd_types.h"
+
+
+#define STATE_WIDTH 20
+#define STATE_HEIGHT 20
+
+
+typedef struct RectInsertTestdataEntry {
+ const char *description;
+ XRectangle new_rect;
+ boolean expected_state[STATE_WIDTH * STATE_HEIGHT];
+} RectInsertTestdataEntry;
+
+
+#endif /* TEST_RECTINSERT_TYPES_H */
diff --git a/src/test-rectinsert.c b/src/test-rectinsert.c
new file mode 100644
index 0000000..71d2030
--- /dev/null
+++ b/src/test-rectinsert.c
@@ -0,0 +1,177 @@
+/******************************************************************************
+* for recordMyDesktop *
+*******************************************************************************
+* *
+* Copyright (C) 2008 Martin Nordholts *
+* *
+* 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, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+******************************************************************************/
+
+#include "config.h"
+#include "rmd_rectinsert.h"
+
+#include "test-rectinsert-types.h"
+#include "test-rectinsert-data.h"
+#include "rmd_types.h"
+
+
+
+static void rmdSetPixel(boolean *state, int x, int y, boolean value) {
+ int index = y * STATE_WIDTH + x;
+
+ // Guard against non-optimal implementations
+ if (index < 0 || index >= STATE_WIDTH * STATE_HEIGHT) {
+ return;
+ }
+
+ state[y * STATE_WIDTH + x] = value;
+}
+
+static boolean rmdGetPixel(boolean *state, int x, int y) {
+ int index = y * STATE_WIDTH + x;
+
+ // Guard against non-optimal implementations
+ if (index < 0 || index >= STATE_WIDTH * STATE_HEIGHT) {
+ return FALSE;
+ }
+
+ return state[y * STATE_WIDTH + x];
+}
+
+static void rmdWriteGeomToState(XRectangle *rect, boolean *state) {
+ int x, y;
+
+ for (y = rect->y; y < rect->y + rect->height; y++) {
+ for (x = rect->x; x < rect->x + rect->width; x++) {
+ rmdSetPixel(state, x, y, TRUE);
+ }
+ }
+}
+
+static void rmdClearState(boolean *state) {
+ int x, y;
+
+ for (y = 0; y < STATE_HEIGHT; y++) {
+ for (x = 0; x < STATE_WIDTH; x++) {
+ rmdSetPixel(state, x, y, FALSE);
+ }
+ }
+}
+
+static void rmdWarnIfNonOptimal(XRectangle *rect) {
+ if (rect->x < 0 || rect->x >= STATE_WIDTH ||
+ rect->y < 0 || rect->y >= STATE_HEIGHT ||
+ rect->width == 0 || rect->width > STATE_WIDTH ||
+ rect->height == 0 || rect->height > STATE_HEIGHT)
+ {
+ // The rmdRectInsert() implementation is not optimal
+ printf(" Non-optimal rect (and rmdRectInsert() implementation) encountered!\n"
+ " rect x = %d, y = %d, width = %hu, height = %hu\n",
+ rect->x,
+ rect->y,
+ rect->width,
+ rect->height);
+ }
+}
+
+static void GetState(RectArea *root, boolean *state) {
+ RectArea *current = root;
+
+ rmdClearState(state);
+
+ while (current)
+ {
+ rmdWarnIfNonOptimal(&current->rect);
+
+ rmdWriteGeomToState(&current->rect, state);
+
+ current = current->next;
+ }
+}
+
+static boolean rmdStatesEqual(boolean *a, boolean *b) {
+ int x, y;
+
+ for (y = 0; y < STATE_HEIGHT; y++) {
+ for (x = 0; x < STATE_WIDTH; x++) {
+ if (rmdGetPixel(a, x, y) != rmdGetPixel(b, x, y)) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void rmdPrintState(boolean *state) {
+ int x, y;
+
+ for (y = 0; y < STATE_HEIGHT; y++) {
+ printf(" ");
+
+ for (x = 0; x < STATE_WIDTH; x++) {
+ printf(rmdGetPixel(state, x, y) ? "X" : "O");
+ printf(x != STATE_WIDTH - 1 ? "," : "");
+ printf((x + 1) % 5 == 0 ? " " : "");
+ }
+
+ printf("\n");
+ printf((y + 1) % 5 == 0 ? "\n" : "");
+ }
+}
+
+/**
+ * This program tests the rmdRectInsert() functionality by calling
+ * rmdRectInsert() with various testdata and after each call comparing
+ * the current state with a predefied set of expected states.
+ */
+int main(int argc, char **argv) {
+ boolean current_state[STATE_WIDTH * STATE_HEIGHT];
+ RectArea *root = NULL;
+ int i = 0;
+ int result = 0;
+
+ printf("== Testing rmdRectInsert() ==\n");
+
+ // Run until there we find end of tests data
+ while (rectinsert_test_data[i].description != NULL) {
+
+ printf("Test #%d: %s\n", i + 1, rectinsert_test_data[i].description);
+ rmdRectInsert(&root, &rectinsert_test_data[i].new_rect);
+ GetState(root, current_state);
+
+ if (!rmdStatesEqual(current_state, rectinsert_test_data[i].expected_state)) {
+ printf(" FAILURE!\n");
+ printf(" Current state:\n");
+ rmdPrintState(current_state);
+
+ printf(" Expected state:\n");
+ rmdPrintState(rectinsert_test_data[i].expected_state);
+
+ // Just set to failure and keep going...
+ result = -1;
+ }
+ else {
+ printf(" PASS\n");
+ }
+
+ // Run next test
+ printf("\n");
+ i++;
+ }
+
+ return result;
+}
© All Rights Reserved