add window selection and picking utils from cxtest for Art's regression tests
authorSteven Edwards <winehacker@gmail.com>
Fri, 11 Nov 2005 21:30:55 +0000 (21:30 +0000)
committerSteven Edwards <winehacker@gmail.com>
Fri, 11 Nov 2005 21:30:55 +0000 (21:30 +0000)
svn path=/trunk/; revision=19149

rosapps/tests/wclickat/wclickat.c [new file with mode: 0644]
rosapps/tests/wpickclick/wpickclick.c [new file with mode: 0644]

diff --git a/rosapps/tests/wclickat/wclickat.c b/rosapps/tests/wclickat/wclickat.c
new file mode 100644 (file)
index 0000000..9ffc886
--- /dev/null
@@ -0,0 +1,686 @@
+/*----------------------------------------------------------------------------\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
diff --git a/rosapps/tests/wpickclick/wpickclick.c b/rosapps/tests/wpickclick/wpickclick.c
new file mode 100644 (file)
index 0000000..36a1aed
--- /dev/null
@@ -0,0 +1,219 @@
+/*----------------------------------------------------------------------------\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