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