Adding wxUIActionSimulator, a class for programmatically controlling the mouse and keyboard.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@63644 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Kevin Ollivier
2010-03-06 20:09:23 +00:00
parent 917f228a1f
commit a02a5cfcf3
10 changed files with 776 additions and 1 deletions

View File

@@ -727,6 +727,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
src/common/textentrycmn.cpp
src/common/toplvcmn.cpp
src/common/treebase.cpp
src/common/uiactioncmn.cpp
src/common/valgen.cpp
src/common/validate.cpp
src/common/valtext.cpp
@@ -966,6 +967,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
wx/treebase.h
wx/treebook.h
wx/treectrl.h
wx/uiaction.h
wx/valgen.h
wx/vidmode.h
wx/vlbox.h
@@ -991,10 +993,11 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
src/generic/caret.cpp
src/generic/imaglist.cpp
src/unix/dialup.cpp
src/unix/displayx11.cpp
src/unix/fontenum.cpp
src/unix/fontutil.cpp
src/unix/uiactionx11.cpp
src/unix/utilsx11.cpp
src/unix/displayx11.cpp
</set>
<set var="XWIN_LOWLEVEL_HDR" hints="files">
wx/generic/caret.h
@@ -1710,6 +1713,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
src/msw/textentry.cpp
src/msw/tglbtn.cpp
src/msw/treectrl.cpp
src/msw/uiaction.cpp
</set>
<set var="MSW_HDR" hints="files">
wx/generic/clrpickerg.h
@@ -2257,6 +2261,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
src/osx/textctrl_osx.cpp
src/osx/tglbtn_osx.cpp
src/osx/toolbar_osx.cpp
src/osx/uiaction_osx.cpp
<!-- wxWebKit files -->
src/html/htmlctrl/webkit/webkit.mm
<!-- Native color/font dialogs -->

51
include/wx/uiaction.h Normal file
View File

@@ -0,0 +1,51 @@
/////////////////////////////////////////////////////////////////////////////
// Name: include/wx/uiaction.cpp
// Purpose: wxUIActionSimulator interface
// Author: Kevin Ollivier
// Modified by:
// Created: 2010-03-06
// RCS-ID: $Id: menu.cpp 54129 2008-06-11 19:30:52Z SC $
// Copyright: (c) Kevin Ollivier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#ifndef _UIACTIONSIMULATOR_H_
#define _UIACTIONSIMULATOR_H_
#include <wx/defs.h>
#include <wx/event.h>
#include <wx/dynarray.h>
class WXDLLIMPEXP_CORE wxUIActionSimulator
{
public:
wxUIActionSimulator();
~wxUIActionSimulator();
// Mouse related
bool MouseMove(long x, long y);
bool MouseDown(int button = wxMOUSE_BTN_LEFT);
bool MouseUp(int button = wxMOUSE_BTN_LEFT);
bool MouseClick(int button = wxMOUSE_BTN_LEFT);
bool MouseDblClick(int button = wxMOUSE_BTN_LEFT);
bool MouseDragDrop(long x1, long y1, long x2, long y2, int button = wxMOUSE_BTN_LEFT);
// Keyboard related:
bool KeyDown(int keycode, bool shiftDown=false, bool cmdDown=false, bool altDown=false)
{ return Key(keycode, true, shiftDown, cmdDown, altDown); }
bool KeyUp(int keycode, bool shiftDown=false, bool cmdDown=false, bool altDown=false)
{ return Key(keycode, false, shiftDown, cmdDown, altDown); }
bool Char(int keycode, bool shiftDown=false, bool cmdDown=false, bool altDown=false);
protected:
// Implementation-wise, since key events take more code to set up on GTK and Mac, it makes
// sense to handle both key down and key up in one method. However, I wanted the API for pressing
// and releasing the mouse and keyboard to be consistent, and I don't really like using a bool
// for pressed state, so I'm leaving this as an implementation detail.
bool Key(int keycode, bool isDown=true, bool shiftDown=false, bool cmdDown=false, bool altDown=false);
};
#endif

150
interface/wx/uiaction.h Normal file
View File

