--- trunk/rosapps/tests/wclickat/wclickat.c 2005-11-11 21:12:13 UTC (rev 19148)
+++ trunk/rosapps/tests/wclickat/wclickat.c 2005-11-11 21:30:55 UTC (rev 19149)
@@ -0,0 +1,686 @@
+/*----------------------------------------------------------------------------
+** wclickat.c
+** Utilty to send clicks to Wine Windows
+**
+** See usage() for usage instructions.
+**
+**---------------------------------------------------------------------------
+** Copyright 2004 Jozef Stefanka for CodeWeavers, Inc.
+** Copyright 2005 Dmitry Timoshkov for CodeWeavers, Inc.
+** Copyright 2005 Francois Gouget for 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 <windows.h>
+#include <windowsx.h>
+#include <stdio.h>
+#include <ctype.h>
+
+
+#define APP_NAME "wclickat"
+#define DEFAULT_DELAY 500
+#define DEFAULT_REPEAT 1000
+
+#define ARRAY_LENGTH(array) (sizeof(array)/sizeof((array)[0]))
+
+static const WCHAR STATIC_CLASS[]={'s','t','a','t','i','c','\0'};
+
+/*----------------------------------------------------------------------------
+** Global variables
+**--------------------------------------------------------------------------*/
+
+#define RC_RUNNING -1
+#define RC_SUCCESS 0
+#define RC_INVALID_ARGUMENTS 1
+#define RC_NODISPLAY 2
+#define RC_TIMEOUT 3
+static int status;
+
+typedef enum
+{
+ ACTION_INVALID,
+ ACTION_FIND,
+ ACTION_LCLICK,
+ ACTION_MCLICK,
+ ACTION_RCLICK
+} action_type;
+static action_type g_action = ACTION_INVALID;
+
+static WCHAR* g_window_class = NULL;
+static WCHAR* g_window_title = NULL;
+static long g_control_id = 0;
+static WCHAR* g_control_class = NULL;
+static WCHAR* g_control_caption = NULL;
+static long g_x = -1;
+static long g_y = -1;
+static long g_dragto_x = -1;
+static long g_dragto_y = -1;
+static long g_disabled = 0;
+
+static long g_delay = DEFAULT_DELAY;
+static long g_timeout = 0;
+static long g_repeat = 0;
+static long g_untildeath = 0;
+static UINT timer_id;
+
+
+/*
+ * Provide some basic debugging support.
+ */
+#ifdef __GNUC__
+#define __PRINTF_ATTR(fmt,args) __attribute__((format (printf,fmt,args)))
+#else
+#define __PRINTF_ATTR(fmt,args)
+#endif
+static int debug_on=0;
+static int init_debug()
+{
+ char* str=getenv("CXTEST_DEBUG");
+ if (str && strstr(str, "+wclickat"))
+ debug_on=1;
+ return debug_on;
+}
+
+static void cxlog(const char* format, ...) __PRINTF_ATTR(1,2);
+static void cxlog(const char* format, ...)
+{
+ va_list valist;
+
+ if (debug_on)
+ {
+ va_start(valist, format);
+ vfprintf(stderr, format, valist);
+ va_end(valist);
+ }
+}
+
+/*----------------------------------------------------------------------------
+** usage
+**--------------------------------------------------------------------------*/
+static void usage(void)
+{
+ fprintf(stderr, "%s - Utility to send clicks to Wine Windows.\n", APP_NAME);
+ fprintf(stderr, "----------------------------------------------\n");
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s action --winclass class --wintitle title [--timeout ms]\n",APP_NAME);
+ fprintf(stderr, " %*.*s [--ctrlclas class] [--ctrlcaption caption] [--ctrlid id]\n", strlen(APP_NAME) + 3, strlen(APP_NAME) + 3, "");
+ fprintf(stderr, " %*.*s [--position XxY] [--delay ms] [--untildeath] [--repeat ms]\n", strlen(APP_NAME) + 3, strlen(APP_NAME) + 3, "");
+ fprintf(stderr, "Where action can be one of:\n");
+ fprintf(stderr, " find Find the specified window or control\n");
+ fprintf(stderr, " button<n> Send a click with the given X button number\n");
+ fprintf(stderr, " click|lclick Synonym for button1 (left click)\n");
+ fprintf(stderr, " mclick Synonym for button2 (middle click)\n");
+ fprintf(stderr, " rclick Synonym for button3 (right click)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "The options are as follows:\n");
+ fprintf(stderr, " --timeout ms How long to wait before failing with a code of %d\n", RC_TIMEOUT);
+ fprintf(stderr, " --winclass class Class name of the top-level window of interest\n");
+ fprintf(stderr, " --wintitle title Title of the top-level window of interest\n");
+ fprintf(stderr, " --ctrlclass name Class name of the control of interest, if any\n");
+ fprintf(stderr, " --ctrlcaption cap A substring of the control's caption\n");
+ fprintf(stderr, " --ctrlid id Id of the control\n");
+ fprintf(stderr, " --position XxY Coordinates for the click, relative to the window / control\n");
+ fprintf(stderr, " --dragto If given, then position specifies start click, and\n");
+ fprintf(stderr, " dragto specifies release coords.\n");
+ fprintf(stderr, " --allow-disabled Match the window or control even hidden or disabled\n");
+ fprintf(stderr, " --delay ms Wait ms milliseconds before clicking. The default is %d\n", DEFAULT_DELAY);
+ fprintf(stderr, " --untildeath Wait until the window disappears\n");
+ fprintf(stderr, " --repeat ms Click every ms milliseconds. The default is %d\n", DEFAULT_REPEAT);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%s returns %d on success\n", APP_NAME, RC_SUCCESS);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Environment variable overrides:\n");
+ fprintf(stderr, " CXTEST_TIME_MULTIPLE Specifies a floating multiplier applied to any\n");
+ fprintf(stderr, " delay and timeout parameters.\n");
+}
+
+static const WCHAR* my_strstriW(const WCHAR* haystack, const WCHAR* needle)
+{
+ const WCHAR *h,*n;
+ WCHAR first;
+
+ if (!*needle)
+ return haystack;
+
+ /* Special case the first character because
+ * we will be doing a lot of comparisons with it.
+ */
+ first=towlower(*needle);
+ needle++;
+ while (*haystack)
+ {
+ while (towlower(*haystack)!=first && *haystack)
+ haystack++;
+
+ h=haystack+1;
+ n=needle;
+ while (towlower(*h)==towlower(*n) && *h)
+ {
+ h++;
+ n++;
+ }
+ if (!*n)
+ return haystack;
+ haystack++;
+ }
+ return NULL;
+}
+
+static BOOL CALLBACK find_control(HWND hwnd, LPARAM lParam)
+{
+ WCHAR str[1024];
+ HWND* pcontrol;
+
+ if (!GetClassNameW(hwnd, str, ARRAY_LENGTH(str)) ||
+ lstrcmpiW(str, g_control_class))
+ return TRUE;
+
+ if (g_control_caption)
+ {
+ if (!GetWindowTextW(hwnd, str, ARRAY_LENGTH(str)) ||
+ !my_strstriW(str, g_control_caption))
+ return TRUE;
+ }
+ if (g_control_id && g_control_id != GetWindowLong(hwnd, GWL_ID))
+ return TRUE;
+
+ /* Check that the control is visible and active */
+ if (!g_disabled)
+ {
+ DWORD style = GetWindowStyle(hwnd);
+ if (!(style & WS_VISIBLE) || (style & WS_DISABLED))
+ return TRUE;
+ }
+
+ pcontrol = (HWND*)lParam;
+ *pcontrol = hwnd;
+ return FALSE;
+}
+
+static BOOL CALLBACK find_top_window(HWND hwnd, LPARAM lParam)
+{
+ WCHAR str[1024];
+ HWND* pwindow;
+
+ if (!GetClassNameW(hwnd, str, ARRAY_LENGTH(str)) ||
+ lstrcmpiW(str, g_window_class))
+ return TRUE;
+
+ if (!GetWindowTextW(hwnd, str, ARRAY_LENGTH(str)) ||
+ lstrcmpiW(str, g_window_title))
+ return TRUE;
+
+ /* Check that the window is visible and active */
+ if (!g_disabled)
+ {
+ DWORD style = GetWindowStyle(hwnd);
+ if (!(style & WS_VISIBLE) || (style & WS_DISABLED))
+ return TRUE;
+ }
+
+ /* See if we find the control we want */
+ if (g_control_class)
+ {
+ HWND control = NULL;
+ EnumChildWindows(hwnd, find_control, (LPARAM)&control);
+ if (!control)
+ return TRUE;
+ hwnd=control;
+ }
+
+ pwindow = (HWND*)lParam;
+ *pwindow = hwnd;
+ return FALSE;
+}
+
+static HWND find_window()
+{
+ HWND hwnd;
+
+ hwnd=NULL;
+ EnumWindows(find_top_window, (LPARAM)&hwnd);
+ return hwnd;
+}
+
+static void do_click(HWND window, DWORD down, DWORD up)
+{
+ WINDOWINFO window_info;
+ long x, y;
+
+ SetForegroundWindow(GetParent(window));
+ window_info.cbSize=sizeof(window_info);
+ GetWindowInfo(window, &window_info);
+
+ /* The calculations below convert the coordinates so they are absolute
+ * screen coordinates in 'Mickeys' as required by mouse_event.
+ * In mickeys the screen size is always 65535x65535.
+ */
+ x=window_info.rcWindow.left+g_x;
+ if (x<window_info.rcWindow.left || x>=window_info.rcWindow.right)
+ x=(window_info.rcWindow.right+window_info.rcWindow.left)/2;
+ x=(x << 16)/GetSystemMetrics(SM_CXSCREEN);
+
+ y=window_info.rcWindow.top+g_y;
+ if (y<window_info.rcWindow.top || y>=window_info.rcWindow.bottom)
+ y=(window_info.rcWindow.bottom+window_info.rcWindow.top)/2;
+ y=(y << 16)/GetSystemMetrics(SM_CYSCREEN);
+
+ mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x, y, 0, 0);
+ if (down) {
+ mouse_event(MOUSEEVENTF_ABSOLUTE | down, x, y, 0, 0);
+ if ((g_dragto_x > 0) && (g_dragto_y > 0)) {
+ int i;
+ long dx, dy;
+ long step_per_x, step_per_y;
+ long dragto_x, dragto_y;
+
+ dragto_x=window_info.rcWindow.left+g_dragto_x;
+ if (dragto_x<window_info.rcWindow.left || dragto_x>=window_info.rcWindow.right)
+ dragto_x=(window_info.rcWindow.right+window_info.rcWindow.left)/2;
+ dragto_x=(dragto_x << 16)/GetSystemMetrics(SM_CXSCREEN);
+
+ dragto_y=window_info.rcWindow.top+g_dragto_y;
+ if (dragto_y<window_info.rcWindow.top || dragto_y>=window_info.rcWindow.bottom)
+ dragto_y=(window_info.rcWindow.bottom+window_info.rcWindow.top)/2;
+ dragto_y=(dragto_y << 16)/GetSystemMetrics(SM_CYSCREEN);
+
+ dx = g_dragto_x - g_x;
+ dy = g_dragto_y - g_y;
+ step_per_x = dx / 4;
+ step_per_y = dy / 4;
+ for (i = 0; i < 4; i++) {
+ mouse_event(MOUSEEVENTF_MOVE, step_per_x, step_per_y, 0, 0);
+ }
+ x=dragto_x;
+ y=dragto_y;
+ }
+ }
+ if (up)
+ mouse_event(MOUSEEVENTF_ABSOLUTE | up, x, y, 0, 0);
+}
+
+static void CALLBACK ClickProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ HWND window = find_window();
+
+ if (!window)
+ {
+ if (g_untildeath)
+ {
+ /* FIXME: The window / control might just be disabled and if
+ * that's the case we should not exit yet. But I don't expect
+ * --untildeath to be used at all anyway so fixing this can
+ * wait until it becomes necessary.
+ */
+ status=RC_SUCCESS;
+ }
+ else
+ cxlog("The window has disappeared!\n");
+ return;
+ }
+
+ switch (g_action)
+ {
+ case ACTION_FIND:
+ /* Nothing to do */
+ break;
+ case ACTION_LCLICK:
+ cxlog("Sending left click\n");
+ do_click(window, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP);
+ break;
+ case ACTION_MCLICK:
+ cxlog("Sending middle click\n");
+ do_click(window, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP);
+ break;
+ case ACTION_RCLICK:
+ cxlog("Sending right click\n");
+ do_click(window, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP);
+ default:
+ fprintf(stderr, "error: unknown action %d\n", g_action);
+ break;
+ }
+ if (!g_repeat)
+ status=RC_SUCCESS;
+}
+
+static void CALLBACK DelayProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ KillTimer(NULL, timer_id);
+ timer_id=0;
+ if (g_repeat)
+ {
+ cxlog("Setting up a timer for --repeat\n");
+ timer_id=SetTimer(NULL, 0, g_repeat, ClickProc);
+ }
+
+ ClickProc(NULL, 0, 0, 0);
+}
+
+static void CALLBACK FindWindowProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ HWND window = find_window();
+ if (!window)
+ return;
+
+ cxlog("Found the window\n");
+ if (g_delay)
+ {
+ cxlog("Waiting for a bit\n");
+ KillTimer(NULL, timer_id);
+ timer_id=SetTimer(NULL, 0, g_delay, DelayProc);
+ do_click(window, 0,0);
+ }
+ else
+ {
+ DelayProc(NULL, 0, 0, 0);
+ }
+}
+
+static void CALLBACK TimeoutProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ status = RC_TIMEOUT;
+}
+
+/*----------------------------------------------------------------------------
+** parse_arguments
+**--------------------------------------------------------------------------*/
+static int arg_get_long(const char** *argv, const char* name, long* value)
+{
+ if (!**argv)
+ {
+ fprintf(stderr, "error: missing argument for '%s'\n", name);
+ return 1;
+ }
+
+ *value=atol(**argv);
+ if (*value < 0)
+ {
+ fprintf(stderr, "error: invalid argument '%s' for '%s'\n",
+ **argv, name);
+ (*argv)++;
+ return 1;
+ }
+ (*argv)++;
+ return 0;
+}
+
+static int arg_get_utf8(const char** *argv, const char* name, WCHAR* *value)
+{
+ int len;
+
+ if (!**argv)
+ {
+ fprintf(stderr, "error: missing argument for '%s'\n", name);
+ return 1;
+ }
+
+ len = MultiByteToWideChar(CP_UTF8, 0, **argv, -1, NULL, 0);
+ *value = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
+ if (!*value)
+ {
+ fprintf(stderr, "error: memory allocation error\n");
+ (*argv)++;
+ return 1;
+ }
+ MultiByteToWideChar(CP_UTF8, 0, **argv, -1, *value, len);
+ (*argv)++;
+ return 0;
+}
+
+static int parse_arguments(int argc, const char** argv)
+{
+ int rc;
+ const char* arg;
+ char* p;
+
+ rc=0;
+ argv++;
+ while (*argv)
+ {
+ arg=*argv++;
+ if (*arg!='-')
+ {
+ if (g_action != ACTION_INVALID)
+ {
+ fprintf(stderr, "error: '%s' an action has already been specified\n", arg);
+ rc=1;
+ }
+ else if (strcmp(arg, "click") == 0 || strcmp(arg, "lclick") == 0)
+ {
+ g_action = ACTION_LCLICK;
+ }
+ else if (strcmp(arg, "mclick") == 0)
+ {
+ g_action = ACTION_MCLICK;
+ }
+ else if (strcmp(arg, "rclick") == 0)
+ {
+ g_action = ACTION_RCLICK;
+ }
+ else if (strncmp(arg, "button", 6) == 0)
+ {
+ int button;
+ char extra='\0';
+ int r=sscanf(arg, "button%d%c", &button, &extra);
+ /* We should always get r==1 but due to a bug in Wine's
+ * msvcrt.dll implementation (at least up to 20050127)
+ * we may also get r==2 and extra=='\0'.
+ */
+ if (r!=1 && (r!=2 || extra!='\0'))
+ {
+ fprintf(stderr, "error: invalid argument '%s' for '%s'\n",
+ *argv, arg);
+ rc=1;
+ }
+ else if (button<1 || button>3)
+ {
+ fprintf(stderr, "error: unknown button '%s'\n", arg);
+ rc=1;
+ }
+ else
+ {
+ /* Just to remain compatible with the enum */
+ g_action=button+ACTION_LCLICK-1;
+ }
+ }
+ else if (strcmp(arg, "find") == 0)
+ {
+ g_action = ACTION_FIND;
+ }
+ else
+ {
+ fprintf(stderr, "error: unknown action '%s'\n", arg);
+ rc=1;
+ }
+ }
+ else if (strcmp(arg, "--winclass") == 0)
+ {
+ rc|=arg_get_utf8(&argv, arg, &g_window_class);
+ }
+ else if (strcmp(arg, "--wintitle") == 0)
+ {
+ rc|=arg_get_utf8(&argv,arg, &g_window_title);
+ }
+ else if (strcmp(arg, "--ctrlclass") == 0)
+ {
+ rc|=arg_get_utf8(&argv, arg, &g_control_class);
+ }
+ else if (strcmp(arg, "--ctrlid") == 0)
+ {
+ rc|=arg_get_long(&argv, arg, &g_control_id);
+ }
+ else if (strcmp(arg, "--ctrlcaption") == 0)
+ {
+ rc|=arg_get_utf8(&argv, arg, &g_control_caption);
+ }
+ else if (strcmp(arg, "--position") == 0)
+ {
+ if (!*argv)
+ {
+ fprintf(stderr, "error: missing argument for '%s'\n", arg);
+ rc=1;
+ }
+ else
+ {
+ char extra='\0';
+ int r=sscanf(*argv, "%ldx%ld%c", &g_x, &g_y, &extra);
+ /* We should always get r==2 but due to a bug in Wine's
+ * msvcrt.dll implementation (at least up to 20050127)
+ * we may also get r==3 and extra=='\0'.
+ */
+ if (r!=2 && (r!=3 || extra!='\0'))
+ {
+ fprintf(stderr, "error: invalid argument '%s' for '%s'\n",
+ *argv, arg);
+ rc=1;
+ }
+ argv++;
+ }
+ }
+ else if (strcmp(arg, "--dragto") == 0)
+ {
+ if (!*argv)
+ {
+ fprintf(stderr, "error: missing argument for '%s'\n", arg);
+ rc=1;
+ }
+ else
+ {
+ char extra='\0';
+ int r=sscanf(*argv, "%ldx%ld%c", &g_dragto_x, &g_dragto_y, &extra);
+ /* We should always get r==2 but due to a bug in Wine's
+ * * msvcrt.dll implementation (at least up to 20050127)
+ * * we may also get r==3 and extra=='\0'.
+ * */
+ if (r!=2 && (r!=3 || extra!='\0'))
+ {
+ fprintf(stderr, "error: invalid argument '%s' for '%s'\n",
+ *argv, arg);
+ rc=1;
+ }
+ argv++;
+ }
+ }
+ else if (strcmp(arg, "--allow-disabled") == 0)
+ {
+ g_disabled = 1;
+ }
+ else if (strcmp(arg, "--delay") == 0)
+ {
+ rc|=arg_get_long(&argv, arg, &g_delay);
+ }
+ else if (strcmp(arg, "--timeout") == 0)
+ {
+ rc|=arg_get_long(&argv, arg, &g_timeout);
+ }
+ else if (strcmp(arg, "--repeat") == 0)
+ {
+ rc|=arg_get_long(&argv, arg, &g_repeat);
+ }
+ else if (strcmp(arg, "--untildeath") == 0)
+ {
+ g_untildeath=1;
+ }
+ else if (strcmp(arg, "--help") == 0)
+ {
+ rc=2;
+ }
+ }
+
+ if (g_action == ACTION_INVALID)
+ {
+ fprintf(stderr, "error: you must specify an action type\n");
+ rc=1;
+ }
+ else
+ {
+ /* Adjust the default delay and repeat parameters depending on
+ * the operating mode so less needs to be specified on the command
+ * line, and so we can assume them to be set right.
+ */
+ if (g_action == ACTION_FIND)
+ g_delay=0;
+ if (!g_untildeath)
+ g_repeat=0;
+ else if (!g_repeat)
+ g_repeat=DEFAULT_REPEAT;
+ }
+
+ if (!g_window_class)
+ {
+ fprintf(stderr, "error: you must specify a --winclass parameter\n");
+ rc=1;
+ }
+ if (!g_window_title)
+ {
+ fprintf(stderr, "error: you must specify a --wintitle parameter\n");
+ rc=1;
+ }
+ if (g_control_class)
+ {
+ if (!g_control_id && !g_control_caption)
+ {
+ fprintf(stderr, "error: you must specify either the control id or its caption\n");
+ rc=1;
+ }
+ }
+
+ /*------------------------------------------------------------------------
+ ** Process environment variables
+ **----------------------------------------------------------------------*/
+ p = getenv("CXTEST_TIME_MULTIPLE");
+ if (p)
+ {
+ float g_multiple = atof(p);
+ g_delay = (long) (((float) g_delay) * g_multiple);
+ g_timeout = (long) (((float) g_timeout) * g_multiple);
+ }
+
+ return rc;
+}
+
+int main(int argc, const char** argv)
+{
+ MSG msg;
+
+ init_debug();
+
+ status = parse_arguments(argc, argv);
+ if (status)
+ {
+ if (status == 2)
+ usage();
+ else
+ fprintf(stderr, "Issue %s --help for usage.\n", *argv);
+ return RC_INVALID_ARGUMENTS;
+ }
+ cxlog("Entering message loop. action=%d\n", g_action);
+
+ if (g_timeout>0)
+ SetTimer(NULL, 0, g_timeout, TimeoutProc);
+ timer_id=SetTimer(NULL, 0, 100, FindWindowProc);
+
+ status=RC_RUNNING;
+ while (status==RC_RUNNING && GetMessage(&msg, NULL, 0, 0)!=0)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ return status;
+}
--- trunk/rosapps/tests/wpickclick/wpickclick.c 2005-11-11 21:12:13 UTC (rev 19148)
+++ trunk/rosapps/tests/wpickclick/wpickclick.c 2005-11-11 21:30:55 UTC (rev 19149)
@@ -0,0 +1,219 @@
+/*----------------------------------------------------------------------------
+** wpickclick.c
+** Utilty to pick clicks posted to Wine Windows
+**
+**
+**---------------------------------------------------------------------------
+** Copyright 2004 Jozef Stefanka for CodeWeavers, Inc.
+** Copyright 2005 Francois Gouget for CodeWeavers, Inc.
+** Copyright 2005 Dmitry Timoshkov for 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 <stdio.h>
+#include <fcntl.h>
+#include <io.h>
+#include <windows.h>
+
+#include "hook.h"
+
+
+#define APP_NAME "wpickclick.exe"
+
+
+static BOOL (WINAPI *pInstallHooks)(HMODULE hdll);
+static void (WINAPI *pRemoveHooks)();
+static action_t* (WINAPI *pGetAction)();
+static void (WINAPI *pFreeAction)(action_t* action);
+
+
+/*
+ * Provide some basic debugging support.
+ */
+#ifdef __GNUC__
+#define __PRINTF_ATTR(fmt,args) __attribute__((format (printf,fmt,args)))
+#else
+#define __PRINTF_ATTR(fmt,args)
+#endif
+static int debug_on=0;
+static int init_debug()
+{
+ char* str=getenv("CXTEST_DEBUG");
+ if (str && strstr(str, "+hook"))
+ debug_on=1;
+ return debug_on;
+}
+
+static void cxlog(const char* format, ...) __PRINTF_ATTR(1,2);
+static void cxlog(const char* format, ...)
+{
+ va_list valist;
+
+ if (debug_on)
+ {
+ va_start(valist, format);
+ vfprintf(stderr, format, valist);
+ va_end(valist);
+ }
+}
+
+static HINSTANCE load_hook_dll()
+{
+ HINSTANCE hinstDll;
+ char dllpath[MAX_PATH];
+ char* p;
+
+ hinstDll=LoadLibrary("hook.dll");
+ if (hinstDll != NULL)
+ return hinstDll;
+
+ if (!GetModuleFileName(NULL,dllpath,sizeof(dllpath)))
+ return NULL;
+
+ p=strrchr(dllpath,'\\');
+ if (!p)
+ return NULL;
+ *p='\0';
+ p=strrchr(dllpath,'\\');
+ if (!p)
+ return NULL;
+ *p='\0';
+ strcat(dllpath,"\\hookdll\\hook.dll");
+ hinstDll=LoadLibrary(dllpath);
+ return hinstDll;
+}
+
+char* cleanup(char* str)
+{
+ char* s;
+
+ while (*str==' ' || *str=='\t' || *str=='\r' || *str=='\n')
+ str++;
+ s=strchr(str,'\n');
+ if (!s)
+ s=str+strlen(str)-1;
+ while (s>str && (*s==' ' || *s=='\t' || *s=='\r' || *s=='\n'))
+ s--;
+ *(s+1)='\0';
+ return str;
+}
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine, int nCmdShow)
+{
+ HINSTANCE hDll;
+ action_t* action;
+
+ init_debug();
+
+ /* Our scripts expect Unix-style line ends */
+ _setmode(1,_O_BINARY);
+ _setmode(2,_O_BINARY);
+
+ if (strstr(lpCmdLine,"--help"))
+ {
+ fprintf(stderr,"%s - Utility to print coordinates, component, window title, component class and window class name of a click\n", APP_NAME);
+ fprintf(stderr,"----------------------------------------------\n");
+ fprintf(stderr,"Usage: %s\n",APP_NAME);
+ fprintf(stderr,"The options are as follows:\n");
+ fprintf(stderr,"After starting you can\n");
+ fprintf(stderr,"select where to click. If we properly track the click, it will be reported\n");
+ fprintf(stderr,"in the following format:\n");
+ fprintf(stderr," button-name x y component_name window_name component_class_name window_class_name\n");
+ fprintf(stderr,"Note that x and y can be negative; this typically happens if you click within the\n");
+ fprintf(stderr,"window manager decorations of a given window.\n");
+ fprintf(stderr,"On success, %s returns 0, non zero on some failure\n",APP_NAME);
+ exit(0);
+ };
+
+ /* Load the hook library */
+ hDll = load_hook_dll();
+ if (!hDll)
+ {
+ fprintf(stderr, "Error: Unable to load 'hook.dll'\n");
+ printf("failed\n");
+ return 1;
+ }
+
+ pInstallHooks=(void*)GetProcAddress(hDll, "InstallHooks");
+ pRemoveHooks=(void*)GetProcAddress(hDll, "RemoveHooks");
+ pGetAction=(void*)GetProcAddress(hDll, "GetAction");
+ pFreeAction=(void*)GetProcAddress(hDll, "FreeAction");
+ if (!pInstallHooks || !pRemoveHooks || !pGetAction)
+ {
+ fprintf(stderr, "Error: Unable to get the hook.dll functions (%ld)\n",
+ GetLastError());
+ printf("failed\n");
+ return 1;
+ }
+
+ if (!pInstallHooks(hDll))
+ {
+ fprintf(stderr, "Error: Unable to install the hooks (%ld)\n",
+ GetLastError());
+ printf("failed\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Ready for capture...\n");
+ action=pGetAction();
+ if (!action)
+ {
+ fprintf(stderr, "Error: GetAction() failed\n");
+ printf("failed\n");
+ return 1;
+ }
+
+ switch (action->action)
+ {
+ case ACTION_FAILED:
+ printf("failed\n");
+ break;
+ case ACTION_NONE:
+ printf("none\n");
+ break;
+ case ACTION_FIND:
+ printf("find\n");
+ break;
+ case ACTION_BUTTON1:
+ case ACTION_BUTTON2:
+ case ACTION_BUTTON3:
+ printf("button%d %ld %ld\n", action->action-ACTION_BUTTON1+1,
+ action->x, action->y);
+ break;
+ default:
+ fprintf(stderr, "Error: Unknown action %d\n",action->action);
+ printf("%d\n", action->action);
+ break;
+ }
+ printf("%s\n", action->window_class);
+ printf("%s\n", action->window_title);
+ printf("%ld\n", action->control_id);
+ printf("%s\n", action->control_class);
+ printf("%s\n", cleanup(action->control_caption));
+
+ cxlog("\n%s: action=%d x=%ld y=%ld\n", __FILE__, action->action,
+ action->x, action->y);
+ cxlog("window_class='%s'\n", action->window_class);
+ cxlog("window_title='%s'\n", action->window_title);
+ cxlog("control_id=%ld\n", action->control_id);
+ cxlog("control_class='%s'\n", action->control_class);
+ cxlog("control_caption='%s'\n", action->control_caption);
+
+ pFreeAction(action);
+ pRemoveHooks();
+ return 0;
+}