summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy White <jwhite@codeweavers.com>2012-07-03 12:55:21 -0500
committerJeremy White <jwhite@codeweavers.com>2012-07-03 12:55:21 -0500
commit37a0a2e73c98a8971a879e58b09190e6567a0f4f (patch)
tree334b4aa11e22b481bdfa42ee2fbe0c0fd2a0429f
Initial commit.
-rw-r--r--.gitignore2
-rw-r--r--Makefile9
-rw-r--r--clickat.c1452
-rwxr-xr-xmeasure203
-rw-r--r--msleep.c23
-rw-r--r--rgb_image.c1092
-rw-r--r--rgb_image.h117
7 files changed, 2898 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..460ad21
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+clickat
+msleep
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..aec5630
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+all: clickat msleep
+
+clickat: clickat.c rgb_image.c rgb_image.h
+ gcc -g -o clickat clickat.c rgb_image.c -lz -ldl -lm -lX11 -lXtst -lXt -Wl,-Bstatic -lpng12 -Wl,-Bdynamic
+
+msleep: msleep.c
+ gcc -g -o msleep msleep.c
+
+
diff --git a/clickat.c b/clickat.c
new file mode 100644
index 0000000..b3c5cde
--- /dev/null
+++ b/clickat.c
@@ -0,0 +1,1452 @@
+/*----------------------------------------------------------------------------
+** clickat.c
+** Utilty to send clicks to X Windows
+**
+** See usage() for usage instructions.
+**
+**---------------------------------------------------------------------------
+** Copyright 2003 by CodeWeavers, Inc.
+**
+** 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 <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/Intrinsic.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/times.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <ctype.h>
+
+#include <X11/extensions/XTest.h>
+#include "rgb_image.h"
+
+/*----------------------------------------------------------------------------
+** Error codes
+**--------------------------------------------------------------------------*/
+#define SUCCESS 0
+#define ERR_INVALID_ARGUMENTS 1
+#define ERR_NODISPLAY 2
+#define ERR_TIMEOUT 3
+#define ERR_BAD_MALLOC 4
+#define ERR_BAD_FILE 5
+
+/*----------------------------------------------------------------------------
+** Global variables
+**--------------------------------------------------------------------------*/
+long g_delay = -1;
+long g_timeout = -1;
+long g_repeat = -1;
+char *g_window = NULL;
+char *g_position = NULL;
+char *g_dragto = NULL;
+char *g_untilwindow = NULL;
+int g_untildeath_flag = 0;
+int g_timed_flag = 0;
+int g_noup_flag = 0;
+int g_nodown_flag = 0;
+int g_untildeath_wait_window = -1 ;
+int g_diffwid = -1;
+Window g_wid = 0;
+int g_x = -1;
+int g_y = -1;
+int g_dragto_x = -1;
+int g_dragto_y = -1;
+int g_xbutton = -1;
+float g_multiple = -1;
+char *g_keybuf = NULL;
+char *g_image = NULL;
+int g_err_margin = 7;
+int g_delay_between_keystrokes = 0;
+int g_print_flag = 1;
+
+/* Moving the mouse normally, I registered ~ 100 moves / second */
+#define TYPICAL_MOUSE_SPEED 10
+
+enum action_type { ACTION_INVALID, ACTION_CLICK, ACTION_FIND, ACTION_KEY, ACTION_SAVE_FOCUS, ACTION_RESTORE_FOCUS};
+
+enum action_type g_action = ACTION_INVALID;
+
+typedef int (*x_error_handler_type)(Display *, XErrorEvent *);
+
+x_error_handler_type g_x_error_handler = NULL;
+
+#define SLEEP_INTERVAL 1000
+
+/*----------------------------------------------------------------------------
+** usage
+**--------------------------------------------------------------------------*/
+void usage(char *argv0)
+{
+ char *bname;
+
+ bname = basename(argv0);
+ printf("%s - Utility to send clicks to X Windows.\n", bname);
+ printf("----------------------------------------------\n");
+ printf("Usage:\n");
+ printf(" %s action [--timeout ms] [--window name] [--position XxY [--dragto XxY]\n", bname);
+ printf(" %*.*s [--untilwindow name] [--untildeath wait|nowait]\n", (int) strlen(bname) + 3, (int) strlen(bname) + 3, "");
+ printf(" %*.*s [--keystroke keys] [--delay-between-keystrokes ms]\n", (int) strlen(bname) + 3, (int) strlen(bname) + 3, "");
+ printf(" %*.*s [--image file.png] [--err-margin num]\n", (int) strlen(bname) + 3, (int) strlen(bname) + 3, "");
+ printf(" %*.*s [--repeat ms] [--delay ms] [--noprint]\n", (int) strlen(bname) + 3, (int) strlen(bname) + 3, "");
+ printf("Where action can be one of:\n");
+ printf(" button<n> Send a click with the given X button number\n");
+ printf(" move Synonym for button0, which is a move with no click\n");
+ printf(" click|lclick Synonym for button1 (left click)\n");
+ printf(" rclick Synonym for button3 (right click)\n");
+ printf(" mclick Synonym for button2 (middle click)\n");
+ printf(" find Find a window\n");
+ printf(" save Save focus information\n");
+ printf(" restore Restore focus information\n");
+ printf(" key Send keystrokes consisting of the remainder of the arguments\n");
+ printf(" Keystrokes are normal text, and can also include:\n");
+ printf(" \\b (backspace), \\t (tab), \\r (return),\n");
+ printf(" \\e (escape), \\d (delete), \\n, or \\\\ (backslash)\n");
+ printf(" \\[some-name] (e.g. \\[F1]; use xev to figure it out).\n");
+ printf(" Control, Alt, and Shift can also be emulated.\n");
+ printf(" The general form is \\XM where X can be either\n");
+ printf(" s (left shift), c (left control), a (left alt)\n");
+ printf(" S (right shift), C (right control), A (right alt),\n");
+ printf(" R (ISO_Level3_Shift, often AltGr)\n");
+ printf(" and M is either '+' (press down) or '-' (release).\n");
+ printf(" Hence, Control-Q is written: \\c+q\\c-\n");
+ printf(" WARNING: Misplacing the \\X- will likely screw up your keyboard\n");
+ printf("\n");
+ printf("The options are as follows:\n");
+ printf(" --timeout ms How long to wait before failing with a code of %d\n", ERR_TIMEOUT);
+ printf(" --window name Send clicks to the specified window\n");
+ printf(" --wid wid Specify window by id, not name\n");
+ printf(" --position Specify where to send the clicks to in x/y coordinates\n");
+ printf(" --dragto If given, then position specifies start click, and\n");
+ printf(" dragto specifies release coords.\n");
+ printf(" --repeat ms If set, makes clickat repeatedly click with ms\n");
+ printf(" delay between clicks.\n");
+ printf(" --delay ms Milliseconds to delay after finding our window\n");
+ printf(" before sending any clicks.\n");
+ printf(" --timed Perform the operation for timeout period.\n");
+ printf(" --noup Just push the mouse button down; no up click.\n");
+ printf(" --nodown Just push the mouse button up; no down click.\n");
+ printf(" --untilwindow nm Will keep clicking until a Window of 'nm' appears\n");
+ printf(" --untildeath Keeps clicking until the window given in --window\n");
+ printf(" goes away\n");
+ printf(" nowait If window doesn't exist from the beginning, we won't wait for it to appear\n");
+ printf(" wait Wait for window to appear and then until it disappears\n");
+ printf(" --diffwid wid Clicks in window with specified name only if it does\n");
+ printf(" not match the window id wid\n");
+ printf(" --keystroke Keys that will be send to the destination window\n");
+ printf(" --delay-between.. Delay between single keystrokes defined in --keystroke\n");
+ printf(" --image file.png Finds the subimage file.png in either a window, specified\n");
+ printf(" by --window name, or in the root window. If the image is\n");
+ printf(" found, then a click is sent to the center of the image's\n");
+ printf(" location on the screen. If --position is specified, then\n");
+ printf(" the click is sent to that offset within the subimage.\n");
+ printf(" Multiple images can be specified by separating each filename\n");
+ printf(" with commas and no spaces (ie. --image f1.png,f2.png,f3.png)\n");
+ printf(" --err-margin num Specifies the error margin for the subimage find, from 0-100,\n");
+ printf(" where 0 is no error margin and 100 produces no errors.\n");
+ printf(" --noprint Don't print the window id.\n");
+ printf("%s returns %d on success\n", bname, SUCCESS);
+ printf("\n");
+ printf("Environment variable overrides:");
+ printf(" CXTEST_TIME_MULTIPLE Specifies a floating multiplier applied to any\n");
+ printf(" delay and timeout parameters.\n");
+}
+
+/*----------------------------------------------------------------------------
+** parse_arguments
+**--------------------------------------------------------------------------*/
+int parse_arguments(int argc, char *argv[])
+{
+ int rc;
+ int longindex = 0;
+ char *p;
+ char *untildeath_type = NULL;
+ enum option_types { OPTION_DELAY, OPTION_TIMEOUT, OPTION_WINDOW,
+ OPTION_POSITION, OPTION_DRAGTO, OPTION_REPEAT, OPTION_UNTILWINDOW,
+ OPTION_UNTILDEATH, OPTION_TIMED, OPTION_NOUP, OPTION_NODOWN, OPTION_HELP, OPTION_DIFFWID, OPTION_WID,
+ OPTION_IMAGE, OPTION_ERR_MARGIN, OPTION_KEY, OPTION_KEY_DELAY, OPTION_NOPRINT};
+
+ static struct option long_options[] =
+ {
+ {"delay", 1, 0, OPTION_DELAY },
+ {"timeout", 1, 0, OPTION_TIMEOUT },
+ {"window", 1, 0, OPTION_WINDOW },
+ {"position", 1, 0, OPTION_POSITION },
+ {"dragto", 1, 0, OPTION_DRAGTO},
+ {"repeat", 1, 0, OPTION_REPEAT },
+ {"untilwindow", 1, 0, OPTION_UNTILWINDOW },
+ {"untildeath", 1, 0, OPTION_UNTILDEATH },
+ {"timed", 0, 0, OPTION_TIMED },
+ {"noup", 0, 0, OPTION_NOUP},
+ {"nodown", 0, 0, OPTION_NODOWN},
+ {"help", 0, 0, OPTION_HELP},
+ {"diffwid", 1, 0, OPTION_DIFFWID},
+ {"wid", 1, 0, OPTION_WID},
+ {"image", 1, 0, OPTION_IMAGE},
+ {"err-margin", 1, 0, OPTION_ERR_MARGIN},
+ {"keystroke", 1, 0, OPTION_KEY},
+ {"delay-between-keystrokes", 1, 0, OPTION_KEY_DELAY},
+ {"noprint", 0, 0, OPTION_NOPRINT},
+ {0, 0, 0, 0}
+ };
+
+ while (1)
+ {
+ rc = getopt_long_only(argc, argv, "", long_options, &longindex);
+ if (rc == -1)
+ break;
+
+ switch (rc)
+ {
+ case OPTION_DELAY:
+ g_delay = atol(optarg);
+ break;
+
+ case OPTION_TIMEOUT:
+ g_timeout = atol(optarg);
+ break;
+
+ case OPTION_WINDOW:
+ g_window = strdup(optarg);
+ break;
+
+ case OPTION_REPEAT:
+ g_repeat = atol(optarg);
+ break;
+
+ case OPTION_POSITION:
+ g_position = strdup(optarg);
+ g_x = atoi(g_position);
+ p = strchr(g_position, 'x');
+ if (p)
+ g_y = atoi(p + 1);
+ break;
+
+ case OPTION_DRAGTO:
+ g_dragto = strdup(optarg);
+ g_dragto_x = atoi(g_dragto);
+ p = strchr(g_dragto, 'x');
+ if (p)
+ g_dragto_y = atoi(p + 1);
+ break;
+
+ case OPTION_UNTILWINDOW:
+ g_untilwindow = strdup(optarg);
+ break;
+
+ case OPTION_TIMED:
+ g_timed_flag = 1;
+ break;
+
+ case OPTION_NOUP:
+ g_noup_flag = 1;
+ break;
+
+ case OPTION_NODOWN:
+ g_nodown_flag = 1;
+ break;
+
+ case OPTION_NOPRINT:
+ g_print_flag = 0;
+ break;
+
+ case OPTION_UNTILDEATH:
+ g_untildeath_flag = 1;
+ untildeath_type = strdup(optarg);
+ if(strcmp(untildeath_type,"wait") == 0)
+ {
+ g_untildeath_wait_window = 1;
+ }
+ else
+ if(strcmp(untildeath_type,"nowait") == 0)
+ {
+ g_untildeath_wait_window = 0;
+ }
+ else
+ {
+ fprintf(stderr, "Error: --untildeath requires a parameter(\"wait\"|\"nowait\")\n");
+ return(1);
+ }
+ break;
+
+ case OPTION_DIFFWID:
+ g_diffwid = atoi(optarg);
+ if ( !g_diffwid)
+ g_diffwid = strtol(optarg, (char **) NULL, 16);
+ break;
+
+ case OPTION_WID:
+ g_wid = atoi(optarg);
+ if ( !g_wid)
+ g_wid = strtol(optarg, (char **) NULL, 16);
+ break;
+
+ case OPTION_IMAGE:
+ g_image = strdup(optarg);
+ break;
+
+ case OPTION_ERR_MARGIN:
+ g_err_margin = atoi(optarg);
+ break;
+
+ case OPTION_KEY:
+ g_keybuf = strdup(optarg);
+ break;
+
+ case OPTION_KEY_DELAY:
+ g_delay_between_keystrokes = strtol(optarg, (char **) NULL, 10);
+ break;
+
+ case OPTION_HELP:
+ return(2);
+ }
+
+ }
+
+ if (optind > argc - 1)
+ {
+ fprintf(stderr, "Error: You must specify an action type\n");
+ return(1);
+ }
+
+ if (strcmp(argv[optind], "key") == 0)
+ {
+ g_action = ACTION_KEY;
+ }
+
+ if (optind < argc - 1 && g_action != ACTION_KEY)
+ {
+ fprintf(stderr, "Error: too many parameters\n");
+ return(1);
+ }
+
+ if (strcmp(argv[optind], "move") == 0)
+ {
+ g_action = ACTION_CLICK;
+ g_xbutton = 0;
+ }
+ if (strcmp(argv[optind], "click") == 0 || strcmp(argv[optind], "lclick") == 0)
+ {
+ g_action = ACTION_CLICK;
+ g_xbutton = 1;
+ }
+ if (strcmp(argv[optind], "rclick") == 0)
+ {
+ g_action = ACTION_CLICK;
+ g_xbutton = 3;
+ }
+ if (strcmp(argv[optind], "mclick") == 0)
+ {
+ g_action = ACTION_CLICK;
+ g_xbutton = 2;
+ }
+ if (memcmp(argv[optind], "button", 6) == 0)
+ {
+ g_action = ACTION_CLICK;
+ g_xbutton = atoi(argv[optind] + 6);
+ }
+
+ if (strcmp(argv[optind], "save") == 0)
+ g_action = ACTION_SAVE_FOCUS;
+
+ if (strcmp(argv[optind], "restore") == 0)
+ g_action = ACTION_RESTORE_FOCUS;
+
+ if (strcmp(argv[optind], "find") == 0)
+ g_action = ACTION_FIND;
+
+ if (g_action == ACTION_INVALID)
+ {
+ fprintf(stderr, "Error: [%s] is not a valid action type\n", argv[optind]);
+ return(1);
+ }
+
+ /*------------------------------------------------------------------------
+ ** Make sure that if we are looking for an image, the error margin is
+ ** set properly (between 0 - 100)
+ **----------------------------------------------------------------------*/
+ if (g_image != NULL && (g_err_margin < 0 || g_err_margin > 100))
+ {
+ fprintf(stderr, "Error: [%d] is not a valid subimage find margin of error\n", g_err_margin);
+ return(1);
+ }
+
+ /*------------------------------------------------------------------------
+ ** Additionally, don't allow image find and a focus action
+ **----------------------------------------------------------------------*/
+ if (g_image != NULL && (g_action == ACTION_SAVE_FOCUS || g_action == ACTION_RESTORE_FOCUS))
+ {
+ fprintf(stderr, "Error: save/restore focus not valid actions with image search\n");
+ return(1);
+ }
+
+ /*------------------------------------------------------------------------
+ ** Process environment variables
+ **----------------------------------------------------------------------*/
+ p = getenv("CXTEST_TIME_MULTIPLE");
+ if (p)
+ {
+ g_multiple = atof(p);
+ if(g_multiple > 0)
+ {
+ if(g_delay != -1)
+ g_delay = (long) (((float) g_delay) * g_multiple);
+ if(g_timeout != -1)
+ g_timeout = (long) (((float) g_timeout) * g_multiple);
+ if(g_repeat != -1)
+ g_repeat = (long) (((float) g_repeat) * g_multiple);
+ }
+ }
+
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** manage_focus
+**--------------------------------------------------------------------------*/
+void manage_focus(Display *display, Window *current_focus, Window new_focus)
+{
+ int revert;
+ XGetInputFocus(display, current_focus, &revert);
+ if (new_focus)
+ XSetInputFocus(display, new_focus, revert, CurrentTime);
+}
+
+/*----------------------------------------------------------------------------
+** find_it_tool
+** Recursable routine for window IsViewable windows
+**--------------------------------------------------------------------------*/
+Window find_it_tool(Display *display, char *name, Window window, int level, int only_zero, int require_viewable)
+{
+ unsigned int count = 0, childnum=0;
+ Window root = 0, parent = 0, *children = NULL, win = 0;
+ XWindowAttributes xwinatt;
+
+ if(!level)
+ return win;
+
+ count = 0;
+ XQueryTree(display, window, &root, &parent, &children, &count);
+
+ if(!children)
+ return win;
+
+//fprintf(stderr,"%d windows\n",count);
+ // ***w/windows having the same name***
+ // with the for loop backward, the most recently focussed window is clicked
+ // with the for loop forward, the least recently focussed window is clicked
+ for(; count>0; count--)
+ {
+ childnum = count - 1;
+ char *str = NULL;
+ Status rc;
+
+ rc = XFetchName(display, children[childnum], &str);
+ if (! str)
+ {
+ XTextProperty tp;
+
+ if (XGetWMName(display, children[childnum], &tp))
+ {
+ if (tp.nitems > 0)
+ {
+ int count = 0, ret;
+ char **list = NULL;
+ ret = XmbTextPropertyToTextList(display, &tp, &list, &count);
+ if((ret == Success || ret > 0) && list != NULL && count == 1)
+ {
+ str = XtNewString(list[0]);
+ }
+ else
+ {
+ printf("value: %s\n", tp.value);
+ }
+ XFreeStringList(list);
+ }
+ }
+ }
+
+ XGetWindowAttributes(display, children[childnum], &xwinatt);
+
+ // make sure window has correct name, fits 'childnum' condition, and IsViewable
+ if( str && !strcmp(name, str) &&
+ ( (only_zero && !childnum) || !only_zero) &&
+ (!require_viewable || xwinatt.map_state == IsViewable) &&
+ ( g_diffwid == -1 || (g_diffwid > -1 && g_diffwid != children[childnum])) )
+ {
+ win = children[childnum];
+ //fprintf(stderr, "ok: window %d is >%s< --> lvl: %d, win: %d\n",childnum,str,level,(int)win);
+ break;
+ }
+ if (str)
+ XFree(str);
+
+ win = find_it_tool(display, name, children[childnum], level-1, only_zero, require_viewable);
+ if(win)
+ break;
+
+ }
+
+ XFree(children);
+
+ return win;
+}
+
+/*----------------------------------------------------------------------------
+** find_it
+** Find a window of a given name that is viewable.
+**
+** Returns 0 on failure
+**--------------------------------------------------------------------------*/
+Window find_it(Display *display, char *name, Window window, int level, int require_viewable)
+{
+ Window win = 0;
+
+ // first, check for i = 0
+ win = find_it_tool(display, name, window, level, 1, require_viewable);
+
+ // if that doesn't work, expand search criteria
+ if ( !win )
+ {
+ win = find_it_tool(display, name, window, level, 0, require_viewable);
+ }
+
+ return win;
+}
+
+/*----------------------------------------------------------------------------
+** CalculateAbsoluteCoordinates
+** Transform relative windows coordinates into absolute ones
+** Parameters:
+** 1. display X display
+** 2. win Window
+** 3-4 x,y Where to click in the window. These coordinates will
+** be changed during recursive calls. And at the end they
+** will represent absolute coordinates on the display
+**--------------------------------------------------------------------------*/
+void CalculateAbsoluteCoordinates(Display *display, Window win,int* x,int* y)
+{
+ Window parent_window, root_window, *children_return = NULL;
+ XWindowAttributes win_atr;
+ unsigned int nchildren_return;
+ int status;
+
+// fprintf(stderr, "CalculateAbsoluteCoordinates: X,Y %d,%d\n",*x,*y);
+
+ status = XQueryTree(display, win, &root_window, &parent_window, &children_return,&nchildren_return);
+ if (!status)
+ {
+ // We most likely hit the BadWindow error, do not continue in the recursion
+ return;
+ }
+
+ if (children_return != NULL)
+ XFree(children_return);
+
+ if (win == root_window)
+ {
+ return;
+ }
+ else
+ {
+ status = XGetWindowAttributes(display, win, &win_atr);
+ // The x and y coordinates define the location of the drawable. For a window,
+ // these coordinates specify the upper-left outer corner relative to its parent's origin.
+ *x += win_atr.x;
+ *y += win_atr.y;
+
+ return CalculateAbsoluteCoordinates(display, parent_window,x,y);
+ }
+}
+
+/*----------------------------------------------------------------------------
+** send_click
+** Send a click to a given window.
+**
+** Parameters:
+** 1. display X display
+** 2. win Window to click to; if 0, send to root
+** 3. type Button type (1-5, 1 is left click, 3 is right click)
+** 4-5 x,y Where in the window to click; note that these can
+** be negative
+**
+**--------------------------------------------------------------------------*/
+void send_click(Display *display, Window win, int type, int x, int y, int drag, int dragx, int dragy)
+{
+ //Window discard;
+ //int revert_state;
+ int x_abs = x;
+ int y_abs = y;
+
+ if (! win)
+ win = DefaultRootWindow(display);
+
+ CalculateAbsoluteCoordinates(display,win,&x_abs,&y_abs);
+ XTestFakeMotionEvent(display, 0, x_abs, y_abs, CurrentTime);
+ if (type == 0)
+ return;
+
+ //XGetInputFocus(display, &discard, &revert_state);
+ //XSetInputFocus(display, win, revert_state, CurrentTime);
+
+ /* Push the button down */
+ if (! g_nodown_flag)
+ XTestFakeButtonEvent(display, type, True, TYPICAL_MOUSE_SPEED);
+
+ /*------------------------------------------------------------------------
+ ** If they've asked us to 'drag' the mouse, then we have to do it over
+ ** time, mostly because most apps look for steps and time between
+ ** them to watch out for 'false' drags
+ **----------------------------------------------------------------------*/
+ if (drag)
+ {
+ int i;
+ int dx;
+ int dy;
+ int step_per_x;
+ int step_per_y;
+ int delay = TYPICAL_MOUSE_SPEED;
+ dx = dragx - x;
+ dy = dragy - y;
+
+ step_per_x = dx / 10;
+ step_per_y = dy / 10;
+
+ if (g_delay >= 0)
+ delay = g_delay;
+
+ for (i = 0; i < 10; i++)
+ {
+ XTestFakeRelativeMotionEvent(display, step_per_x, step_per_y, delay);
+ dx -= step_per_x;
+ dy -= step_per_y;
+ }
+
+ if (dx || dy)
+ XTestFakeRelativeMotionEvent(display, dx, dy, delay);
+ }
+
+ /* Push the button up */
+ if (! g_noup_flag)
+ XTestFakeButtonEvent(display, type, False, TYPICAL_MOUSE_SPEED);
+}
+
+
+/*----------------------------------------------------------------------------
+** send_key
+** Send the given keycode to the current focused window
+**
+**--------------------------------------------------------------------------*/
+void send_key(Display *display, unsigned int k, int on, int off)
+{
+ if (on)
+ XTestFakeKeyEvent(display, k, True, CurrentTime + g_delay_between_keystrokes);
+
+ if (off)
+ XTestFakeKeyEvent(display, k, False, CurrentTime);
+}
+
+/*----------------------------------------------------------------------------
+** send_string
+** Send a given string to either the current focused window or to an
+** optionally specified window at an optionally specified position.
+**
+**--------------------------------------------------------------------------*/
+int send_string(Display *display, Window win, char *position, int x, int y,
+ const unsigned char *str)
+{
+ Window discard;
+ int revert_state;
+ const unsigned char *p, *q;
+ unsigned char *r;
+ unsigned int k;
+ unsigned int c;
+ int i;
+ int on, off;
+ Window dummy_win;
+ int dummy_int;
+ int shifted;
+ int turn_lock_on = 0;
+ int turn_shift_on = 0;
+ int rc = -1;
+ KeySym lower, upper;
+ int go_low, go_high;
+
+ unsigned int mask;
+
+ struct
+ {
+ unsigned char escape_char;
+ unsigned int keysym;
+ int requires_flag;
+ } escape_array[] =
+ {
+ { 't', XK_Tab , 0 },
+ { 'b', XK_BackSpace , 0 },
+ { 'e', XK_Escape , 0 },
+ { 'd', XK_Delete , 0 },
+ { 'n', XK_Linefeed , 0 },
+ { 'r', XK_Return , 0 },
+ { '\\', XK_backslash , 0 },
+ { 's', XK_Shift_L , 1 },
+ { 'S', XK_Shift_R , 1 },
+ { 'c', XK_Control_L , 1 },
+ { 'C', XK_Control_R , 1 },
+ { 'a', XK_Alt_L , 1 },
+ { 'A', XK_Alt_R , 1 },
+ { 'R', XK_ISO_Level3_Shift, 1 },
+ };
+
+ if (str == NULL)
+ return 0;
+
+ /*------------------------------------------------------------------------
+ ** If they specified a Window, set it up as the focus window,
+ ** optionally moving the pointer too
+ **----------------------------------------------------------------------*/
+ if (win)
+ {
+ if (position)
+ {
+ int x_abs = x;
+ int y_abs = y;
+
+ CalculateAbsoluteCoordinates(display, win, &x_abs, &y_abs);
+ XTestFakeMotionEvent(display, 0, x_abs, y_abs, CurrentTime);
+ }
+
+ XGetInputFocus(display, &discard, &revert_state);
+ XSetInputFocus(display, win, revert_state, CurrentTime);
+ }
+
+ /*------------------------------------------------------------------------
+ ** Retrieve the current mask; we have to muck with the shift
+ ** state and we should restore things as we go
+ **----------------------------------------------------------------------*/
+ XQueryPointer(display, DefaultRootWindow(display), &dummy_win, &dummy_win,
+ &dummy_int, &dummy_int, &dummy_int, &dummy_int,
+ &mask);
+
+ if (mask & LockMask)
+ {
+ turn_lock_on = 1;
+ send_key(display, XKeysymToKeycode(display, XK_Caps_Lock), 0, 1);
+ }
+ shifted = mask & ShiftMask;
+ if (shifted)
+ turn_shift_on = 1;
+
+ /*------------------------------------------------------------------------
+ ** Loop through the string, decoding it as we go.
+ **----------------------------------------------------------------------*/
+ for (p = str; *p; p++)
+ {
+ c = *p;
+ k = NoSymbol;
+ r = NULL;
+ on = off = 1;
+ if (*p == '\\' && *(p + 1))
+ {
+ p++;
+
+ /*----------------------------------------------------------------
+ ** Break out symbols of the type '\[keyname]'
+ **--------------------------------------------------------------*/
+ if (*p == '[')
+ {
+ q = strchr(p, ']');
+ if (q && q > p + 1)
+ {
+ r = malloc(q - p);
+ if (r)
+ {
+ memset(r, '\0', q - p);
+ memcpy(r, p + 1, q - p - 1);
+ c = XStringToKeysym(r);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Error: malformed keyname '%s'\n", p);
+ goto ERREXIT;
+ }
+
+ p = q;
+ }
+
+ else
+ {
+ /*------------------------------------------------------------
+ ** Translate any escape codes of the type '\t', '\b', etc
+ **----------------------------------------------------------*/
+ for (i = 0; i < sizeof(escape_array) / sizeof(escape_array[0]); i++)
+ {
+ if (escape_array[i].escape_char == *p)
+ {
+ if (escape_array[i].requires_flag)
+ {
+ if (*(p + 1) == '+')
+ off = 0;
+ else if (*(p + 1) == '-')
+ on = 0;
+ else
+ {
+ fprintf(stderr, "Error: \\%c requires a following + or - (for shifted on or off)\n",
+ *p);
+ goto ERREXIT;
+ }
+ p++;
+ }
+
+ c = escape_array[i].keysym;
+ break;
+ }
+ }
+
+ if (i >= sizeof(escape_array) / sizeof(escape_array[0]))
+ {
+ fprintf(stderr, "Error: \\%c not a recognized code\n", *p);
+ goto ERREXIT;
+ }
+
+ }
+ }
+
+ /*--------------------------------------------------------------------
+ ** Determine if this is a key which requires shift key
+ ** changes; if so, put the shift key in the right place
+ ** FIXME: This logic looks kludgey, at very best
+ **------------------------------------------------------------------*/
+ XConvertCase((KeySym) c, &lower, &upper);
+ go_low = go_high = 0;
+ if (lower != upper)
+ {
+ if ((KeySym) c == lower)
+ go_low = 1;
+ else if ((KeySym) c == upper)
+ go_high = 1;
+ }
+
+ if (!go_low && !go_high)
+ {
+ if (strchr("`1234567890-=\\][';/.,", c))
+ go_low = 1;
+ else if (strchr("~!@#$%^&*()_+|}{\":?><", c))
+ go_high = 1;
+ }
+
+ if (go_low && shifted)
+ {
+ send_key(display, XKeysymToKeycode(display, XK_Shift_L), 0, 1);
+ shifted = 0;
+ }
+ else if (go_high && !shifted)
+ {
+ send_key(display, XKeysymToKeycode(display, XK_Shift_L), 1, 0);
+ shifted = 1;
+ }
+
+ /*--------------------------------------------------------------------
+ ** Look up the key code that matches the key symbol
+ **------------------------------------------------------------------*/
+ k = XKeysymToKeycode(display, (KeySym) c);
+
+ if (k == NoSymbol)
+ {
+ if (r)
+ fprintf(stderr, "Error converting [%s] to a Keysym\n", r);
+ else
+ fprintf(stderr, "Error converting 0x%x('%c') to a Keysym\n", c, c);
+ }
+ else
+ {
+ send_key(display, k, on, off);
+ }
+
+ if (r)
+ free(r);
+
+ }
+
+ /*------------------------------------------------------------------------
+ ** Restore shift state
+ **----------------------------------------------------------------------*/
+ rc = 0;
+ERREXIT:
+ if (shifted && ! turn_shift_on)
+ send_key(display, XKeysymToKeycode(display, XK_Shift_L), 0, 1);
+
+ if (! shifted && turn_shift_on)
+ send_key(display, XKeysymToKeycode(display, XK_Shift_L), 1, 0);
+
+ if (turn_lock_on)
+ send_key(display, XKeysymToKeycode(display, XK_Caps_Lock), 1, 0);
+
+ return(rc);
+}
+
+
+/*----------------------------------------------------------------------------
+** handle_xerrors
+** There is no way for us to discover that our window has disappeared
+** without risking a 'BadWindow' or 'BadMatch' error. Trap it and discard those;
+** relay any other errors that come through.
+**--------------------------------------------------------------------------*/
+int handle_xerrors(Display *display, XErrorEvent *event)
+{
+ char error_message[255];
+
+ XGetErrorText(display, event->error_code, error_message, 255);
+ fprintf(stderr, "Xlib error: %s\n", error_message);
+
+ if (event->error_code == BadWindow || event->error_code == BadMatch)
+ return(0);
+
+ if (g_x_error_handler)
+ return((*g_x_error_handler)(display, event));
+
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** relax
+** Delay for a while; generally this is going to be the
+** time given by SLEEP_PERIOD, unless we're trying to click
+** repeatedly faster than that.
+**--------------------------------------------------------------------------*/
+void relax(void)
+{
+ struct timespec timer;
+ long sleep_period = SLEEP_INTERVAL;
+
+ if (g_repeat > 0 && g_repeat < sleep_period)
+ sleep_period = g_repeat;
+
+ timer.tv_sec = sleep_period / 1000;
+ timer.tv_nsec = (sleep_period % 1000) * 1000 * 1000;
+
+ nanosleep(&timer, NULL);
+}
+
+/*----------------------------------------------------------------------------
+** mstime
+** Return time in milliseconds
+**--------------------------------------------------------------------------*/
+long mstime(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (tv.tv_sec * 1000 + (tv.tv_usec / 1000));
+}
+
+
+/*----------------------------------------------------------------------------
+** parse_multimages
+** Parses string of image files separated by commas w/out spaces,
+** allocates the appropriate memory for a rgb_image array, and then reads
+** the png files into rgb_image format.
+**
+** parameters:
+** *image_count -- the number of images contained in g_image, can be a
+** partial count if not all memory allocated properly
+** *rc -- the return code for the function
+** 0 - success
+** !0 - failure
+**--------------------------------------------------------------------------*/
+rgb_image** parse_multimages(int *image_count, int *rc)
+{
+ int i, h_count=0, icount=0, slen;
+ char *holster=NULL;
+ rgb_image **rimage = NULL;
+ *rc = SUCCESS;
+
+ /*--------------------------------------------------------------------------
+ ** Count the number of images in g_image -- count stored in icount
+ **------------------------------------------------------------------------*/
+ slen = strlen(g_image);
+ h_count=0;
+ for(i=0; i < slen; i++)
+ {
+ if(h_count > 0 && ((i+1) == slen || g_image[i] == ','))
+ {
+ icount++;
+ h_count = 0;
+ }
+ else
+ h_count++;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Allocate memory for the rgb_image array
+ **------------------------------------------------------------------------*/
+ rimage = malloc(sizeof(rgb_image*)*icount);
+ if(rimage == NULL)
+ {
+ fprintf(stderr,"Unable to allocate memory for rgb_image**\n");
+ *rc = ERR_BAD_MALLOC;
+ goto EXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Initialize the rgb_image structures -- set the *image_count, used to
+ ** deallocate memory for each rgb_image structure
+ **------------------------------------------------------------------------*/
+ for(i=0; i < icount; i++)
+ {
+ rimage[i] = NULL;
+ rimage[i] = rgb_image_init();
+ if(rimage[i] == NULL)
+ {
+ fprintf(stderr,"Unable to allocate memory for rgb_image*\n");
+ *rc = ERR_BAD_MALLOC;
+ *image_count = i;
+ goto EXIT;
+ }
+ }
+ *image_count = icount;
+
+ /*--------------------------------------------------------------------------
+ ** Allocate a string to store each image files name
+ **------------------------------------------------------------------------*/
+ holster = malloc(sizeof(char)*(slen+1));
+ if(holster == NULL)
+ {
+ fprintf(stderr,"Unable to allocate memory for string\n");
+ *rc = ERR_BAD_MALLOC;
+ goto EXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Convert the png images to rgb_images
+ **------------------------------------------------------------------------*/
+ h_count = icount = 0;
+ for(i=0; i < slen; i++)
+ {
+ /*------------------------------------------------------------------------
+ ** Convert the png file if at the end of the file name
+ **----------------------------------------------------------------------*/
+ if(h_count > 0 && ((i+1) == slen || g_image[i] == ','))
+ {
+ /*----------------------------------------------------------------------
+ ** Remember to add a '\0' to the end of the string
+ **--------------------------------------------------------------------*/
+ if((i+1) == slen && g_image[i] != ',')
+ holster[h_count++] = g_image[i];
+ holster[h_count] = '\0';
+
+ /*----------------------------------------------------------------------
+ ** Convert the png file and evaluate its success
+ **--------------------------------------------------------------------*/
+ switch(png_to_rgb_image(holster, rimage[icount]))
+ {
+ case 0:
+ icount++;
+ h_count = 0;
+ holster[0] = '\0';
+ break;
+
+ case 1:
+ *rc = ERR_BAD_FILE;
+ goto EXIT;
+
+ case 2:
+ *rc = ERR_BAD_MALLOC;
+ goto EXIT;
+ }
+ }
+ else
+ /*----------------------------------------------------------------------
+ ** If not at the end of the filename, keep appending
+ **--------------------------------------------------------------------*/
+ holster[h_count++] = g_image[i];
+ }
+
+EXIT:
+ /*--------------------------------------------------------------------------
+ ** Remember to free the memory allocated for the string holster
+ **------------------------------------------------------------------------*/
+ if(holster != NULL)
+ free(holster);
+ return(rimage);
+}
+
+int main(int argc, char *argv[])
+{
+ Display * display;
+ Window my_window = 0;
+ int rc;
+ clock_t start_ms;
+ clock_t now;
+ long start_delay = -1;
+ int done_click = 0;
+ long next_click_at = 0;
+ int been_there = 0;
+ win_image *wimage = NULL;
+ rgb_image **subimage = NULL, *image = NULL;
+ int image_count = 0, i;
+
+ /*------------------------------------------------------------------------
+ ** Parse arguments
+ **----------------------------------------------------------------------*/
+ rc = parse_arguments(argc, argv);
+ if (rc)
+ {
+ if (rc == 2)
+ usage(argv[0]);
+ else
+ fprintf(stderr, "Issue %s --help for usage.\n", argv[0]);
+ exit(ERR_INVALID_ARGUMENTS);
+ }
+
+ /*------------------------------------------------------------------------
+ ** Open the Display
+ **----------------------------------------------------------------------*/
+ display = XOpenDisplay(NULL);
+ if (! display)
+ {
+ fprintf(stderr, "Error: could not open display\n");
+ return(ERR_NODISPLAY);
+ }
+ g_x_error_handler = XSetErrorHandler(handle_xerrors);
+
+ /*------------------------------------------------------------------------
+ ** Handle requests to report the focused window
+ **----------------------------------------------------------------------*/
+ if (g_action == ACTION_SAVE_FOCUS)
+ {
+ Window win;
+ manage_focus(display, &win, 0);
+ printf("--wid 0x%lx\n", win);
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+
+ /*------------------------------------------------------------------------
+ ** Set up an override, if one is given
+ **----------------------------------------------------------------------*/
+ if (g_wid)
+ my_window = g_wid;
+
+ /*------------------------------------------------------------------------
+ ** Allocate memory for rgb_image and win_image structures if image find
+ ** and convert the png image to an rgb image for the struct subimage
+ **----------------------------------------------------------------------*/
+ if (g_image != NULL)
+ {
+ wimage = win_image_init();
+ if(wimage == NULL)
+ {
+ rc = ERR_BAD_MALLOC;
+ goto ERREXIT;
+ }
+
+ image = rgb_image_init();
+ if(image == NULL)
+ {
+ rc = ERR_BAD_MALLOC;
+ goto ERREXIT;
+ }
+
+ subimage = parse_multimages(&image_count, &rc);
+ if(rc != SUCCESS)
+ goto ERREXIT;
+ }
+
+
+ /*------------------------------------------------------------------------
+ ** Loop until we're done
+ **----------------------------------------------------------------------*/
+ for (now = start_ms = mstime(); 1 ; now = mstime())
+ {
+ /*--------------------------------------------------------------------
+ ** Test our timeout
+ **------------------------------------------------------------------*/
+ if (g_timeout > 0)
+ {
+ if (now > start_ms + g_timeout)
+ {
+ rc = ERR_TIMEOUT;
+ goto ERREXIT;
+ }
+ }
+
+ /*--------------------------------------------------------------------
+ ** Okay, if we're only supposed to do 1 click, and it's done,
+ ** saddle up and head home...
+ **------------------------------------------------------------------*/
+ if (done_click && ! g_timed_flag && ! g_untilwindow && g_untildeath_flag == 0)
+ {
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------
+ ** If we're supposed to act on a window, let's find it.
+ **------------------------------------------------------------------*/
+ if (! my_window && g_window)
+ {
+ my_window = find_it(display, g_window, DefaultRootWindow(display), 3,
+ g_action == ACTION_RESTORE_FOCUS ? 0 : 1);
+
+ /*----------------------------------------------------------------
+ ** If they want a post find delay, start that clock ticking...
+ **--------------------------------------------------------------*/
+ if (my_window && g_delay)
+ {
+ start_delay = now;
+ }
+ }
+ else if (! my_window && ! g_window && g_image)
+ {
+ /*----------------------------------------------------------------
+ ** Set my_window to be the root window
+ **--------------------------------------------------------------*/
+ my_window = DefaultRootWindow(display);
+ }
+
+ /*--------------------------------------------------------------------
+ ** If we're restoring focus, let's do that and get out
+ **------------------------------------------------------------------*/
+ if (g_action == ACTION_RESTORE_FOCUS && my_window)
+ {
+ Window win;
+ manage_focus(display, &win, my_window);
+ XMapRaised(display, my_window);
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------
+ ** If we're just finding, and we have one, we're done
+ **------------------------------------------------------------------*/
+ if (my_window && g_action == ACTION_FIND && g_untildeath_flag == 0 && g_image == NULL)
+ {
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------
+ ** If we have set --untildeath with nowait flag and we don't find
+ ** our window, we're done
+ **------------------------------------------------------------------*/
+ if (!my_window && g_untildeath_flag && g_untildeath_wait_window == 0)
+ {
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------
+ ** If our window dies, and they gave --untildeath, bail
+ **------------------------------------------------------------------*/
+ if (my_window && g_untildeath_flag)
+ {
+ if (g_image && done_click)
+ {
+ /*--------------------------------------------------------------
+ ** If the window is not the root window and is no longer
+ ** present, then success
+ **------------------------------------------------------------*/
+ if (my_window != DefaultRootWindow(display) &&
+ find_it(display, g_window, DefaultRootWindow(display), 3, 1) != my_window)
+ {
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+ /*--------------------------------------------------------------
+ ** See if any of the images are still present, if not success
+ **------------------------------------------------------------*/
+ else
+ {
+ for(i = 0; i < image_count; i++)
+ if(!rgb_image_find_subimage(image, subimage[i], g_err_margin, &g_x, &g_y))
+ break;
+
+ if(i == image_count)
+ {
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+ }
+ }
+ else if (!g_image)
+ {
+ if (find_it(display, g_window, DefaultRootWindow(display), 3, 1) != my_window)
+ {
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+ }
+ }
+
+ /*--------------------------------------------------------------------
+ ** If they were waiting until a *different* window appears, then
+ ** check for that...
+ **------------------------------------------------------------------*/
+ if (g_untilwindow)
+ {
+ if (find_it(display, g_untilwindow, DefaultRootWindow(display), 3, 1))
+ {
+ rc = SUCCESS;
+ goto ERREXIT;
+ }
+ }
+
+ /*--------------------------------------------------------------------
+ ** If this isn't the first time through the loop, let's relax so as to
+ ** not chew too much cpu.
+ **------------------------------------------------------------------*/
+ if (been_there)
+ relax();
+ else
+ been_there = 1;
+
+ /*--------------------------------------------------------------------
+ ** If there is a post find delay, then reloop until that expires
+ **------------------------------------------------------------------*/
+ if (start_delay > 0)
+ {
+ if (now > start_delay + g_delay)
+ start_delay = 0;
+ else
+ {
+ continue;
+ }
+ }
+
+ /*--------------------------------------------------------------------
+ ** If we're waiting for a window, and it isn't here, we can't
+ ** do anything
+ **------------------------------------------------------------------*/
+ if (g_window && ! my_window)
+ continue;
+
+ /*--------------------------------------------------------------------
+ ** Send clicks and keystrokes, if any
+ **------------------------------------------------------------------*/
+ if ( g_image != NULL && (! done_click || (g_repeat > 0 && now >= next_click_at)))
+ {
+ /*--------------------------------------------------------------------
+ ** Get the ximage for my_window
+ **------------------------------------------------------------------*/
+ if( my_window)
+ {
+ if(win_image_set_ximage(wimage, display, my_window))
+ {
+ rc = ERR_BAD_MALLOC;
+ goto ERREXIT;
+ }
+ if(win_image_to_rgb_image(wimage, image))
+ {
+ rc = ERR_BAD_MALLOC;
+ goto ERREXIT;
+ }
+ /*------------------------------------------------------------------
+ ** Search image with each image in subimage array
+ **----------------------------------------------------------------*/
+ for(i = 0; i < image_count; i++)
+ {
+ switch(rgb_image_find_subimage(image, subimage[i], g_err_margin, &g_x, &g_y))
+ {
+ case 1: /* subimage not found */
+ break;
+
+ case 2: /* error in parameters */
+ rc = ERR_INVALID_ARGUMENTS;
+ goto ERREXIT;
+
+ case 0: /* subimage found */
+ if (g_action == ACTION_CLICK && g_xbutton >= 0)
+ send_click(display, my_window, g_xbutton, g_x, g_y,
+ g_dragto != NULL, g_dragto_x, g_dragto_y);
+
+ if (g_action == ACTION_KEY)
+ {
+ rc = send_string(display, my_window, g_position, g_x, g_y, g_keybuf);
+ if (rc != SUCCESS)
+ goto ERREXIT;
+ }
+ done_click = 1;
+ break;
+
+ default: /* unrecognized return value */
+ rc = ERR_INVALID_ARGUMENTS;
+ goto ERREXIT;
+ }
+ }
+
+ /*------------------------------------------------------------------
+ ** No subimages found, no timeout specified, so exit
+ **----------------------------------------------------------------*/
+ if(!done_click && g_timeout == -1)
+ {
+ rc = ERR_TIMEOUT;
+ goto ERREXIT;
+ }
+ }
+
+ if (g_repeat > 0)
+ next_click_at = now + g_repeat;
+ }
+ else if (! done_click || (g_repeat > 0 && now >= next_click_at))
+ {
+ if (g_action == ACTION_CLICK && g_xbutton >= 0 && (! g_window || my_window))
+ send_click(display, my_window, g_xbutton, g_x, g_y,
+ g_dragto != NULL, g_dragto_x, g_dragto_y);
+
+ if (g_action == ACTION_KEY)
+ {
+ rc = send_string(display, my_window, g_position, g_x, g_y, g_keybuf);
+ if (rc != SUCCESS)
+ goto ERREXIT;
+ }
+
+ if (g_repeat > 0)
+ next_click_at = now + g_repeat;
+ done_click = 1;
+ }
+
+ }
+
+ rc = ERR_TIMEOUT;
+
+ERREXIT:
+ if (g_print_flag)
+ printf("%d\n", (int) my_window);
+ win_image_destroy(wimage);
+ rgb_image_destroy(image);
+ /*------------------------------------------------------------------------
+ ** Free rgb_image array
+ **----------------------------------------------------------------------*/
+ if(subimage != NULL)
+ {
+ for(i = 0; i < image_count; i++)
+ rgb_image_destroy(subimage[i]);
+
+ free(subimage);
+ }
+
+ XCloseDisplay(display);
+ exit(rc);
+}
diff --git a/measure b/measure
new file mode 100755
index 0000000..ef72ef2
--- /dev/null
+++ b/measure
@@ -0,0 +1,203 @@
+#!/bin/bash
+
+# This controls what steps to run
+do_walk=0
+do_smile=0
+do_typing=1
+
+CLICKAT=./clickat
+
+wid=0
+xoffset=0
+yoffset=0
+fname=master.ogv
+if [ $# -gt 0 ] ; then
+ if [ "$1" == "spice display 0" ] ; then
+ xoffset=0
+ yoffset=59
+ fname=spicegtk.ogv
+ fi
+
+ if [ "$1" == "TightVNC: apevia:3" ] ; then
+ xoffset=0
+ yoffset=0
+ fname=x11vnc.ogv
+ fi
+
+ if [ "$1" == "SPICEc:0" ] ; then
+ xoffset=0
+ yoffset=0
+ fname=spicec.ogv
+ fi
+
+
+ if [ "$1" == "Spice Javascript client - Google Chrome" ] ; then
+ xoffset=24
+ yoffset=122
+ fname=spicejs.ogv
+ fi
+
+ wid=`$CLICKAT find --timeout 1 --window "$1"`
+ if [ $? -ne 0 ] ; then
+ echo "Could not find window $1"
+ exit 1
+ fi
+ CLICKAT="$CLICKAT --wid $wid --noprint"
+
+ if [ $# -gt 1 ] ; then
+ fname="$2"
+ fi
+fi
+
+/sbin/ifconfig > $fname.ifconfig
+date >> $fname.ifconfig
+recordmydesktop --windowid $wid -x $xoffset -y $((yoffset+4)) --width 800 --height 592 --no-sound --overwrite --full-shots --output $fname &
+record_pid=$!
+
+$CLICKAT restore
+echo Begin...hover over each of 8 choices
+
+if [ $do_walk -eq 1 ] ; then
+# Walk through the opening Libre Office screen, testing mouse position, and hover text
+$CLICKAT move --timed --timeout 500 --position $((225+xoffset))x$((245+yoffset))
+$CLICKAT move --timed --timeout 500 --position $((225+xoffset))x$((295+yoffset))
+$CLICKAT move --timed --timeout 500 --position $((225+xoffset))x$((345+yoffset))
+$CLICKAT move --timed --timeout 500 --position $((225+xoffset))x$((420+yoffset))
+$CLICKAT move --timed --timeout 500 --position $((525+xoffset))x$((245+yoffset))
+$CLICKAT move --timed --timeout 500 --position $((525+xoffset))x$((295+yoffset))
+$CLICKAT move --timed --timeout 500 --position $((525+xoffset))x$((345+yoffset))
+$CLICKAT move --timed --timeout 500 --position $((525+xoffset))x$((420+yoffset))
+
+echo Hover over the 3 lower choices
+$CLICKAT move --timed --timeout 1500 --position $((160+xoffset))x$((545+yoffset))
+$CLICKAT move --timed --timeout 1500 --position $((190+xoffset))x$((545+yoffset))
+$CLICKAT move --timed --timeout 1500 --position $((220+xoffset))x$((545+yoffset))
+sleep 1
+fi
+
+if [ $do_smile -eq 1 ] ; then
+# Open a drawing
+echo Opening a drawing, wait for it to be ready
+$CLICKAT key --keystroke '\a+r\a-'
+sleep 5
+
+lo_button_y=560
+# Draw a smile
+echo Draw a smile
+$CLICKAT click --timed --timeout 100 --position $((180+xoffset))x$((lo_button_y+yoffset))
+#$CLICKAT click --timed --timeout 100 --position $((375+xoffset))x$((445+yoffset))
+radius=100
+centerx=520
+centery=280
+x=440
+$CLICKAT click --timed --timeout 100 --position $((440+xoffset))x$((340+yoffset)) --noup
+while [ $x -le 600 ] ; do
+ x=$((x+5))
+ if [ $x -ge $centerx ] ; then
+ a=$((x-centerx))
+ fi
+ if [ $x -lt $centerx ] ; then
+ a=$((centerx - x))
+ fi
+ b=`echo "sqrt(($radius*$radius)-($a*$a))" | bc`
+ $CLICKAT move --position $((x+xoffset))x$((centery+b+yoffset))
+ ./msleep 10
+done
+$CLICKAT click --position $((600+xoffset))x$((340+yoffset)) --nodown
+./msleep 1000
+
+$CLICKAT click --position $((124+xoffset))x$((lo_button_y+yoffset))
+./msleep 100
+$CLICKAT click --delay 50 --position $((460+xoffset))x$((240+yoffset)) --dragto $((500+xoffset))x$((280+yoffset))
+./msleep 1000
+$CLICKAT click --position $((124+xoffset))x$((lo_button_y+yoffset))
+./msleep 100
+$CLICKAT click --delay 50 --position $((540+xoffset))x$((240+yoffset)) --dragto $((580+xoffset))x$((280+yoffset))
+
+
+
+# Export our smile
+echo Export the smile
+rm -f /tmp/smile.png
+$CLICKAT click --timed --timeout 100 --position $((10+xoffset))x$((37+yoffset))
+$CLICKAT key --delay-between-keystrokes 100 --keystroke 't\r'
+sleep 2
+$CLICKAT key --keystroke '\a+n\a-'
+sleep 1
+$CLICKAT key --delay-between-keystrokes 100 --keystroke '/tmp/smile\t'
+sleep 1
+$CLICKAT key --delay-between-keystrokes 100 --keystroke 'pn\r'
+sleep 6
+$CLICKAT key --delay-between-keystrokes 100 --keystroke '2\[Delete]'
+./msleep 500
+
+lo_compression_y=347
+lo_compression_x=384
+$CLICKAT click --delay 50 --noup --position $((lo_compression_x+xoffset))x$((lo_compression_y+yoffset)) --dragto $((lo_compression_x-120+xoffset))x$((lo_compression_y+yoffset))
+./msleep 2000
+$CLICKAT click --delay 50 --nodown --dragto $((lo_compression_x+xoffset))x$((lo_compression_y+yoffset)) --position $((lo_compression_x-120+xoffset))x$((lo_compression_y+yoffset))
+./msleep 100
+$CLICKAT click --timed --timeout 100 --position $((396+xoffset))x$((530+yoffset))
+
+# Close the drawing
+echo Close the drawing
+$CLICKAT click --timed --timeout 100 --position $((10+xoffset))x$((37+yoffset))
+$CLICKAT key --delay-between-keystrokes 100 --keystroke 'c'
+sleep 4
+$CLICKAT key --delay-between-keystrokes 100 --keystroke 'd'
+fi
+
+if [ $do_typing -eq 1 ] ; then
+sleep 1
+
+# Open a document
+echo Open a text document
+$CLICKAT key --keystroke '\a+d\a-'
+sleep 6
+
+echo Type for a while
+# Type a paragraph at ~ 120 wps
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."
+
+echo Insert the smile
+$CLICKAT click --timed --timeout 100 --position $((129+xoffset))x$((37+yoffset))
+$CLICKAT click --timed --timeout 100 --position $((241+xoffset))x$((460+yoffset))
+$CLICKAT click --timed --timeout 100 --position $((406+xoffset))x$((460+yoffset))
+sleep 3
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "/tmp/smile.png\r"
+sleep 5
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "\e"
+sleep 4
+
+echo Add more text
+# Add some more text
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "\r"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "\r"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "\r"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "\r"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "\r"
+$CLICKAT key --delay-between-keystrokes 100 --keystroke "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi"
+
+
+echo Scroll up and then back down
+# Scroll up and then back down
+$CLICKAT button4 --repeat 50 --timeout 2000 --timed --position $((400+xoffset))x$((300+yoffset))
+$CLICKAT button5 --repeat 50 --timeout 2000 --timed --position $((400+xoffset))x$((300+yoffset))
+
+echo Close the document
+# Close the document
+$CLICKAT click --timed --timeout 100 --position $((10+xoffset))x$((37+yoffset))
+$CLICKAT key --delay-between-keystrokes 100 --keystroke 'c'
+sleep 4
+$CLICKAT key --delay-between-keystrokes 100 --keystroke 'd'
+fi
+
+echo ... done
+/sbin/ifconfig >> $fname.ifconfig
+date >> $fname.ifconfig
+
+kill -s INT $record_pid
diff --git a/msleep.c b/msleep.c
new file mode 100644
index 0000000..469e5b1
--- /dev/null
+++ b/msleep.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+/*----------------------------------------------------------------------------
+** msleep
+** Delay for a given number of milliseconds
+** time given by SLEEP_PERIOD, unless we're trying to click
+** repeatedly faster than that.
+**--------------------------------------------------------------------------*/
+void main(int argc, char *argv[])
+{
+ int i;
+ struct timespec timer;
+
+ for (i = 1; i < argc; i++)
+ {
+ timer.tv_sec = atol(argv[i]) / 1000L;
+ timer.tv_nsec = (atol(argv[i]) % 1000L) * 1000L * 1000L;
+ nanosleep(&timer, NULL);
+ }
+}
+
+
diff --git a/rgb_image.c b/rgb_image.c
new file mode 100644
index 0000000..4281b10
--- /dev/null
+++ b/rgb_image.c
@@ -0,0 +1,1092 @@
+/*----------------------------------------------------------------------------
+** rgb_image.c
+**
+**
+**---------------------------------------------------------------------------
+** Copyright 2004 by CodeWeavers, Inc.
+**
+** 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 <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdlib.h>
+#include <png.h>
+#include "rgb_image.h"
+
+/*----------------------------------------------------------------------------
+** Initialize the pixel_format structure
+**--------------------------------------------------------------------------*/
+void pixel_format_init(pixel_format *var)
+{
+ var->r_mask = 0;
+ var->g_mask = 0;
+ var->b_mask = 0;
+ var->r_count = 0;
+ var->g_count = 0;
+ var->b_count = 0;
+ var->r_shift = -1;
+ var->g_shift = -1;
+ var->b_shift = -1;
+}
+
+/*----------------------------------------------------------------------------
+** Destroy the ximage structure and free the win_image structure
+**--------------------------------------------------------------------------*/
+void win_image_destroy_ximage(win_image *var)
+{
+ if(var->ximage != NULL)
+ XDestroyImage(var->ximage);
+
+ var->ximage = (XImage*) NULL;
+ var->origin_x = 0;
+ var->origin_y = 0;
+ var->width = 0;
+ var->height = 0;
+}
+
+/*----------------------------------------------------------------------------
+** Destroy the ximage structure and free the win_image structure
+**--------------------------------------------------------------------------*/
+void win_image_destroy(win_image *var)
+{
+ if(var != NULL)
+ {
+ win_image_destroy_ximage(var);
+ free(var);
+ var = NULL;
+ }
+}
+
+/*----------------------------------------------------------------------------
+** Allocate and initialize memory for the win_image structure
+**--------------------------------------------------------------------------*/
+win_image* win_image_init()
+{
+ win_image *var;
+
+ var = malloc(sizeof(win_image));
+ if(var == NULL)
+ {
+ fprintf(stderr,"Error: unable to allocate memory for win_image structure\n");
+ return(NULL);
+ }
+
+ var->ximage = (XImage *)NULL;
+ pixel_format_init(&var->pformat);
+ var->origin_x = 0;
+ var->origin_y = 0;
+ var->width = 0;
+ var->height = 0;
+
+ return(var);
+}
+
+/*----------------------------------------------------------------------------
+** Destroy the data memory allocated for the rgb_image
+**--------------------------------------------------------------------------*/
+void rgb_image_destroy_data(rgb_image *var)
+{
+ int x;
+
+ /* free the columns of rgb */
+ for(x = 0; x < var->width; x++)
+ free(var->data[x]);
+
+ /* remember to free the row of pointers */
+ if(var->data != NULL)
+ free(var->data);
+
+ var->data = NULL;
+ var->width = 0;
+ var->height = 0;
+}
+
+/*----------------------------------------------------------------------------
+** Destroy the memory allocated for the rgb_image
+**--------------------------------------------------------------------------*/
+void rgb_image_destroy(rgb_image *var)
+{
+ if(var != NULL)
+ {
+ rgb_image_destroy_data(var);
+ free(var);
+ var = NULL;
+ }
+}
+
+/*----------------------------------------------------------------------------
+** Allocate and initialize memory for the rgb_image structure
+**--------------------------------------------------------------------------*/
+rgb_image* rgb_image_init()
+{
+ rgb_image *var;
+
+ var = malloc(sizeof(rgb_image));
+ if(var == NULL)
+ {
+ fprintf(stderr,"Error: unable to allocate memory for rgb_image structure\n");
+ return(NULL);
+ }
+
+ var->data = NULL;
+ var->width = 0;
+ var->height = 0;
+ var->bit_depth = 0;
+
+ return(var);
+}
+
+/*----------------------------------------------------------------------------
+** Allocate and initialize memory for the rgb_hist structure
+**--------------------------------------------------------------------------*/
+rgb_hist* rgb_hist_init()
+{
+ rgb_hist *var;
+ int i;
+
+ var = malloc(sizeof(rgb_hist));
+ if(var==NULL)
+ {
+ fprintf(stderr, "Error: unable to allocate memory for rgb_hist structure\n");
+ return(NULL);
+ }
+
+ for(i=0; i<256; i++)
+ {
+ var->r[i]=0;
+ var->g[i]=0;
+ var->b[i]=0;
+ }
+ var->total_count=0;
+
+ return(var);
+}
+
+/*----------------------------------------------------------------------------
+** Destroy the memory allocated for the rgb_image
+**--------------------------------------------------------------------------*/
+void rgb_hist_destroy(rgb_hist *var)
+{
+ if(var != NULL)
+ {
+ free(var);
+ var = NULL;
+ }
+}
+
+/*----------------------------------------------------------------------------
+** Allocate the data array for the rgb_image w/given dimensions
+** --if the correct mem size has already been allocated, do nothing and
+** return success
+**--------------------------------------------------------------------------*/
+int rgb_image_malloc(rgb_image *image, int width, int height)
+{
+ int x;
+
+ /*--------------------------------------------------------------------------
+ ** Make sure the dimensions passed are legitimate -- positive value
+ **------------------------------------------------------------------------*/
+ if(width < 1 || height < 1)
+ {
+ fprintf(stderr,"Error: invalid dimensions [%d x %d] passed to rgb_image_malloc\n",width,height);
+ return(1);
+ }
+
+ /*--------------------------------------------------------------------------
+ ** If the dimensions are the same, then return success, nothing to be done
+ **------------------------------------------------------------------------*/
+ if(width == image->width && height == image->height)
+ return(0);
+
+ /*--------------------------------------------------------------------------
+ ** If the image already contains data, it needs to be destroyed
+ **------------------------------------------------------------------------*/
+ if(image->data != NULL)
+ rgb_image_destroy_data(image);
+
+ /*--------------------------------------------------------------------------
+ ** Begin memory allocation -- allocate memory in columns s.t. it is easy
+ ** to read coordinate values (ie. [x][y])
+ **------------------------------------------------------------------------*/
+ image->data = malloc(width * sizeof(rgb*));
+ if(image->data == NULL)
+ {
+ fprintf(stderr,"Unable to allocate memory for rgb_image struct\n");
+ return(1);
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Allocate memory for each column
+ **------------------------------------------------------------------------*/
+ for(x = 0; x < width; x++)
+ {
+ image->data[x] = NULL;
+ image->data[x] = malloc(height * sizeof(rgb));
+ if(image->data[x] == NULL)
+ {
+ /*----------------------------------------------------------------------
+ ** Unable to allocate memory, make sure to clean-up the memory that has
+ ** already been allocated
+ **--------------------------------------------------------------------*/
+ image->width = x;
+ rgb_image_destroy(image);
+ fprintf(stderr,"Unable to allocate memory for rgb_image struct\n");
+ return(1);
+ }
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Set the image width and height before returning success
+ **------------------------------------------------------------------------*/
+ image->width = width;
+ image->height = height;
+ //image->bit_depth = 8;
+
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** Convert an rgb_image to a png file.
+** Return values:
+** 0 - success
+** 1 - bad png file -- not able to open
+** 2 - bad memory allocation
+**--------------------------------------------------------------------------*/
+int rgb_image_to_png(rgb_image *image, char *png_file)
+{
+ FILE *fp;
+ int x, y;
+ int rc = 0;
+ png_structp png_ptr = (png_structp)NULL;
+ png_infop info_ptr = (png_infop)NULL;
+ png_bytepp write_data = (png_bytepp)NULL;
+
+ /*--------------------------------------------------------------------------
+ ** Open the png_file for writing
+ **------------------------------------------------------------------------*/
+ fp = fopen(png_file, "wb");
+ if (!fp)
+ {
+ rc = ERR_PNG_FILE;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Create the png write structures
+ **------------------------------------------------------------------------*/
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Set the callback jump buffer
+ **------------------------------------------------------------------------*/
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Initialize the io structures
+ **------------------------------------------------------------------------*/
+ png_init_io(png_ptr, fp);
+
+ /*--------------------------------------------------------------------------
+ ** Set the image information
+ **------------------------------------------------------------------------*/
+ png_set_IHDR(png_ptr, info_ptr, image->width, image->height, image->bit_depth, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ /*--------------------------------------------------------------------------
+ ** Allocate memory for the write buffer
+ **------------------------------------------------------------------------*/
+ write_data = (png_bytepp)malloc(image->height * sizeof(png_bytep));
+ if(!write_data)
+ {
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+ for(y=0; y < image->height; y++)
+ {
+ write_data[y] = (png_bytep)malloc(image->width * 3);
+ if(!write_data[y])
+ {
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Move information from the rgb_image structure into the write buffer
+ **------------------------------------------------------------------------*/
+ for(y=0; y < image->height; y++)
+ {
+ for(x=0; x < image->width; x++)
+ {
+ write_data[y][x*3] = image->data[x][y].r;
+ write_data[y][(x*3)+1] = image->data[x][y].g;
+ write_data[y][(x*3)+2] = image->data[x][y].b;
+ }
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Insert data into info_ptr and write the information to the file
+ **------------------------------------------------------------------------*/
+ png_set_rows(png_ptr, info_ptr, write_data);
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+
+ERREXIT:
+
+ for(y=0; y < image->height; y++)
+ if(write_data[y])
+ free(write_data[y]);
+
+ if(write_data)
+ free(write_data);
+
+ if(png_ptr && info_ptr)
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ else if(png_ptr)
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+
+ if(fp)
+ fclose(fp);
+
+ return(rc);
+}
+
+/*----------------------------------------------------------------------------
+** Combine two png images. attach_image will be to the left of main_image.
+** The resultant image will be set to main_image. attach_image will be preserved.
+** Return values:
+** 0 - success
+** 1 - failure
+**--------------------------------------------------------------------------*/
+int rgb_image_attach(rgb_image **main_image, rgb_image **attach_image, int count)
+{
+ rgb_image *tmp_image = NULL;
+ rgb_image *tmain = *main_image;
+ int width, height;
+ int rc = 0;
+ int i, j, c, separator, x, y;
+ int attach_width = 0, attach_height = 0;
+
+ tmp_image = rgb_image_init();
+ if(tmp_image == NULL)
+ {
+ fprintf(stderr,"Unable to allocate memory for rgb_image\n");
+ rc = 1;
+ goto EXIT;
+ }
+
+ // set the offset for the main image
+ separator = 2;
+
+ // figure out the size needed for attachment portion
+ for(i = 0; i < count; i++)
+ {
+ if(attach_height)
+ attach_height += separator;
+ attach_height += attach_image[i]->height;
+
+ if(!attach_width || attach_image[i]->width > attach_width)
+ attach_width = attach_image[i]->width;
+ }
+
+
+ // determine the height of the new image
+ if(tmain->height > attach_height)
+ height = tmain->height;
+ else
+ height = attach_height;
+ height += separator*2;
+
+ // determine the width of the new image
+ width = attach_width + tmain->width + (separator*3);
+
+ // allocate the memory for the new image
+ if(rgb_image_malloc(tmp_image, width, height))
+ {
+ rc = 1;
+ goto EXIT;
+ }
+
+ // more writing done, but more sensible approach
+ // clear out the image first
+ for(i = 0; i < width; i++)
+ {
+ for(j = 0; j < height; j++)
+ {
+ tmp_image->data[i][j].r = 0;
+ tmp_image->data[i][j].g = 0;
+ tmp_image->data[i][j].b = 0;
+ }
+ }
+
+ // write out the attached images
+ x = separator;
+ y = separator;
+ for(c = 0; c < count; c++)
+ {
+ for(i = 0; i < attach_image[c]->width; i++)
+ {
+ for(j = 0; j < attach_image[c]->height; j++)
+ {
+ tmp_image->data[i+x][y+j].r = attach_image[c]->data[i][j].r;
+ tmp_image->data[i+x][y+j].g = attach_image[c]->data[i][j].g;
+ tmp_image->data[i+x][y+j].b = attach_image[c]->data[i][j].b;
+ }
+ }
+ y += attach_image[c]->height + separator;
+ }
+
+ // write out the main image
+ x = attach_width + (2*separator);
+ y = separator;
+ for(i = 0; i < tmain->width; i++)
+ {
+ for(j = 0; j < tmain->height; j++)
+ {
+ tmp_image->data[i+x][j+y].r = tmain->data[i][j].r;
+ tmp_image->data[i+x][j+y].g = tmain->data[i][j].g;
+ tmp_image->data[i+x][j+y].b = tmain->data[i][j].b;
+ }
+ }
+
+ // draw the border
+
+ // don't forget to set the bit depth for the new rgb_image
+ // todo: make sure bit depth of two images are compatible
+ tmp_image->bit_depth = 8;
+
+EXIT:
+ if(tmp_image)
+ {
+ rgb_image_destroy(*main_image);
+ *main_image = tmp_image;
+ }
+
+ return(rc);
+}
+
+/*----------------------------------------------------------------------------
+** Convert a png file to an rgb_image structure.
+** Return values:
+** 0 - success
+** 1 - bad png file (either not able to open or bad format)
+** 2 - bad memory allocation
+**--------------------------------------------------------------------------*/
+int png_to_rgb_image(char *png_file, rgb_image *image)
+{
+ FILE *fp;
+ int read_num = 8, rc = SUCCESS;
+ int width, height;
+ int x,y;
+ unsigned char header[read_num];
+ png_structp png_ptr = (png_structp)NULL;
+ png_infop info_ptr = (png_infop)NULL;
+ png_bytepp row_pointers;
+
+ /*--------------------------------------------------------------------------
+ ** Make sure that png_file exists and that it is a png file
+ **------------------------------------------------------------------------*/
+ fp = fopen(png_file, "rb");
+ if (!fp)
+ {
+ char *p = getenv("CXTEST_PICTURE_SEARCH_DIR");
+ if (p && strlen(p))
+ {
+ char *q = malloc(strlen(p) + strlen(png_file) + 3);
+ strcpy(q, p);
+ if (*(q + strlen(q) - 1) != '/')
+ strcat(q, "/");
+ strcat(q, png_file);
+ fp = fopen(q, "rb");
+ free(q);
+ }
+ }
+
+ if (!fp)
+ {
+ fprintf(stderr,"Error: unable to open file [%s]\n",png_file);
+ rc = ERR_PNG_FILE;
+ goto ERREXIT;
+ }
+ if(read_num != fread(header, 1, read_num, fp))
+ {
+ fprintf(stderr,"Error: unable to read header from file [%s]\n",png_file);
+ rc = ERR_PNG_FILE;
+ goto ERREXIT;
+ }
+ if(png_sig_cmp(header, 0, read_num) != 0)
+ {
+ fprintf(stderr,"Error: [%s] is not a png formatted imgae file\n",png_file);
+ rc = ERR_PNG_FILE;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Initialize the png structures
+ **------------------------------------------------------------------------*/
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ fprintf(stderr,"Error: unable to create png read struct\n");
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ fprintf(stderr,"Error: unable to create png info struct\n");
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Initialize the IO structures
+ **------------------------------------------------------------------------*/
+ png_init_io(png_ptr, fp);
+ png_set_sig_bytes(png_ptr, read_num);
+
+ /*--------------------------------------------------------------------------
+ ** Read the png file and with appropriate transformations -- convert to
+ ** 8 bit color depth and remove alpha component
+ **------------------------------------------------------------------------*/
+ png_read_png(png_ptr, info_ptr, (PNG_TRANSFORM_STRIP_16|PNG_TRANSFORM_STRIP_ALPHA|PNG_TRANSFORM_PACKING), NULL);
+
+ /*--------------------------------------------------------------------------
+ ** Get the height and width of the png image -- allocate appropriate memory
+ ** for the rgb_image
+ **------------------------------------------------------------------------*/
+ height = png_get_image_height(png_ptr, info_ptr);
+ width = png_get_image_width(png_ptr, info_ptr);
+ if(rgb_image_malloc(image, width, height))
+ {
+ rc = ERR_MALLOC;
+ goto ERREXIT;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Get the data row pointers and copy image into rgb_image -- png_get_rows
+ ** is a high level function call, png takes care of malloc and free
+ **------------------------------------------------------------------------*/
+ row_pointers = png_get_rows(png_ptr, info_ptr);
+ for(y=0; y < height; y++)
+ {
+ for(x=0; x < width; x++)
+ {
+ image->data[x][y].r = row_pointers[y][x*3];
+ image->data[x][y].g = row_pointers[y][(x*3)+1];
+ image->data[x][y].b = row_pointers[y][(x*3)+2];
+ }
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Set the bit_depth for the image
+ **--------------------------------------------------------------------------*/
+ image->bit_depth = 8;
+
+ERREXIT:
+
+ if(fp)
+ fclose(fp);
+
+ if(png_ptr && info_ptr)
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ else if(png_ptr)
+ png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+
+ return(rc);
+}
+
+/*----------------------------------------------------------------------------
+** Set the pixel format for ximage
+** precondition: win_image_init() has been called for w_image
+** and w_image->ximage has been set (no longer null)
+**--------------------------------------------------------------------------*/
+void win_image_set_pixel_format(win_image *w_image)
+{
+ /*--------------------------------------------------------------------------
+ ** Initialize temp r, g, and b masks, to be used to determine the
+ ** r, g, and b counts and shifts
+ **------------------------------------------------------------------------*/
+ unsigned long tmp_rmask = w_image->ximage->red_mask;
+ unsigned long tmp_gmask = w_image->ximage->green_mask;
+ unsigned long tmp_bmask = w_image->ximage->blue_mask;
+ int i;
+
+ /*--------------------------------------------------------------------------
+ ** Figure out the shift and count for r, g, and b
+ ** Find the Red shift and count
+ **------------------------------------------------------------------------*/
+ for(i = 0; i < 32; i++)
+ {
+ if((tmp_rmask & 1) == 1)
+ {
+ if(w_image->pformat.r_shift == -1)
+ w_image->pformat.r_shift = i;
+ w_image->pformat.r_count++;
+ }
+ else if(w_image->pformat.r_shift != -1)
+ break;
+
+ tmp_rmask = tmp_rmask >> 1;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Find the Green shift and count
+ **------------------------------------------------------------------------*/
+ for(i = 0; i < 32; i++)
+ {
+ if((tmp_gmask & 1) == 1)
+ {
+ if(w_image->pformat.g_shift == -1)
+ w_image->pformat.g_shift = i;
+ w_image->pformat.g_count++;
+ }
+ else if(w_image->pformat.g_shift != -1)
+ break;
+
+ tmp_gmask = tmp_gmask >> 1;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Find the Blue shift and count
+ **------------------------------------------------------------------------*/
+ for(i = 0; i < 32; i++)
+ {
+ if((tmp_bmask & 1) == 1)
+ {
+ if(w_image->pformat.b_shift == -1)
+ w_image->pformat.b_shift = i;
+ w_image->pformat.b_count++;
+ }
+ else if(w_image->pformat.b_shift != -1)
+ break;
+
+ tmp_bmask = tmp_bmask >> 1;
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Assign the r, g, and b masks
+ **------------------------------------------------------------------------*/
+ w_image->pformat.r_mask = w_image->ximage->red_mask;
+ w_image->pformat.g_mask = w_image->ximage->green_mask;
+ w_image->pformat.b_mask = w_image->ximage->blue_mask;
+}
+
+/*----------------------------------------------------------------------------
+** Set the viewable parameters for an ximage
+**--------------------------------------------------------------------------*/
+void find_viewable_coords(int global_coord, int dpy_dimension, int win_dimension, int *origin_coord, int *adjusted_dimension)
+{
+ /*--------------------------------------------------------------------------
+ ** If the dimensions are already set, then adjust them accordingly
+ **------------------------------------------------------------------------*/
+ if(*adjusted_dimension > 0)
+ {
+ if(*adjusted_dimension + *origin_coord >= dpy_dimension)
+ *adjusted_dimension -= ((*adjusted_dimension + *origin_coord) - dpy_dimension);
+
+ if(*origin_coord < 0)
+ {
+ *adjusted_dimension += *origin_coord;
+ *origin_coord = 0;
+ }
+ }
+ else
+ {
+ /*------------------------------------------------------------------------
+ ** Adjust the dimension around the dimension axis origin
+ **----------------------------------------------------------------------*/
+ *adjusted_dimension = win_dimension;
+ if( global_coord < 0 )
+ {
+ *origin_coord -= global_coord;
+ *adjusted_dimension += global_coord;
+ }
+
+ /*------------------------------------------------------------------------
+ ** Adjust the dimension at the other edge of the window
+ **----------------------------------------------------------------------*/
+ if( global_coord + *adjusted_dimension >= dpy_dimension )
+ *adjusted_dimension -= ((*adjusted_dimension + global_coord) - dpy_dimension);
+ }
+
+ /*------------------------------------------------------------------------
+ ** Aditional control if we are in broken X, i.e CrossOver Mac 6.1 branch
+ ** with bundled X implemenation
+ **----------------------------------------------------------------------*/
+ if(*adjusted_dimension > dpy_dimension)
+ *adjusted_dimension = dpy_dimension;
+}
+
+/*----------------------------------------------------------------------------
+** Set the viewable parameters for an ximage
+**--------------------------------------------------------------------------*/
+void win_image_set_viewable_params(win_image *w_image, int global_x, int global_y, int display_width, int display_height, int win_width, int win_height)
+{
+ /*--------------------------------------------------------------------------
+ ** Find the viewable origin coord and width for the x axis
+ **------------------------------------------------------------------------*/
+ find_viewable_coords(global_x, display_width, win_width, &w_image->origin_x, &w_image->width);
+
+ /*--------------------------------------------------------------------------
+ ** Find the viewable origin coord and width for the y axis
+ **------------------------------------------------------------------------*/
+ find_viewable_coords(global_y, display_height, win_height, &w_image->origin_y, &w_image->height);
+}
+
+/*----------------------------------------------------------------------------
+** Get the ximage
+** -- might want to think about changing operations for win == root_win
+** return 0 on success, 1 on failure
+**--------------------------------------------------------------------------*/
+int win_image_set_ximage(win_image *wimage, Display *display, Window win)
+{
+ Window root_win = 0, dummy;
+ XWindowAttributes win_attr;
+ int global_x, global_y;
+ int display_width = -1, display_height = -1;
+
+ /*--------------------------------------------------------------------------
+ ** Make sure to reset wimage if it already has an XImage
+ **--------------------------------------------------------------------------*/
+ if(wimage->ximage != (XImage *)NULL)
+ win_image_destroy_ximage(wimage);
+
+ /*--------------------------------------------------------------------------
+ ** Get the root window
+ **--------------------------------------------------------------------------*/
+ root_win = DefaultRootWindow(display);
+ if(!root_win)
+ {
+ fprintf(stderr, "Unable to get default root window\n");
+ return(1);
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Get the window attributes
+ **--------------------------------------------------------------------------*/
+ if( !XGetWindowAttributes(display, win, &win_attr))
+ {
+ fprintf(stderr,"Unable to get window attributes\n");
+ return(1);
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Find the global coordinates of the upper left corner of the window
+ **--------------------------------------------------------------------------*/
+ if( !XTranslateCoordinates(display, win, root_win, 0, 0, &global_x, &global_y, &dummy))
+ {
+ fprintf(stderr,"Unable to translate coordinates\n");
+ return(1);
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Get the display width and height
+ **--------------------------------------------------------------------------*/
+ display_width = DisplayWidth(display, DefaultScreen(display));
+ display_height = DisplayHeight(display, DefaultScreen(display));
+ if ( display_width == -1 || display_height == -1)
+ {
+ fprintf(stderr,"Unable to retrieve display width and/or height\n");
+ return(1);
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Find and set the viewable window parameters for win and capture the ximage
+ **--------------------------------------------------------------------------*/
+ win_image_set_viewable_params(wimage, global_x, global_y, display_width, display_height, win_attr.width, win_attr.height);
+ wimage->ximage = XGetImage(display, win, wimage->origin_x, wimage->origin_y, wimage->width, wimage->height, AllPlanes, ZPixmap);
+
+ if( wimage->ximage == (XImage *)NULL)
+ {
+ fprintf(stderr, "Unable to allocate ximage\n");
+
+ return(1);
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Get the pixel format for w_image.ximage, but only if it has not previously
+ ** been set... pixel format will not change on fly
+ **--------------------------------------------------------------------------*/
+ if(wimage->pformat.r_shift == -1)
+ win_image_set_pixel_format(wimage);
+
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** Convert to 8 bit depth
+**--------------------------------------------------------------------------*/
+short convert_to_8bit(short pixel_component, int component_size)
+{
+ short return_val;
+ switch(component_size)
+ {
+ case 5:
+ return_val = pixel_component << 3 | pixel_component >> 2;
+ break;
+
+ case 6:
+ return_val = pixel_component << 2 | pixel_component >> 4;
+ break;
+
+ default:
+ return_val = pixel_component;
+ break;
+ }
+
+ return(return_val);
+}
+
+/*----------------------------------------------------------------------------
+** Convert a win_image to a rgb_image
+**--------------------------------------------------------------------------*/
+int win_image_to_rgb_image(win_image *w_image, rgb_image *r_image)
+{
+ int x, y;
+ unsigned long pixel;
+ short r, g, b;
+
+ /*--------------------------------------------------------------------------
+ ** Allocate memory for r_image
+ **--------------------------------------------------------------------------*/
+ if(rgb_image_malloc(r_image, w_image->width, w_image->height))
+ return(1);
+
+ /*--------------------------------------------------------------------------
+ ** Go through the ximage and assign r_image values, x first
+ **--------------------------------------------------------------------------*/
+ for(x = 0; x < w_image->width; x++)
+ {
+ /*------------------------------------------------------------------------
+ ** Go through the y values for each x value of ximage
+ **------------------------------------------------------------------------*/
+ for(y = 0; y < w_image->height; y++)
+ {
+ pixel = XGetPixel(w_image->ximage, x, y);
+
+ /*----------------------------------------------------------------------
+ ** Find the r, g, and b values
+ **----------------------------------------------------------------------*/
+ r = (pixel & w_image->pformat.r_mask) >> w_image->pformat.r_shift;
+ g = (pixel & w_image->pformat.g_mask) >> w_image->pformat.g_shift;
+ b = (pixel & w_image->pformat.b_mask) >> w_image->pformat.b_shift;
+
+ /*----------------------------------------------------------------------
+ ** Convert the pixels to 8 bit per r, g, b and assign values
+ **----------------------------------------------------------------------*/
+ r_image->data[x][y].r = convert_to_8bit(r, w_image->pformat.r_count);
+ r_image->data[x][y].g = convert_to_8bit(g, w_image->pformat.g_count);
+ r_image->data[x][y].b = convert_to_8bit(b, w_image->pformat.b_count);
+ }
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Record the bit depth that is being used
+ **--------------------------------------------------------------------------*/
+ r_image->bit_depth = 8;
+
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** Convert an rgb image to an rgb histogram
+**--------------------------------------------------------------------------*/
+int rgb_image_to_rgb_hist(rgb_image *r_image, rgb_hist *r_hist)
+{
+ int i=0;
+ int j=0;
+
+ /*--------------------------------------------------------------------------
+ ** Allocate memory for r_hist
+ **--------------------------------------------------------------------------*/
+ // r_hist=rgb_hist_init();
+ //if( r_hist==NULL )
+ // return(1);
+
+ // printf("width: %d, height: %d\n",r_image->width,r_image->height);
+ for(i=0; i<r_image->width; i++)
+ {
+ for(j=0; j<r_image->height; j++)
+ {
+ /*-----------------------------------------------------------------------
+ ** At each pixel, get the rgb image's r,g and b values
+ **---------------------------------------------------------------------*/
+ int r_val=r_image->data[i][j].r;
+ int g_val=r_image->data[i][j].g;
+ int b_val=r_image->data[i][j].b;
+
+ /*----------------------------------------------------------------------------
+ ** Increment the spots in the arrays that correspond to the image's rgb values
+ **--------------------------------------------------------------------------*/
+ r_hist->r[r_val]++;
+ r_hist->g[g_val]++;
+ r_hist->b[b_val]++;
+
+ /*------------------------------------------------------------------
+ ** Increment the variable that stores how many pixels there are
+ **----------------------------------------------------------------*/
+ r_hist->total_count++;
+ }
+ }
+ r_hist->width=r_image->width;
+ r_hist->height=r_image->height;
+
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** Convert a win image to an rgb histogram
+**--------------------------------------------------------------------------*/
+int win_image_to_rgb_hist(win_image *w_image, rgb_hist *r_hist)
+{
+
+ rgb_image *r_image=NULL;
+
+ /*--------------------------------------------------------------------------
+ ** Convert the win image to an rgb image
+ **--------------------------------------------------------------------------*/
+ if (!win_image_to_rgb_image(w_image, r_image))
+ return(1);
+
+ /*--------------------------------------------------------------------------
+ ** Call rgb_image_to_rgb_hist on the newly created rgb image
+ **--------------------------------------------------------------------------*/
+ if (!rgb_image_to_rgb_hist(r_image, r_hist))
+ return(1);
+
+ /*--------------------------------------------------------------------------
+ ** If we are here, both function calls worked; we exit successfully
+ **--------------------------------------------------------------------------*/
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** Compare two pixels
+** return 0 if they are within the error margin, 1 if they are not
+** -- might want to think about making percentage
+**--------------------------------------------------------------------------*/
+int rgb_pixel_comparison(rgb *p1, rgb *p2, int err_margin)
+{
+ /*--------------------------------------------------------------------------
+ ** Compare each component of the pixel separately
+ **--------------------------------------------------------------------------*/
+ if( err_margin < abs(p1->r - p2->r))
+ return 1;
+
+ if( err_margin < abs(p1->g - p2->g))
+ return 1;
+
+ if( err_margin < abs(p1->b - p2->b))
+ return 1;
+
+ /*--------------------------------------------------------------------------
+ ** All components w/in error margin... success
+ **--------------------------------------------------------------------------*/
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+** Move through the subimage, comparing each pixel with a set location
+** from the image
+**--------------------------------------------------------------------------*/
+int rgb_image_subimage_search (rgb_image *image, rgb_image *subimage, int err_margin, int x, int y)
+{
+ int i, j;
+
+ for (i = 0; i < subimage->width; i++)
+ {
+ for (j = 0; j < subimage->height; j++)
+ {
+ /*------------------------------------------------------------------------
+ ** Do a comparison of each pixel
+ **----------------------------------------------------------------------*/
+ if ( rgb_pixel_comparison( &image->data[i+x][j+y], &subimage->data[i][j], err_margin))
+ return(1);
+ }
+ }
+
+ /*--------------------------------------------------------------------------
+ ** All pixels in the subimage are within the error margin, success
+ **--------------------------------------------------------------------------*/
+ return(0);
+}
+
+/*----------------------------------------------------------------------------
+** Find one rgb_image inside of another
+** --return 0 on success, 1 on image not found, 2 on error
+**--------------------------------------------------------------------------*/
+int rgb_image_find_subimage(rgb_image *image, rgb_image *subimage, int err_margin, int *x, int *y)
+{
+ int x_diff, y_diff;
+ int i = 0, j = 0;
+ int rc = 1;
+
+ /*--------------------------------------------------------------------------
+ ** Make sure that the subimage is smaller than the image & that they have
+ ** the same bit depth
+ **--------------------------------------------------------------------------*/
+ x_diff = image->width - subimage->width;
+ y_diff = image->height - subimage->height;
+ if (x_diff < 0 || y_diff < 0 || image->bit_depth != subimage->bit_depth)
+ return(2);
+
+ /*--------------------------------------------------------------------------
+ ** Loop through the image, sending a search at each potential location of
+ ** the subimage's upper-left corner
+ **--------------------------------------------------------------------------*/
+ for (i = 0; i <= x_diff && rc; i++)
+ {
+ for (j = 0; j <= y_diff && rc; j++)
+ {
+ rc = rgb_image_subimage_search(image, subimage, err_margin, i, j);
+ }
+ }
+
+ /*--------------------------------------------------------------------------
+ ** Adjust the coordinates, x and y, to be coordinates of the image
+ ** -- if no coords are passed in, or if they are not applicable to the
+ ** subimage (ie. too large), then use the middle of the subimage
+ **--------------------------------------------------------------------------*/
+ if(!rc)
+ {
+ if(*x < 0 || *x > subimage->width || *y < 0 || *y > subimage->height)
+ {
+ *x = subimage->width/2;
+ *y = subimage->height/2;
+ }
+ *x += i;
+ *y += j;
+ }
+
+ return(rc);
+}
diff --git a/rgb_image.h b/rgb_image.h
new file mode 100644
index 0000000..26b5d14
--- /dev/null
+++ b/rgb_image.h
@@ -0,0 +1,117 @@
+/*----------------------------------------------------------------------------
+** rgb_image.h
+**
+**
+**---------------------------------------------------------------------------
+** Copyright 2004 by CodeWeavers, Inc.
+**
+** 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
+**
+**--------------------------------------------------------------------------*/
+
+#define SUCCESS 0
+#define ERR_PNG_FILE 1
+#define ERR_MALLOC 2
+
+/*----------------------------------------------------------------------------
+** Structure to act as rgb holster
+**--------------------------------------------------------------------------*/
+typedef struct _rgb
+{
+ short r;
+ short g;
+ short b;
+} rgb;
+
+/*----------------------------------------------------------------------------
+** Structure to store image information in rgb format
+**--------------------------------------------------------------------------*/
+typedef struct _rgb_image
+{
+ rgb **data;
+ int width;
+ int height;
+ int bit_depth;
+} rgb_image;
+
+/*----------------------------------------------------------------------------
+** Structure to record the pixel format for an ximage
+**--------------------------------------------------------------------------*/
+typedef struct _pixel_format
+{
+ unsigned long r_mask;
+ unsigned long g_mask;
+ unsigned long b_mask;
+ int r_count;
+ int g_count;
+ int b_count;
+ int r_shift;
+ int g_shift;
+ int b_shift;
+} pixel_format;
+
+/*----------------------------------------------------------------------------
+** Structure to store a window's image and misc information
+**--------------------------------------------------------------------------*/
+typedef struct _win_image
+{
+ XImage *ximage;
+ pixel_format pformat;
+ int origin_x;
+ int origin_y;
+ int width;
+ int height;
+} win_image;
+
+/*----------------------------------------------------------------------------
+** Structure to store an rgb histogram
+**--------------------------------------------------------------------------*/
+typedef struct _rgb_hist
+{
+ double r[256];
+ double g[256];
+ double b[256];
+ int total_count;
+ int width;
+ int height;
+} rgb_hist;
+
+/*----------------------------------------------------------------------------
+** Function prototypes
+**--------------------------------------------------------------------------*/
+int win_image_to_rgb_hist(win_image *w_image, rgb_hist *r_hist);
+int rgb_image_to_rgb_hist(rgb_image *r_image, rgb_hist *r_hist);
+rgb_hist* rgb_hist_init();
+void rgb_hist_destroy(rgb_hist *var);
+void pixel_format_init(pixel_format *var);
+void win_image_destroy_ximage(win_image *var);
+void win_image_destroy(win_image *var);
+win_image* win_image_init();
+void rgb_image_destroy_data(rgb_image *var);
+void rgb_image_destroy(rgb_image *var);
+rgb_image* rgb_image_init();
+int rgb_image_malloc(rgb_image *image, int width, int height);
+int rgb_image_to_png(rgb_image *image, char *png_file);
+int png_to_rgb_image(char *png_file, rgb_image *image);
+void win_image_set_pixel_format(win_image *w_image);
+void find_viewable_coords(int global_coord, int dpy_dimension, int win_dimension, int *origin_coord, int *adjusted_dimension);
+void win_image_set_viewable_params(win_image *w_image, int global_x, int global_y, int display_width, int display_height, int win_width, int win_height);
+int win_image_set_ximage(win_image *wimage, Display *display, Window win);
+short convert_to_8bit(short pixel_component, int component_size);
+int win_image_to_rgb_image(win_image *w_image, rgb_image *r_image);
+int rgb_pixel_comparison(rgb *p1, rgb *p2, int err_margin);
+int rgb_image_subimage_search (rgb_image *image, rgb_image *subimage, int err_margin, int x, int y);
+int rgb_image_find_subimage(rgb_image *image, rgb_image *subimage, int err_margin, int *x, int *y);
+int rgb_image_attach(rgb_image **main_image, rgb_image **attach_image, int count);