From 0303a440e395b685a1bca18a25bf7ce40145bc59 Mon Sep 17 00:00:00 2001 From: Steven Edwards Date: Fri, 11 Nov 2005 21:30:55 +0000 Subject: [PATCH] add window selection and picking utils from cxtest for Art's regression tests svn path=/trunk/; revision=19149 --- rosapps/tests/wclickat/wclickat.c | 686 ++++++++++++++++++++++++++ rosapps/tests/wpickclick/wpickclick.c | 219 ++++++++ 2 files changed, 905 insertions(+) create mode 100644 rosapps/tests/wclickat/wclickat.c create mode 100644 rosapps/tests/wpickclick/wpickclick.c diff --git a/rosapps/tests/wclickat/wclickat.c b/rosapps/tests/wclickat/wclickat.c new file mode 100644 index 00000000000..9ffc886222d --- /dev/null +++ b/rosapps/tests/wclickat/wclickat.c @@ -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 +#include +#include +#include + + +#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 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.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.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.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.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; +} diff --git a/rosapps/tests/wpickclick/wpickclick.c b/rosapps/tests/wpickclick/wpickclick.c new file mode 100644 index 00000000000..36a1aede172 --- /dev/null +++ b/rosapps/tests/wpickclick/wpickclick.c @@ -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 +#include +#include +#include + +#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; +}