diff --git a/configure b/configure index 58cb107264..fb8476aeae 100755 --- a/configure +++ b/configure @@ -945,6 +945,8 @@ WEBKIT_LIBS WEBKIT_CFLAGS COND_PYTHON PYTHON +XTST_LIBS +XTST_CFLAGS LIBNOTIFY_LIBS LIBNOTIFY_CFLAGS GNOMEVFS_LIBS @@ -1094,6 +1096,7 @@ with_gtkprint with_gnomevfs with_libnotify with_opengl +with_xtest with_dmalloc with_sdl with_regex @@ -1402,6 +1405,8 @@ GNOMEVFS_CFLAGS GNOMEVFS_LIBS LIBNOTIFY_CFLAGS LIBNOTIFY_LIBS +XTST_CFLAGS +XTST_LIBS WEBKIT_CFLAGS WEBKIT_LIBS CAIRO_CFLAGS @@ -2324,6 +2329,7 @@ Optional Packages: --with-gnomevfs use GNOME VFS for associating MIME types --with-libnotify use libnotify for notifications --with-opengl use OpenGL (or Mesa) + --with-xtest use XTest extension --with-dmalloc use dmalloc library (http://dmalloc.com/) --with-sdl use SDL for audio on Unix --with-regex enable support for wxRegEx class @@ -2401,6 +2407,8 @@ Some influential environment variables: C compiler flags for LIBNOTIFY, overriding pkg-config LIBNOTIFY_LIBS linker flags for LIBNOTIFY, overriding pkg-config + XTST_CFLAGS C compiler flags for XTST, overriding pkg-config + XTST_LIBS linker flags for XTST, overriding pkg-config WEBKIT_CFLAGS C compiler flags for WEBKIT, overriding pkg-config WEBKIT_LIBS linker flags for WEBKIT, overriding pkg-config @@ -4833,6 +4841,35 @@ fi eval "$wx_cv_use_opengl" + withstring= + defaultval=$wxUSE_ALL_FEATURES + if test -z "$defaultval"; then + if test x"$withstring" = xwithout; then + defaultval=yes + else + defaultval=no + fi + fi + +# Check whether --with-xtest was given. +if test "${with_xtest+set}" = set; then : + withval=$with_xtest; + if test "$withval" = yes; then + wx_cv_use_xtest='wxUSE_XTEST=yes' + else + wx_cv_use_xtest='wxUSE_XTEST=no' + fi + +else + + wx_cv_use_xtest='wxUSE_XTEST=${'DEFAULT_wxUSE_XTEST":-$defaultval}" + +fi + + + eval "$wx_cv_use_xtest" + + fi @@ -33666,9 +33703,109 @@ if test "$wxUSE_MOUSEWHEEL" = "yes" ; then fi if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then - $as_echo "#define wxUSE_UIACTIONSIMULATOR 1" >>confdefs.h + if test "$wxUSE_GTK" = 1 -o "$wxUSE_MOTIF" = 1 -o "$wxUSE_X11" = 1; then + if test "$wxUSE_XTEST" = "yes" ; then - SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XTST" >&5 +$as_echo_n "checking for XTST... " >&6; } + +if test -n "$PKG_CONFIG"; then + if test -n "$XTST_CFLAGS"; then + pkg_cv_XTST_CFLAGS="$XTST_CFLAGS" + else + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xtst\""; } >&5 + ($PKG_CONFIG --exists --print-errors "xtst") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_XTST_CFLAGS=`$PKG_CONFIG --cflags "xtst" 2>/dev/null` +else + pkg_failed=yes +fi + fi +else + pkg_failed=untried +fi +if test -n "$PKG_CONFIG"; then + if test -n "$XTST_LIBS"; then + pkg_cv_XTST_LIBS="$XTST_LIBS" + else + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xtst\""; } >&5 + ($PKG_CONFIG --exists --print-errors "xtst") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_XTST_LIBS=`$PKG_CONFIG --libs "xtst" 2>/dev/null` +else + pkg_failed=yes +fi + fi +else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + XTST_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "xtst"` + else + XTST_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "xtst"` + fi + # Put the nasty error message in config.log where it belongs + echo "$XTST_PKG_ERRORS" >&5 + + + if test "$WXGTK3" = 1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest not found, disabling wxUIActionSimulator" >&5 +$as_echo "$as_me: WARNING: XTest not found, disabling wxUIActionSimulator" >&2;} + wxUSE_UIACTIONSIMULATOR=no + fi + wxUSE_XTEST="no" + + +elif test $pkg_failed = untried; then + + if test "$WXGTK3" = 1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest not found, disabling wxUIActionSimulator" >&5 +$as_echo "$as_me: WARNING: XTest not found, disabling wxUIActionSimulator" >&2;} + wxUSE_UIACTIONSIMULATOR=no + fi + wxUSE_XTEST="no" + + +else + XTST_CFLAGS=$pkg_cv_XTST_CFLAGS + XTST_LIBS=$pkg_cv_XTST_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + + GUI_TK_LIBRARY="$GUI_TK_LIBRARY $XTST_LIBS" + CFLAGS="$XTST_CFLAGS $CFLAGS" + CXXFLAGS="$XTST_CFLAGS $CXXFLAGS" + $as_echo "#define wxUSE_XTEST 1" >>confdefs.h + + +fi + elif test "$WXGTK3" = 1; then + wxUSE_UIACTIONSIMULATOR=no + fi + fi + + if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then + $as_echo "#define wxUSE_UIACTIONSIMULATOR 1" >>confdefs.h + + SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" + fi fi if test "$wxUSE_DC_TRANSFORM_MATRIX" = "yes" ; then diff --git a/configure.in b/configure.in index 2c6c6a14d4..15b95395ee 100644 --- a/configure.in +++ b/configure.in @@ -542,6 +542,7 @@ WX_ARG_WITHOUT(gtkprint, [ --without-gtkprint don't use GTK printing sup WX_ARG_WITH(gnomevfs, [ --with-gnomevfs use GNOME VFS for associating MIME types], wxUSE_LIBGNOMEVFS) WX_ARG_WITH(libnotify, [ --with-libnotify use libnotify for notifications], wxUSE_LIBNOTIFY) WX_ARG_WITH(opengl, [ --with-opengl use OpenGL (or Mesa)], wxUSE_OPENGL) +WX_ARG_WITH(xtest, [ --with-xtest use XTest extension], wxUSE_XTEST) fi dnl for GUI only @@ -6389,8 +6390,38 @@ if test "$wxUSE_MOUSEWHEEL" = "yes" ; then fi if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then - AC_DEFINE(wxUSE_UIACTIONSIMULATOR) - SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" + if test "$wxUSE_GTK" = 1 -o "$wxUSE_MOTIF" = 1 -o "$wxUSE_X11" = 1; then + if test "$wxUSE_XTEST" = "yes" ; then + PKG_CHECK_MODULES(XTST, xtst, + [ + GUI_TK_LIBRARY="$GUI_TK_LIBRARY $XTST_LIBS" + CFLAGS="$XTST_CFLAGS $CFLAGS" + CXXFLAGS="$XTST_CFLAGS $CXXFLAGS" + AC_DEFINE(wxUSE_XTEST) + ], + [ + if test "$WXGTK3" = 1; then + dnl This class can't work without XTest with GTK+ 3 + dnl which uses XInput2 and so ignores XSendEvent(). + AC_MSG_WARN([XTest not found, disabling wxUIActionSimulator]) + wxUSE_UIACTIONSIMULATOR=no + fi + dnl The other ports can use XSendEvent(), so don't warn + wxUSE_XTEST="no" + ] + ) + elif test "$WXGTK3" = 1; then + dnl As per above, wxUIActionSimulator can't be used in this case, + dnl but there is no need to warn, presumably the user knows what + dnl he's doing if --without-xtest was explicitly specified. + wxUSE_UIACTIONSIMULATOR=no + fi + fi + + if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then + AC_DEFINE(wxUSE_UIACTIONSIMULATOR) + SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" + fi fi if test "$wxUSE_DC_TRANSFORM_MATRIX" = "yes" ; then diff --git a/docs/changes.txt b/docs/changes.txt index 644a31248e..88a20de802 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -88,6 +88,7 @@ All (GUI): wxGTK: +- Make wxUIActionSimulator work with GTK+ 3 (Scott Talbert). - Implement setting link colours in wxHyperlinkCtrl for GTK+3 (Hanmac). - Support background colour in wxDataViewCtrl attributes. - Improve wxSpinCtrl best size calculation. diff --git a/docs/doxygen/mainpages/const_wxusedef.h b/docs/doxygen/mainpages/const_wxusedef.h index b81cd786d6..1e474318c9 100644 --- a/docs/doxygen/mainpages/const_wxusedef.h +++ b/docs/doxygen/mainpages/const_wxusedef.h @@ -267,6 +267,7 @@ library: @itemdef{wxUSE_LIBSDL, Use SDL for wxSound implementation.} @itemdef{wxUSE_PLUGINS, See also wxUSE_LIBSDL.} @itemdef{wxUSE_UNIX, Enabled on Unix Platform.} +@itemdef(wxUSE_XTEST, Use XTest extension.} @endDefList diff --git a/include/wx/private/uiaction.h b/include/wx/private/uiaction.h new file mode 100644 index 0000000000..ed6417ac3c --- /dev/null +++ b/include/wx/private/uiaction.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/private/uiaction.h +// Purpose: wxUIActionSimulatorImpl declaration +// Author: Vadim Zeitlin +// Created: 2016-05-21 +// Copyright: (c) 2016 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PRIVATE_UIACTION_H_ +#define _WX_PRIVATE_UIACTION_H_ + +// ---------------------------------------------------------------------------- +// Platform-specific implementation of wxUIActionSimulator +// ---------------------------------------------------------------------------- + +class wxUIActionSimulatorImpl +{ +public: + wxUIActionSimulatorImpl() { } + virtual ~wxUIActionSimulatorImpl() { } + + // Low level mouse methods which must be implemented in the derived class. + virtual bool MouseMove(long x, long y) = 0; + virtual bool MouseDown(int button = wxMOUSE_BTN_LEFT) = 0; + virtual bool MouseUp(int button = wxMOUSE_BTN_LEFT) = 0; + + // Higher level mouse methods which have default implementation in the base + // class but can be overridden if necessary. + virtual bool MouseClick(int button = wxMOUSE_BTN_LEFT); + virtual bool MouseDblClick(int button = wxMOUSE_BTN_LEFT); + virtual bool MouseDragDrop(long x1, long y1, long x2, long y2, + int button = wxMOUSE_BTN_LEFT); + + // The low-level port-specific function which really generates the key + // presses. It should generate exactly one key event with the given + // parameters. + virtual bool DoKey(int keycode, int modifiers, bool isDown) = 0; + +private: + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorImpl); +}; + +#endif // _WX_PRIVATE_UIACTION_H_ diff --git a/include/wx/uiaction.h b/include/wx/uiaction.h index 498da69821..9b8df1094f 100644 --- a/include/wx/uiaction.h +++ b/include/wx/uiaction.h @@ -2,11 +2,10 @@ // Name: wx/uiaction.h // Purpose: wxUIActionSimulator interface // Author: Kevin Ollivier, Steven Lamerton, Vadim Zeitlin -// Modified by: // Created: 2010-03-06 -// Copyright: (c) Kevin Ollivier +// Copyright: (c) 2010 Kevin Ollivier // (c) 2010 Steven Lamerton -// (c) 2010 Vadim Zeitlin +// (c) 2010-2016 Vadim Zeitlin // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -22,11 +21,8 @@ class WXDLLIMPEXP_CORE wxUIActionSimulator { public: - wxUIActionSimulator() { } - - - // Default dtor, copy ctor and assignment operator are ok (even though the - // last two don't make much sense for this class). + wxUIActionSimulator(); + ~wxUIActionSimulator(); // Mouse simulation @@ -82,10 +78,12 @@ private: void SimulateModifiers(int modifier, bool isDown); - // The low-level port-specific function which really generates the key - // presses. It should generate exactly one key event with the given - // parameters. - bool DoKey(int keycode, int modifiers, bool isDown); + + // This pointer is allocated in the ctor and points to the + // platform-specific implementation. + class wxUIActionSimulatorImpl* const m_impl; + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulator); }; #endif // wxUSE_UIACTIONSIMULATOR diff --git a/include/wx/unix/chkconf.h b/include/wx/unix/chkconf.h index b8a1904133..d51d36466d 100644 --- a/include/wx/unix/chkconf.h +++ b/include/wx/unix/chkconf.h @@ -41,3 +41,11 @@ # endif # endif #endif /* wxUSE_GSTREAMER */ + +#ifndef wxUSE_XTEST +# ifdef wxABORT_ON_CONFIG_ERROR +# error "wxUSE_XTEST must be defined, please read comment near the top of this file." +# else +# define wxUSE_XTEST 0 +# endif +#endif /* !defined(wxUSE_XTEST) */ diff --git a/include/wx/unix/utilsx11.h b/include/wx/unix/utilsx11.h index e3019acc67..498b22f061 100644 --- a/include/wx/unix/utilsx11.h +++ b/include/wx/unix/utilsx11.h @@ -67,6 +67,13 @@ public: wxX11Display() { m_dpy = XOpenDisplay(NULL); } ~wxX11Display() { if ( m_dpy ) XCloseDisplay(m_dpy); } + // Pseudo move ctor: steals the open display from the other object. + explicit wxX11Display(wxX11Display& display) + { + m_dpy = display.m_dpy; + display.m_dpy = NULL; + } + operator Display *() const { return m_dpy; } // Using DefaultRootWindow() with an object of wxX11Display class doesn't diff --git a/samples/uiaction/uiaction.cpp b/samples/uiaction/uiaction.cpp index 87fcf2f575..1211102661 100644 --- a/samples/uiaction/uiaction.cpp +++ b/samples/uiaction/uiaction.cpp @@ -2,9 +2,9 @@ // Name: uiaction.cpp // Purpose: wxUIActionSimulator sample // Author: Kevin Ollivier -// Modified by: // Created: 04/01/98 -// Copyright: (c) Kevin Ollivier, Steven Lamerton +// Copyright: (c) 2010 Kevin Ollivier, Steven Lamerton +// (c) 2016 Vadim Zeitlin // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -33,6 +33,8 @@ #include "wx/uiaction.h" #endif +#include "wx/stopwatch.h" + // ---------------------------------------------------------------------------- // resources // ---------------------------------------------------------------------------- @@ -76,6 +78,7 @@ public: MyFrame(const wxString& title); void OnButtonPressed(wxCommandEvent& event); + void OnNew(wxCommandEvent& event); void OnRunSimulation(wxCommandEvent& event); void OnSimulateText(wxCommandEvent& event); void OnExit(wxCommandEvent& WXUNUSED(event)) { Close(); } @@ -89,6 +92,7 @@ private: wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_BUTTON(wxID_ANY, MyFrame::OnButtonPressed) + EVT_MENU(wxID_NEW, MyFrame::OnNew) EVT_MENU(RunSimulation, MyFrame::OnRunSimulation) EVT_MENU(SimulateText, MyFrame::OnSimulateText) EVT_MENU(wxID_EXIT, MyFrame::OnExit) @@ -139,9 +143,9 @@ MyFrame::MyFrame(const wxString& title) wxMenu *fileMenu = new wxMenu; fileMenu->Append(wxID_NEW, "&New File...", "Open a new file"); - fileMenu->Append(RunSimulation, "&Run Simulation", + fileMenu->Append(RunSimulation, "&Run Simulation\tCtrl-R", "Run predefined UI action simulation"); - fileMenu->Append(SimulateText, "Simulate &text input...", + fileMenu->Append(SimulateText, "Simulate &text input...\tCtrl-T", "Enter text to simulate"); fileMenu->AppendSeparator(); @@ -170,8 +174,25 @@ MyFrame::MyFrame(const wxString& title) // event handlers +void MyFrame::OnNew(wxCommandEvent& WXUNUSED(event)) +{ + m_text->AppendText("\"New\" menu item was selected\n"); +} + void MyFrame::OnRunSimulation(wxCommandEvent& WXUNUSED(event)) { + m_text->SetValue("=== Starting the simulation " + "(release any pressed keys) ===\n"); + + // This sleep is needed to give the time for the currently pressed modifier + // keys, if any, to be released. Notice that Control modifier could well be + // pressed if this command was activated from the menu using accelerator + // and keeping it pressed would totally derail the test below, e.g. "A" key + // press would actually become "Ctrl+A" selecting the entire text and so on. + wxMilliSleep(500); + + wxStopWatch sw; + wxUIActionSimulator sim; // Add some extra distance to take account of window decorations @@ -193,6 +214,15 @@ void MyFrame::OnRunSimulation(wxCommandEvent& WXUNUSED(event)) sim.Text("1 234.57e-8"); sim.Char(WXK_RETURN); + // Process the resulting text events + wxYield(); + + // Emulate opening a menu from keyboard. + sim.Char('F', wxMOD_ALT); + sim.Char('N'); + wxYield(); + + m_text->AppendText(wxString::Format("\n=== Done in %ldms ===\n", sw.Time())); } void MyFrame::OnSimulateText(wxCommandEvent& WXUNUSED(event)) diff --git a/setup.h.in b/setup.h.in index 24564f34b5..06c9fc942d 100644 --- a/setup.h.in +++ b/setup.h.in @@ -639,6 +639,16 @@ #define wxUSE_GSTREAMER_PLAYER 0 +/* + Use XTest extension to implement wxUIActionSimulator? + + Default is 1, it is set to 0 if the necessary headers/libraries are not + found by configure. + + Recommended setting: 1, wxUIActionSimulator won't work in wxGTK3 without it. + */ +#define wxUSE_XTEST 0 + /* --- start MSW options --- */ diff --git a/src/common/uiactioncmn.cpp b/src/common/uiactioncmn.cpp index a74a0d10ec..3c9b41fadb 100644 --- a/src/common/uiactioncmn.cpp +++ b/src/common/uiactioncmn.cpp @@ -2,11 +2,10 @@ // Name: src/common/uiactioncmn.cpp // Purpose: wxUIActionSimulator common implementation // Author: Kevin Ollivier, Steven Lamerton, Vadim Zeitlin -// Modified by: // Created: 2010-03-06 -// Copyright: (c) Kevin Ollivier +// Copyright: (c) 2010 Kevin Ollivier // (c) 2010 Steven Lamerton -// (c) 2010 Vadim Zeitlin +// (c) 2010-2016 Vadim Zeitlin // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -24,8 +23,48 @@ #include "wx/listbox.h" #endif // wxNO_RTTI +#include "wx/private/uiaction.h" + +// ---------------------------------------------------------------------------- +// Methods forwarded to wxUIActionSimulatorImpl +// ---------------------------------------------------------------------------- + +bool wxUIActionSimulator::MouseMove(long x, long y) +{ + return m_impl->MouseMove(x, y); +} + +bool wxUIActionSimulator::MouseDown(int button) +{ + return m_impl->MouseDown(button); +} + +bool wxUIActionSimulator::MouseUp(int button) +{ + return m_impl->MouseUp(button); +} bool wxUIActionSimulator::MouseClick(int button) +{ + return m_impl->MouseClick(button); +} + +bool wxUIActionSimulator::MouseDblClick(int button) +{ + return m_impl->MouseDblClick(button); +} + +bool wxUIActionSimulator::MouseDragDrop(long x1, long y1, long x2, long y2, + int button) +{ + return m_impl->MouseDragDrop(x1, y1, x2, y2, button); +} + +// ---------------------------------------------------------------------------- +// Methods implemented in wxUIActionSimulatorImpl itself +// ---------------------------------------------------------------------------- + +bool wxUIActionSimulatorImpl::MouseClick(int button) { MouseDown(button); MouseUp(button); @@ -33,9 +72,7 @@ bool wxUIActionSimulator::MouseClick(int button) return true; } -#ifndef __WXOSX__ - -bool wxUIActionSimulator::MouseDblClick(int button) +bool wxUIActionSimulatorImpl::MouseDblClick(int button) { MouseDown(button); MouseUp(button); @@ -45,7 +82,7 @@ bool wxUIActionSimulator::MouseDblClick(int button) return true; } -bool wxUIActionSimulator::MouseDragDrop(long x1, long y1, long x2, long y2, +bool wxUIActionSimulatorImpl::MouseDragDrop(long x1, long y1, long x2, long y2, int button) { MouseMove(x1, y1); @@ -56,8 +93,6 @@ bool wxUIActionSimulator::MouseDragDrop(long x1, long y1, long x2, long y2, return true; } -#endif - bool wxUIActionSimulator::Key(int keycode, int modifiers, bool isDown) { @@ -69,7 +104,7 @@ wxUIActionSimulator::Key(int keycode, int modifiers, bool isDown) if ( isDown ) SimulateModifiers(modifiers, true); - bool rc = DoKey(keycode, modifiers, isDown); + bool rc = m_impl->DoKey(keycode, modifiers, isDown); if ( !isDown ) SimulateModifiers(modifiers, false); @@ -80,11 +115,11 @@ wxUIActionSimulator::Key(int keycode, int modifiers, bool isDown) void wxUIActionSimulator::SimulateModifiers(int modifiers, bool isDown) { if ( modifiers & wxMOD_SHIFT ) - DoKey(WXK_SHIFT, modifiers, isDown); + m_impl->DoKey(WXK_SHIFT, modifiers, isDown); if ( modifiers & wxMOD_ALT ) - DoKey(WXK_ALT, modifiers, isDown); + m_impl->DoKey(WXK_ALT, modifiers, isDown); if ( modifiers & wxMOD_CONTROL ) - DoKey(WXK_CONTROL, modifiers, isDown); + m_impl->DoKey(WXK_CONTROL, modifiers, isDown); } bool wxUIActionSimulator::Char(int keycode, int modifiers) diff --git a/src/msw/uiaction.cpp b/src/msw/uiaction.cpp index 5308d9c0f1..9860d76b33 100644 --- a/src/msw/uiaction.cpp +++ b/src/msw/uiaction.cpp @@ -19,6 +19,8 @@ #endif #include "wx/uiaction.h" +#include "wx/private/uiaction.h" + #include "wx/msw/wrapwin.h" #include "wx/msw/private/keyboard.h" @@ -28,6 +30,31 @@ namespace { +class wxUIActionSimulatorMSWImpl : public wxUIActionSimulatorImpl +{ +public: + // Returns a pointer to the global simulator object: as it's stateless, we + // can reuse the same one without having to allocate it on the heap all the + // time. + static wxUIActionSimulatorMSWImpl* Get() + { + static wxUIActionSimulatorMSWImpl s_impl; + return &s_impl; + } + + virtual bool MouseMove(long x, long y) wxOVERRIDE; + virtual bool MouseDown(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + virtual bool MouseUp(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + + virtual bool DoKey(int keycode, int modifiers, bool isDown) wxOVERRIDE; + +private: + // This class has no public ctors, use Get() instead. + wxUIActionSimulatorMSWImpl() { } + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorMSWImpl); +}; + DWORD EventTypeForMouseButton(int button, bool isDown) { switch (button) @@ -49,7 +76,7 @@ DWORD EventTypeForMouseButton(int button, bool isDown) } // anonymous namespace -bool wxUIActionSimulator::MouseDown(int button) +bool wxUIActionSimulatorMSWImpl::MouseDown(int button) { POINT p; wxGetCursorPosMSW(&p); @@ -57,7 +84,7 @@ bool wxUIActionSimulator::MouseDown(int button) return true; } -bool wxUIActionSimulator::MouseMove(long x, long y) +bool wxUIActionSimulatorMSWImpl::MouseMove(long x, long y) { // Because MOUSEEVENTF_ABSOLUTE takes measurements scaled between 0 & 65535 // we need to scale our input too @@ -73,7 +100,7 @@ bool wxUIActionSimulator::MouseMove(long x, long y) return true; } -bool wxUIActionSimulator::MouseUp(int button) +bool wxUIActionSimulatorMSWImpl::MouseUp(int button) { POINT p; wxGetCursorPosMSW(&p); @@ -82,7 +109,7 @@ bool wxUIActionSimulator::MouseUp(int button) } bool -wxUIActionSimulator::DoKey(int keycode, int WXUNUSED(modifiers), bool isDown) +wxUIActionSimulatorMSWImpl::DoKey(int keycode, int WXUNUSED(modifiers), bool isDown) { bool isExtended; DWORD vkkeycode = wxMSWKeyboard::WXToVK(keycode, &isExtended); @@ -98,4 +125,15 @@ wxUIActionSimulator::DoKey(int keycode, int WXUNUSED(modifiers), bool isDown) return true; } +wxUIActionSimulator::wxUIActionSimulator() + : m_impl(wxUIActionSimulatorMSWImpl::Get()) +{ +} + +wxUIActionSimulator::~wxUIActionSimulator() +{ + // We can use a static wxUIActionSimulatorMSWImpl object because it's + // stateless, so no need to delete it. +} + #endif // wxUSE_UIACTIONSIMULATOR diff --git a/src/osx/uiaction_osx.cpp b/src/osx/uiaction_osx.cpp index 78112ef07e..31b7d987ae 100644 --- a/src/osx/uiaction_osx.cpp +++ b/src/osx/uiaction_osx.cpp @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////// // Name: src/osx/uiaction_osx.cpp -// Purpose: wxUIActionSimulator implementation +// Purpose: wxUIActionSimulatorOSXImpl implementation // Author: Kevin Ollivier, Steven Lamerton, Vadim Zeitlin // Modified by: // Created: 2010-03-06 @@ -19,6 +19,7 @@ #if wxUSE_UIACTIONSIMULATOR #include "wx/uiaction.h" +#include "wx/private/uiaction.h" #include "wx/log.h" @@ -111,9 +112,38 @@ CGPoint GetMousePosition() return pos; } +class wxUIActionSimulatorOSXImpl : public wxUIActionSimulatorImpl +{ +public: + // Returns a pointer to the global simulator object: as it's stateless, we + // can reuse the same one without having to allocate it on the heap all the + // time. + static wxUIActionSimulatorOSXImpl* Get() + { + static wxUIActionSimulatorOSXImpl s_impl; + return &s_impl; + } + + virtual bool MouseMove(long x, long y) wxOVERRIDE; + virtual bool MouseDown(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + virtual bool MouseUp(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + + virtual bool MouseDblClick(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + virtual bool MouseDragDrop(long x1, long y1, long x2, long y2, + int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + + virtual bool DoKey(int keycode, int modifiers, bool isDown) wxOVERRIDE; + +private: + // This class has no public ctors, use Get() instead. + wxUIActionSimulatorOSXImpl() { } + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorOSXImpl); +}; + } // anonymous namespace -bool wxUIActionSimulator::MouseDown(int button) +bool wxUIActionSimulatorOSXImpl::MouseDown(int button) { CGEventType type = CGEventTypeForMouseButton(button, true); wxCFRef event( @@ -131,7 +161,7 @@ bool wxUIActionSimulator::MouseDown(int button) return true; } -bool wxUIActionSimulator::MouseMove(long x, long y) +bool wxUIActionSimulatorOSXImpl::MouseMove(long x, long y) { CGPoint pos; pos.x = x; @@ -154,7 +184,7 @@ bool wxUIActionSimulator::MouseMove(long x, long y) return true; } -bool wxUIActionSimulator::MouseUp(int button) +bool wxUIActionSimulatorOSXImpl::MouseUp(int button) { CGEventType type = CGEventTypeForMouseButton(button, false); wxCFRef event( @@ -172,7 +202,7 @@ bool wxUIActionSimulator::MouseUp(int button) return true; } -bool wxUIActionSimulator::MouseDblClick(int button) +bool wxUIActionSimulatorOSXImpl::MouseDblClick(int button) { CGEventType downtype = CGEventTypeForMouseButton(button, true); CGEventType uptype = CGEventTypeForMouseButton(button, false); @@ -201,7 +231,7 @@ bool wxUIActionSimulator::MouseDblClick(int button) return true; } -bool wxUIActionSimulator::MouseDragDrop(long x1, long y1, long x2, long y2, +bool wxUIActionSimulatorOSXImpl::MouseDragDrop(long x1, long y1, long x2, long y2, int button) { CGPoint pos1,pos2; @@ -241,7 +271,7 @@ bool wxUIActionSimulator::MouseDragDrop(long x1, long y1, long x2, long y2, } bool -wxUIActionSimulator::DoKey(int keycode, int WXUNUSED(modifiers), bool isDown) +wxUIActionSimulatorOSXImpl::DoKey(int keycode, int WXUNUSED(modifiers), bool isDown) { CGKeyCode cgcode = wxCharCodeWXToOSX((wxKeyCode)keycode); @@ -258,5 +288,15 @@ wxUIActionSimulator::DoKey(int keycode, int WXUNUSED(modifiers), bool isDown) return true; } -#endif // wxUSE_UIACTIONSIMULATOR +wxUIActionSimulator::wxUIActionSimulator() + : m_impl(wxUIActionSimulatorOSXImpl::Get()) +{ +} +wxUIActionSimulator::~wxUIActionSimulator() +{ + // We can use a static wxUIActionSimulatorOSXImpl object because it's + // stateless, so no need to delete it. +} + +#endif // wxUSE_UIACTIONSIMULATOR diff --git a/src/qt/uiaction.cpp b/src/qt/uiaction.cpp index 833d5c9f75..6490cf35d9 100644 --- a/src/qt/uiaction.cpp +++ b/src/qt/uiaction.cpp @@ -12,13 +12,15 @@ #pragma hdrstop #endif +#include "wx/uiaction.h" +#include "wx/private/uiaction.h" + #include #include #include #include "wx/qt/defs.h" #include "wx/qt/private/utils.h" -#include "wx/uiaction.h" #include "wx/qt/private/converter.h" @@ -37,6 +39,31 @@ inline QWindow* argForEvents(QWidget* w) { return w->windowHandle(); } inline QWidget* argForEvents(QWidget* w) { return w; } #endif +class wxUIActionSimulatorQtImpl : public wxUIActionSimulatorImpl +{ +public: + // Returns a pointer to the global simulator object: as it's stateless, we + // can reuse the same one without having to allocate it on the heap all the + // time. + static wxUIActionSimulatorQtImpl* Get() + { + static wxUIActionSimulatorQtImpl s_impl; + return &s_impl; + } + + virtual bool MouseMove(long x, long y) wxOVERRIDE; + virtual bool MouseDown(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + virtual bool MouseUp(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + + virtual bool DoKey(int keycode, int modifiers, bool isDown) wxOVERRIDE; + +private: + // This class has no public ctors, use Get() instead. + wxUIActionSimulatorQtImpl() { } + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorQtImpl); +}; + static MouseButton ConvertMouseButton( int button ) { MouseButton qtButton; @@ -95,24 +122,24 @@ static bool SimulateKeyboardKey( KeyAction keyAction, Key key ) return widget != NULL; } -bool wxUIActionSimulator::MouseDown( int button ) +bool wxUIActionSimulatorQtImpl::MouseDown( int button ) { return SimulateMouseButton( MousePress, ConvertMouseButton( button )); } -bool wxUIActionSimulator::MouseUp(int button) +bool wxUIActionSimulatorQtImpl::MouseUp(int button) { return SimulateMouseButton( MouseRelease, ConvertMouseButton( button )); } -bool wxUIActionSimulator::MouseMove(long x, long y) +bool wxUIActionSimulatorQtImpl::MouseMove(long x, long y) { QCursor::setPos( x, y ); return true; } -bool wxUIActionSimulator::DoKey(int keyCode, int modifiers, bool isDown) +bool wxUIActionSimulatorQtImpl::DoKey(int keyCode, int modifiers, bool isDown) { Qt::KeyboardModifiers qtmodifiers; enum Key key; @@ -124,5 +151,16 @@ bool wxUIActionSimulator::DoKey(int keyCode, int modifiers, bool isDown) return SimulateKeyboardKey( keyAction, key ); } -#endif // wxUSE_UIACTIONSIMULATOR +wxUIActionSimulator::wxUIActionSimulator() + : m_impl(wxUIActionSimulatorQtImpl::Get()) +{ +} + +wxUIActionSimulator::~wxUIActionSimulator() +{ + // We can use a static wxUIActionSimulatorQtImpl object because it's + // stateless, so no need to delete it. +} + +#endif // wxUSE_UIACTIONSIMULATOR diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index a19a3d2284..4bf33c93b0 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -2,11 +2,10 @@ // Name: src/unix/uiactionx11.cpp // Purpose: wxUIActionSimulator implementation // Author: Kevin Ollivier, Steven Lamerton, Vadim Zeitlin -// Modified by: // Created: 2010-03-06 -// Copyright: (c) Kevin Ollivier +// Copyright: (c) 2010 Kevin Ollivier // (c) 2010 Steven Lamerton -// (c) 2010 Vadim Zeitlin +// (c) 2010-2016 Vadim Zeitlin // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -18,16 +17,72 @@ #include "wx/event.h" #include "wx/evtloop.h" +#include "wx/private/uiaction.h" + #include #include +#if wxUSE_XTEST +#include +#endif #include "wx/unix/utilsx11.h" +#ifdef __WXGTK3__ +#include +#include +#endif + +// Normally we fall back on "plain X" implementation if XTest is not available, +// but it's useless to do it when using GTK+ 3 as it's not going to work with +// it anyhow because GTK+ 3 needs XInput2 events and not the "classic" ones we +// synthesize here, so don't even compile in this code for wxGTK3 port. +#define wxUSE_PLAINX_IMPL (!defined(__WXGTK3__)) + namespace { -void SendButtonEvent(int button, bool isDown) +// Base class for both available X11 implementations. +class wxUIActionSimulatorX11Impl : public wxUIActionSimulatorImpl { +public: + // Return the most appopriate implementation to use: if XTest is available, + // use it, otherwise use plain X11 calls. + // + // The returned pointer is owned by the caller. + static wxUIActionSimulatorImpl* New(); + + virtual bool MouseMove(long x, long y) wxOVERRIDE; + virtual bool MouseDown(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + virtual bool MouseUp(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; + + virtual bool DoKey(int keycode, int modifiers, bool isDown) wxOVERRIDE; + +protected: + // This ctor takes ownership of the display. + explicit wxUIActionSimulatorX11Impl(wxX11Display& display) + : m_display(display) + { + } + + wxX11Display m_display; + +private: + // Common implementation of Mouse{Down,Up}() which just forwards to + // DoX11Button() after translating wx button to X button constant. + bool SendButtonEvent(int button, bool isDown); + + virtual bool DoX11Button(int xbutton, bool isDown) = 0; + virtual bool DoX11MouseMove(long x, long y) = 0; + virtual bool DoX11Key(KeyCode xkeycode, int modifiers, bool isDown) = 0; + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorX11Impl); +}; + +bool wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) +{ + if ( !m_display ) + return false; + int xbutton; switch (button) { @@ -42,12 +97,37 @@ void SendButtonEvent(int button, bool isDown) break; default: wxFAIL_MSG("Unsupported button passed in."); - return; + return false; } - wxX11Display display; - wxCHECK_RET(display, "No display available!"); + // Ensure that the event is received by the correct window by processing + // all pending events, notably mouse moves. + XSync(m_display, False /* don't discard */); + return DoX11Button(xbutton, isDown); +} + +#if wxUSE_PLAINX_IMPL + +// Implementation using just plain X11 calls. +class wxUIActionSimulatorPlainX11Impl : public wxUIActionSimulatorX11Impl +{ +public: + explicit wxUIActionSimulatorPlainX11Impl(wxX11Display& display) + : wxUIActionSimulatorX11Impl(display) + { + } + +private: + virtual bool DoX11Button(int xbutton, bool isDown) wxOVERRIDE; + virtual bool DoX11MouseMove(long x, long y) wxOVERRIDE; + virtual bool DoX11Key(KeyCode xkeycode, int modifiers, bool isDown) wxOVERRIDE; + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorPlainX11Impl); +}; + +bool wxUIActionSimulatorPlainX11Impl::DoX11Button(int xbutton, bool isDown) +{ XEvent event; memset(&event, 0x00, sizeof(event)); @@ -55,7 +135,7 @@ void SendButtonEvent(int button, bool isDown) event.xbutton.button = xbutton; event.xbutton.same_screen = True; - XQueryPointer(display, display.DefaultRoot(), + XQueryPointer(m_display, m_display.DefaultRoot(), &event.xbutton.root, &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state); @@ -64,54 +144,29 @@ void SendButtonEvent(int button, bool isDown) while (event.xbutton.subwindow) { event.xbutton.window = event.xbutton.subwindow; - XQueryPointer(display, event.xbutton.window, + XQueryPointer(m_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); -} - -} // anonymous namespace - -bool wxUIActionSimulator::MouseDown(int button) -{ - SendButtonEvent(button, true); - return true; -} - -bool wxUIActionSimulator::MouseMove(long x, long y) -{ - wxX11Display display; - wxASSERT_MSG(display, "No display available!"); - - Window root = display.DefaultRoot(); - XWarpPointer(display, None, root, 0, 0, 0, 0, x, y); - - // At least with wxGTK we must always process the pending events before the - // mouse position change really takes effect, so just do it from here - // instead of forcing the client code using this function to always use - // wxYield() which is unnecessary under the other platforms. - if ( wxEventLoopBase* const loop = wxEventLoop::GetActive() ) - { - loop->YieldFor(wxEVT_CATEGORY_USER_INPUT); - } + XSendEvent(m_display, PointerWindow, True, 0xfff, &event); return true; } -bool wxUIActionSimulator::MouseUp(int button) +bool wxUIActionSimulatorPlainX11Impl::DoX11MouseMove(long x, long y) { - SendButtonEvent(button, false); + Window root = m_display.DefaultRoot(); + XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); return true; } -bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) +bool +wxUIActionSimulatorPlainX11Impl::DoX11Key(KeyCode xkeycode, + int modifiers, + bool isDown) { - wxX11Display display; - wxCHECK_MSG(display, false, "No display available!"); - int mask, type; if ( isDown ) @@ -125,14 +180,9 @@ bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) mask = KeyReleaseMask; } - WXKeySym xkeysym = wxCharCodeWXToX(keycode); - KeyCode xkeycode = XKeysymToKeycode(display, xkeysym); - if ( xkeycode == NoSymbol ) - return false; - Window focus; int revert; - XGetInputFocus(display, &focus, &revert); + XGetInputFocus(m_display, &focus, &revert); if (focus == None) return false; @@ -147,7 +197,7 @@ bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) mod |= ControlMask; XKeyEvent event; - event.display = display; + event.display = m_display; event.window = focus; event.root = DefaultRootWindow(event.display); event.subwindow = None; @@ -166,4 +216,141 @@ bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) return true; } +#endif // wxUSE_PLAINX_IMPL + +#if wxUSE_XTEST + +// Implementation using XTest extension. +class wxUIActionSimulatorXTestImpl : public wxUIActionSimulatorX11Impl +{ +public: + explicit wxUIActionSimulatorXTestImpl(wxX11Display& display) + : wxUIActionSimulatorX11Impl(display) + { + } + +private: + virtual bool DoX11Button(int xbutton, bool isDown) wxOVERRIDE; + virtual bool DoX11MouseMove(long x, long y) wxOVERRIDE; + virtual bool DoX11Key(KeyCode xkeycode, int modifiers, bool isDown) wxOVERRIDE; + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorXTestImpl); +}; + +bool wxUIActionSimulatorXTestImpl::DoX11Button(int xbutton, bool isDown) +{ + return XTestFakeButtonEvent(m_display, xbutton, isDown, 0) != 0; +} + +bool wxUIActionSimulatorXTestImpl::DoX11MouseMove(long x, long y) +{ +#ifdef __WXGTK3__ + // We need to take into account the scaling factor as the input coordinates + // are in GTK logical "application pixels", while we need the physical + // "device pixels" for the X call below, so scale them if we have the + // required support at both compile- and run-time. +#if GTK_CHECK_VERSION(3,10,0) + if ( gtk_check_version(3, 10, 0) == NULL ) + { + if ( GdkScreen* const screen = gdk_screen_get_default() ) + { + // For multi-monitor support we would need to determine to which + // monitor the point (x, y) belongs, for now just use the scale + // factor of the main one. + gint const scale = gdk_screen_get_monitor_scale_factor(screen, 0); + x *= scale; + y *= scale; + } + } +#endif // GTK+ 3.10+ +#endif // __WXGTK3__ + + return XTestFakeMotionEvent(m_display, -1, x, y, 0) != 0; +} + +bool +wxUIActionSimulatorXTestImpl::DoX11Key(KeyCode xkeycode, + int WXUNUSED(modifiers), + bool isDown) +{ + return XTestFakeKeyEvent(m_display, xkeycode, isDown, 0) != 0; +} + +#endif // wxUSE_XTEST + +wxUIActionSimulatorImpl* wxUIActionSimulatorX11Impl::New() +{ + wxX11Display display; + +#if wxUSE_XTEST + // If we can fall back on plain X implementation, check if XTest extension + // is available and if it isn't, use the other one. OTOH if we don't have + // the other one anyhow, then testing for XTest availability is useless. +#if wxUSE_PLAINX_IMPL + int dummy; + if ( XTestQueryExtension(display, &dummy, &dummy, &dummy, &dummy) ) +#endif // wxUSE_PLAINX_IMPL + return new wxUIActionSimulatorXTestImpl(display); +#endif // wxUSE_XTEST + +#if wxUSE_PLAINX_IMPL + return new wxUIActionSimulatorPlainX11Impl(display); +#endif // wxUSE_PLAINX_IMPL +} + +} // anonymous namespace + +bool wxUIActionSimulatorX11Impl::MouseDown(int button) +{ + return SendButtonEvent(button, true); +} + +bool wxUIActionSimulatorX11Impl::MouseMove(long x, long y) +{ + if ( !m_display ) + return false; + + if ( !DoX11MouseMove(x, y) ) + return false; + + // At least with wxGTK we must always process the pending events before the + // mouse position change really takes effect, so just do it from here + // instead of forcing the client code using this function to always use + // wxYield() which is unnecessary under the other platforms. + if ( wxEventLoopBase* const loop = wxEventLoop::GetActive() ) + { + loop->YieldFor(wxEVT_CATEGORY_USER_INPUT); + } + + return true; +} + +bool wxUIActionSimulatorX11Impl::MouseUp(int button) +{ + return SendButtonEvent(button, false); +} + +bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) +{ + if ( !m_display ) + return false; + + WXKeySym xkeysym = wxCharCodeWXToX(keycode); + KeyCode xkeycode = XKeysymToKeycode(m_display, xkeysym); + if ( xkeycode == NoSymbol ) + return false; + + return DoX11Key(xkeycode, modifiers, isDown); +} + +wxUIActionSimulator::wxUIActionSimulator() + : m_impl(wxUIActionSimulatorX11Impl::New()) +{ +} + +wxUIActionSimulator::~wxUIActionSimulator() +{ + delete m_impl; +} + #endif // wxUSE_UIACTIONSIMULATOR