@@ -0,0 +1,150 @@
/////////////////////////////////////////////////////////////////////////////
// Name: uiaction.h
// Purpose: interface of wxUIActionSimulator
// Author: wxWidgets team
// RCS-ID: $Id$
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
/**
@class wxUIActionSimulator
wxUIActionSimulator is a class used to simulate user interface actions
such as a mouse click or a key press.
Common usages for this class would be to provide playback and record (aka macro recording)
functionality for users, or to drive unit tests by simulating user sessions.
See the uiaction sample for example usage of this class.
NOTE: For keyboard operations, currently you must pass the keycode of the actual
key on the keyboard. To simulate, e.g. IME actions, you'd need to simulate the actual
keypresses needed to active the IME, then the keypresses needed to type and select
the desired character.
@library{wxcore}
*/
class wxUIActionSimulator
{
public:
/**
Constructor.
*/
wxUIActionSimulator();
~wxUIActionSimulator();
/**
Move the mouse to the specified coordinates.
@param x
x coordinate to move to, in screen coordinates.
@param y
y coordinate to move to, in screen coordinates.
*/
bool MouseMove(long x, long y);
/**
Press a mouse button.
@param button
Button to press. Valid constants are wxMOUSE_BTN_LEFT, wxMOUSE_BTN_MIDDLE, and wxMOUSE_BTN_RIGHT.
*/
bool MouseDown(int button = wxMOUSE_BTN_LEFT);
/**
Release a mouse button.
@param button
Button to press. See wxUIActionSimulator::MouseDown for a list of valid constants.
*/
bool MouseUp(int button = wxMOUSE_BTN_LEFT);
/**
Click a mouse button.
@param button
Button to press. See wxUIActionSimulator::MouseDown for a list of valid constants.
*/
bool MouseClick(int button = wxMOUSE_BTN_LEFT);
/**
Double-click a mouse button.
@param button
Button to press. See wxUIActionSimulator::MouseDown for a list of valid constants.
*/
bool MouseDblClick(int button = wxMOUSE_BTN_LEFT);
/**
Perform a drag and drop operation.
@param x1
x start coordinate, in screen coordinates.
@param y1
y start coordinate, in screen coordinates.
@param x2
x desintation coordinate, in screen coordinates.
@param y2
y destination coordinate, in screen coordinates.
@param button
Button to press. See wxUIActionSimulator::MouseDown for a list of valid constants.
*/
bool MouseDragDrop(long x1, long y1, long x2, long y2, int button = wxMOUSE_BTN_LEFT);
/**
Press a key.
@param keycode
key to operate on, as an integer.
@param shiftDown
true if the shift key should be pressed, false otherwise.
@param cmdDown
true if the cmd key should be pressed, false otherwise.
@param altDown
true if the alt key should be pressed, false otherwise.
*/
bool KeyDown(int keycode, bool shiftDown=false, bool cmdDown=false, bool altDown=false);
/**
Release a key.
@param keycode
key to operate on, as an integer.
@param shiftDown
true if the shift key should be pressed, false otherwise.
@param cmdDown
true if the cmd key should be pressed, false otherwise.
@param altDown
true if the alt key should be pressed, false otherwise.
*/
bool KeyUp(int keycode, bool shiftDown=false, bool cmdDown=false, bool altDown=false);
/**
Press and release a key.
@param keycode
key to operate on, as an integer.
@param shiftDown
true if the shift key should be pressed, false otherwise.
@param cmdDown
true if the cmd key should be pressed, false otherwise.
@param altDown
true if the alt key should be pressed, false otherwise.
*/
bool Char(int keycode, bool shiftDown=false, bool cmdDown=false, bool altDown=false);
};
#endif

View File

@@ -84,6 +84,7 @@
<subproject id="toolbar" template="sub"/>
<subproject id="treectrl" template="sub"/>
<subproject id="typetest" template="sub"/>
<subproject id="uiaction" template="sub"/>
<subproject id="validate" template="sub"/>
<subproject id="vscroll" template="sub"/>
<subproject id="widgets" template="sub"/>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" ?>
<!-- $Id$ -->
<makefile>
<include file="../../build/bakefiles/common_samples.bkl"/>
<exe id="uiaction" template="wx_sample" template_append="wx_append">
<sources>uiaction.cpp</sources>
<wx-lib>core</wx-lib>
<wx-lib>base</wx-lib>
<uid type="creatorid">wx06</uid>
</exe>
</makefile>

View File

