add window selection and picking utils from cxtest for Art's regression tests
Added: trunk/rosapps/tests/wclickat/
Added: trunk/rosapps/tests/wclickat/wclickat.c
Added: trunk/rosapps/tests/wpickclick/
Added: trunk/rosapps/tests/wpickclick/wpickclick.c

Added: trunk/rosapps/tests/wclickat/wclickat.c
--- 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;
+}

Added: trunk/rosapps/tests/wpickclick/wpickclick.c
--- 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;
+}