@@ -0,0 +1,161 @@
/////////////////////////////////////////////////////////////////////////////
// Name: uiaction.cpp
// Purpose: wxUIActionSimulator sample
// Author: Kevin Ollivier
// Modified by:
// Created: 04/01/98
// RCS-ID: $Id$
// Copyright: (c) Kevin Ollivier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/uiaction.h"
// ----------------------------------------------------------------------------
// resources
// ----------------------------------------------------------------------------
// the application icon (under Windows and OS/2 it is in resources and even
// though we could still include the XPM here it would be unused)
#if !defined(__WXMSW__) && !defined(__WXPM__)
#include "../sample.xpm"
#endif
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
// ctor(s)
MyFrame(const wxString& title);
void OnButtonPressed(wxCommandEvent&);
void OnRunSimulation(wxCommandEvent&);
bool ButtonPressed() const { return m_buttonPressed; }
bool MenuSelected() const { return m_menuSelected; }
private:
bool m_buttonPressed;
bool m_menuSelected;
};
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// IDs for the controls and the menu commands
enum
{
// menu items
TheButton = wxID_ANY,
RunSimulation = wxID_ANY
};
IMPLEMENT_APP(MyApp)
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// the application class
// ----------------------------------------------------------------------------
bool MyApp::OnInit()
{
if ( !wxApp::OnInit() )
return false;
MyFrame *frame = new MyFrame("wxUIActionSimulator sample application");
frame->Show(true);
return true;
}
// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------
// frame constructor
MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title)
{
SetIcon(wxICON(sample));
m_buttonPressed = false;
m_menuSelected = false;
#if wxUSE_MENUS
// create a menu bar
wxMenu *fileMenu = new wxMenu;
fileMenu->Append(wxID_NEW, "&New File...", "Open a new file");
fileMenu->Append(RunSimulation, "&Run Simulation...", "Run the UI action simulation");
fileMenu->Append(wxID_EXIT, "E&xit\tAlt-X", "Quit this program");
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(fileMenu, "&File");
SetMenuBar(menuBar);
#endif // wxUSE_MENUS
wxButton* button = new wxButton(this, TheButton, "Button");
button->SetName("TheButton");
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnButtonPressed, this, button->GetId());
Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnRunSimulation, this, RunSimulation);
}
// event handlers
void MyFrame::OnRunSimulation(wxCommandEvent&)
{
wxUIActionSimulator sim;
wxWindow* button = FindWindow(wxString("TheButton"));
wxPoint globalPoint = button->ClientToScreen(wxPoint(20, 10));
sim.MouseMove(globalPoint.x, globalPoint.y);
sim.MouseClick(wxMOUSE_BTN_LEFT);
wxYield();
if (ButtonPressed())
wxMessageBox("Button automagically pressed!");
}
void MyFrame::OnButtonPressed(wxCommandEvent&)
{
m_buttonPressed = true;
}

View File

@@ -0,0 +1,59 @@
/////////////////////////////////////////////////////////////////////////////
// Name: src/common/uiactioncmn.cpp
// Purpose: wxUIActionSimulator common implementation
// Author: Kevin Ollivier
// Modified by:
// Created: 2010-03-06
// RCS-ID: $Id: menu.cpp 54129 2008-06-11 19:30:52Z SC $
// Copyright: (c) Kevin Ollivier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#include "wx/uiaction.h"
wxUIActionSimulator::wxUIActionSimulator()
{
}
wxUIActionSimulator::~wxUIActionSimulator()
{
}
bool wxUIActionSimulator::MouseClick(int button)
{
MouseDown(button);
MouseUp(button);
return true;
}
bool wxUIActionSimulator::MouseDblClick(int button)
{
MouseDown(button);
MouseUp(button);
MouseDown(button);
MouseUp(button);
return true;
}
bool wxUIActionSimulator::MouseDragDrop(long x1, long y1, long x2, long y2, int button)
{
MouseMove(x1, y1);
MouseDown(button);
MouseMove(x2, y2);
MouseUp(button);
return true;
}
bool wxUIActionSimulator::Char(int keycode, bool shiftDown, bool cmdDown, bool altDown)
{
Key(keycode, false, shiftDown, cmdDown, altDown);
Key(keycode, true, shiftDown, cmdDown, altDown);
return true;
}

78
src/msw/uiaction.cpp Normal file
View File

@@ -0,0 +1,78 @@
/////////////////////////////////////////////////////////////////////////////
// Name: src/msw/uiaction.cpp
// Purpose: wxUIActionSimulator implementation
// Author: Kevin Ollivier
// Modified by:
// Created: 2010-03-06
// RCS-ID: $Id: menu.cpp 54129 2008-06-11 19:30:52Z SC $
// Copyright: (c) Kevin Ollivier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/defs.h"
#endif
#include "wx/uiaction.h"
#include <windows.h>
DWORD EventTypeForMouseButton(int button, bool isDown)
{
switch (button)
{
case wxMOUSE_BTN_LEFT:
if (isDown)
return MOUSEEVENTF_LEFTDOWN;
else
return MOUSEEVENTF_LEFTUP;
case wxMOUSE_BTN_RIGHT:
if (isDown)
return MOUSEEVENTF_RIGHTDOWN;
else
return MOUSEEVENTF_RIGHTUP;
case wxMOUSE_BTN_MIDDLE:
if (isDown)
return MOUSEEVENTF_MIDDLEDOWN;
else
return MOUSEEVENTF_MIDDLEUP;
default:
wxFAIL_MSG("Unsupported button passed in.");
return -1;
}
}
bool wxUIActionSimulator::MouseDown(int button)
{
POINT p;
GetCursorPos(&p);
mouse_event(EventTypeForMouseButton(button, true), p.x, p.y, 0, 0);
return true;
}
bool wxUIActionSimulator::MouseMove(long x, long y)
{
mouse_event(MOUSEEVENTF_MOVE, x, y, 0, 0);
return true;
}
bool wxUIActionSimulator::MouseUp(int button)
{
POINT p;
GetCursorPos(&p);
mouse_event(EventTypeForMouseButton(button, false), p.x, p.y, 0, 0);
return true;
}
bool wxUIActionSimulator::Key(int keycode, bool isDown, bool shiftDown, bool cmdDown, bool altDown)
{
DWORD flags = 0;
if (!isDown)
flags = KEYEVENTF_KEYUP;
keybd_event(keycode, 0, flags, 0);
return true;
}

132
src/osx/uiaction_osx.cpp Normal file
View File

@@ -0,0 +1,132 @@
/////////////////////////////////////////////////////////////////////////////
// Name: src/osx/uiaction_osx.cpp
// Purpose: wxUIActionSimulator implementation
// Author: Kevin Ollivier
// Modified by:
// Created: 2010-03-06
// RCS-ID: $Id: menu.cpp 54129 2008-06-11 19:30:52Z SC $
// Copyright: (c) Kevin Ollivier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include <wx/defs.h>
#include <wx/uiaction.h>
#include <ApplicationServices/ApplicationServices.h>
CGEventTapLocation tap = kCGSessionEventTap;
CGEventType CGEventTypeForMouseButton(int button, bool isDown)
{
switch (button)
{
case wxMOUSE_BTN_LEFT:
if (isDown)
return kCGEventLeftMouseDown;
else
return kCGEventLeftMouseUp;
case wxMOUSE_BTN_RIGHT:
if (isDown)
return kCGEventRightMouseDown;
else
return kCGEventRightMouseUp;
// Apparently all other buttons use the constant OtherMouseDown
default:
if (isDown)
return kCGEventOtherMouseDown;
else
return kCGEventOtherMouseUp;
}
}
void SendCharCode(CGCharCode keycode, bool isDown)
{
CGEventRef event = CGEventCreateKeyboardEvent(NULL, keycode, isDown);
if (event)
{
CGEventPost(kCGHIDEventTap, event);
}
CFRelease(event);
}
bool wxUIActionSimulator::MouseDown(int button)
{
CGPoint pos;
int x, y;
wxGetMousePosition(&x, &y);
pos.x = x;
pos.y = y;
CGEventType type = CGEventTypeForMouseButton(button, true);
CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, button);
CGEventSetType(event, type);
if (event)
{
CGEventPost(tap, event);
}
CFRelease(event);
return true;
}
bool wxUIActionSimulator::MouseMove(long x, long y)
{
CGPoint pos;
pos.x = x;
pos.y = y;
CGEventType type = kCGEventMouseMoved;
CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, kCGMouseButtonLeft);
CGEventSetType(event, type);
if (event)
{
CGEventPost(tap, event);
}
CFRelease(event);
return true;
}
bool wxUIActionSimulator::MouseUp(int button)
{
CGPoint pos;
int x, y;
wxGetMousePosition(&x, &y);
pos.x = x;
pos.y = y;
CGEventType type = CGEventTypeForMouseButton(button, false);
CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, button);
CGEventSetType(event, type);
if (event)
{
CGEventPost(tap, event);
}
CFRelease(event);
return true;
}
bool wxUIActionSimulator::Key(int keycode, bool isDown, bool shiftDown, bool cmdDown, bool altDown)
{
if (shiftDown)
SendCharCode((CGCharCode)56, true);
if (altDown)
SendCharCode((CGCharCode)58, true);
if (cmdDown)
SendCharCode((CGCharCode)55, true);
SendCharCode((CGCharCode)keycode, isDown);
if (shiftDown)
SendCharCode((CGCharCode)56, false);
if (altDown)
SendCharCode((CGCharCode)58, false);
if (cmdDown)
SendCharCode((CGCharCode)55, false);
return true;
}

123
src/unix/uiactionx11.cpp Normal file
View File

@@ -0,0 +1,123 @@
/////////////////////////////////////////////////////////////////////////////
// Name: src/unix/uiactionx11.cpp
// Purpose: wxUIActionSimulator implementation
// Author: Kevin Ollivier
// Modified by:
// Created: 2010-03-06
// RCS-ID: $Id: menu.cpp 54129 2008-06-11 19:30:52Z SC $
// Copyright: (c) Kevin Ollivier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include <wx/defs.h>
#include <wx/uiaction.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
void SendButtonEvent(int button, bool isDown)
{
int xbutton = 0;
switch (button)
{
case wxMOUSE_BTN_LEFT:
xbutton = 1;
break;
case wxMOUSE_BTN_RIGHT:
xbutton = 2;
break;
case wxMOUSE_BTN_MIDDLE:
xbutton = 3;
break;
default:
wxFAIL_MSG("Unsupported button passed in.");
}
XEvent event;
Display *display = XOpenDisplay(0);
wxASSERT_MSG(display, "No display available!");
memset(&event, 0x00, sizeof(event));
if (isDown)
event.type = ButtonPress;
else
event.type = ButtonRelease;
event.xbutton.button = xbutton;
event.xbutton.same_screen = True;
XQueryPointer(display, RootWindow(display, DefaultScreen(display)), &event.xbutton.root, &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
event.xbutton.subwindow = event.xbutton.window;
while (event.xbutton.subwindow)
{
event.xbutton.window = event.xbutton.subwindow;
XQueryPointer(display, event.xbutton.window, &event.xbutton.root, &event.xbutton.subwindow, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
}
XSendEvent(display, PointerWindow, True, 0xfff, &event);
XFlush(display);
XCloseDisplay(display);
}
bool wxUIActionSimulator::MouseDown(int button)
{
SendButtonEvent(button, true);
return true;
}
bool wxUIActionSimulator::MouseMove(long x, long y)
{
Display *display = XOpenDisplay(0);
wxASSERT_MSG(display, "No display available!");
Window root = DefaultRootWindow(display);
XWarpPointer(display, None, root, 0, 0, 0, 0, x, y);
XFlush(display);
XCloseDisplay(display);
return true;
}
bool wxUIActionSimulator::MouseUp(int button)
{
SendButtonEvent(button, false);
return true;
}
bool wxUIActionSimulator::Key(int keycode, bool isDown, bool WXUNUSED(shiftDown), bool WXUNUSED(cmdDown), bool WXUNUSED(altDown))
{
Display *display = XOpenDisplay(0);
wxASSERT_MSG(display, "No display available!");
XKeyEvent event;
int mask = 0xfff;
memset(&event, 0x00, sizeof(event));
if (isDown) {
event.type = KeyPress;
mask = KeyPressMask;
}
else {
event.type = KeyRelease;
mask = KeyReleaseMask;
}
event.same_screen = True;
XQueryPointer(display, RootWindow(display, DefaultScreen(display)), &event.root, &event.window, &event.x_root, &event.y_root, &event.x, &event.y, &event.state);
event.subwindow = event.window;
while (event.subwindow)
{
event.window = event.subwindow;
XQueryPointer(display, event.window, &event.root, &event.subwindow, &event.x_root, &event.y_root, &event.x, &event.y, &event.state);
}
XSendEvent(display, PointerWindow, True, mask, (XEvent*) &event);
XFlush(display);
XCloseDisplay(display);
return true;
}