From 42b53f156d768a6e8e817a0f8127eba07524a3a5 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 21 May 2016 23:04:13 +0200 Subject: [PATCH 01/16] Show the time taken by the simulation in the uiaction sample If nothing else, it allows it to see when the simulation is done. --- samples/uiaction/uiaction.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/samples/uiaction/uiaction.cpp b/samples/uiaction/uiaction.cpp index 87fcf2f575..fd5567c1bd 100644 --- a/samples/uiaction/uiaction.cpp +++ b/samples/uiaction/uiaction.cpp @@ -33,6 +33,8 @@ #include "wx/uiaction.h" #endif +#include "wx/stopwatch.h" + // ---------------------------------------------------------------------------- // resources // ---------------------------------------------------------------------------- @@ -172,6 +174,10 @@ MyFrame::MyFrame(const wxString& title) void MyFrame::OnRunSimulation(wxCommandEvent& WXUNUSED(event)) { + m_text->SetValue("=== Starting the simulation ===\n"); + + wxStopWatch sw; + wxUIActionSimulator sim; // Add some extra distance to take account of window decorations @@ -193,6 +199,10 @@ void MyFrame::OnRunSimulation(wxCommandEvent& WXUNUSED(event)) sim.Text("1 234.57e-8"); sim.Char(WXK_RETURN); + // Process the resulting text events + wxYield(); + + m_text->AppendText(wxString::Format("\n=== Done in %ldms ===\n", sw.Time())); } void MyFrame::OnSimulateText(wxCommandEvent& WXUNUSED(event)) From a31a7522c4d3ad9b227c6009e3bc365020884ec2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 19 May 2016 23:51:26 +0200 Subject: [PATCH 02/16] Add accelerators for the menu items in wxUIActionSimulator sample No real changes, just make it more convenient to test using this sample. --- samples/uiaction/uiaction.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/samples/uiaction/uiaction.cpp b/samples/uiaction/uiaction.cpp index fd5567c1bd..ad2272d00f 100644 --- a/samples/uiaction/uiaction.cpp +++ b/samples/uiaction/uiaction.cpp @@ -141,9 +141,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(); @@ -174,7 +174,15 @@ MyFrame::MyFrame(const wxString& title) void MyFrame::OnRunSimulation(wxCommandEvent& WXUNUSED(event)) { - m_text->SetValue("=== Starting the simulation ===\n"); + 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; From a4716916b7d53adff5648311f6f400b533b460f8 Mon Sep 17 00:00:00 2001 From: Scott Talbert Date: Tue, 17 May 2016 20:54:22 -0400 Subject: [PATCH 03/16] Add support for using the XTest extension in wxUIActionSimulator Fixes wxUIActionSimulator under wxGTK3, see #17530. --- configure | 124 ++++++++++++++++++++++++ configure.in | 15 +++ docs/changes.txt | 1 + docs/doxygen/mainpages/const_wxusedef.h | 1 + include/wx/android/setup.h | 3 + include/wx/chkconf.h | 8 ++ include/wx/gtk/setup0.h | 3 + include/wx/motif/setup0.h | 3 + include/wx/msw/setup0.h | 3 + include/wx/osx/setup0.h | 3 + include/wx/setup_inc.h | 3 + include/wx/univ/setup0.h | 3 + setup.h.in | 2 + src/unix/uiactionx11.cpp | 22 +++++ 14 files changed, 194 insertions(+) diff --git a/configure b/configure index e2a08b9f66..980a3f4fbe 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 @@ -1092,6 +1094,7 @@ with_gtkprint with_gnomevfs with_libnotify with_opengl +with_xtest with_dmalloc with_sdl with_regex @@ -1397,6 +1400,8 @@ GNOMEVFS_CFLAGS GNOMEVFS_LIBS LIBNOTIFY_CFLAGS LIBNOTIFY_LIBS +XTST_CFLAGS +XTST_LIBS WEBKIT_CFLAGS WEBKIT_LIBS CAIRO_CFLAGS @@ -2318,6 +2323,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 @@ -2391,6 +2397,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 @@ -4823,6 +4831,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 @@ -33532,6 +33569,93 @@ if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then $as_echo "#define wxUSE_UIACTIONSIMULATOR 1" >>confdefs.h SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" + if test "$wxUSE_XTEST" = "yes" ; then + +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 + + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 +$as_echo "$as_me: WARNING: XTest extension not found" >&2;} + wxUSE_XTEST="no" + + +elif test $pkg_failed = untried; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 +$as_echo "$as_me: WARNING: XTest extension not found" >&2;} + 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 + fi fi if test "$wxUSE_DC_TRANSFORM_MATRIX" = "yes" ; then diff --git a/configure.in b/configure.in index 0b974b26e9..e284593efc 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 @@ -6360,6 +6361,20 @@ fi if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then AC_DEFINE(wxUSE_UIACTIONSIMULATOR) SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" + 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) + ], + [ + AC_MSG_WARN([XTest extension not found]) + wxUSE_XTEST="no" + ] + ) + fi fi if test "$wxUSE_DC_TRANSFORM_MATRIX" = "yes" ; then diff --git a/docs/changes.txt b/docs/changes.txt index 68c0b34a1d..9ba1fea212 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -85,6 +85,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/android/setup.h b/include/wx/android/setup.h index 5bf57497a3..5fe489dd07 100644 --- a/include/wx/android/setup.h +++ b/include/wx/android/setup.h @@ -1403,6 +1403,9 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 +// Use the XTest extension for wxUIActionSimulator? +#define wxUSE_XTEST 1 + // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/chkconf.h b/include/wx/chkconf.h index 0e4814216d..73d58fe550 100644 --- a/include/wx/chkconf.h +++ b/include/wx/chkconf.h @@ -1231,6 +1231,14 @@ # endif #endif /* !defined(wxUSE_XRC) */ +#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) */ + #endif /* wxUSE_GUI */ /* diff --git a/include/wx/gtk/setup0.h b/include/wx/gtk/setup0.h index 277c2e4fd8..369bb40902 100644 --- a/include/wx/gtk/setup0.h +++ b/include/wx/gtk/setup0.h @@ -1404,6 +1404,9 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 +// Use the XTest extension for wxUIActionSimulator? +#define wxUSE_XTEST 1 + // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/motif/setup0.h b/include/wx/motif/setup0.h index 75426b39b5..ec3e107806 100644 --- a/include/wx/motif/setup0.h +++ b/include/wx/motif/setup0.h @@ -1404,6 +1404,9 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 +// Use the XTest extension for wxUIActionSimulator? +#define wxUSE_XTEST 1 + // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/msw/setup0.h b/include/wx/msw/setup0.h index 6ccbf11792..445eaa9cfb 100644 --- a/include/wx/msw/setup0.h +++ b/include/wx/msw/setup0.h @@ -1404,6 +1404,9 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 +// Use the XTest extension for wxUIActionSimulator? +#define wxUSE_XTEST 1 + // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/osx/setup0.h b/include/wx/osx/setup0.h index 47046eb9a9..3793f94809 100644 --- a/include/wx/osx/setup0.h +++ b/include/wx/osx/setup0.h @@ -1405,6 +1405,9 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 +// Use the XTest extension for wxUIActionSimulator? +#define wxUSE_XTEST 1 + // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/setup_inc.h b/include/wx/setup_inc.h index 4b404fbed1..6cee4cbed3 100644 --- a/include/wx/setup_inc.h +++ b/include/wx/setup_inc.h @@ -1400,6 +1400,9 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 +// Use the XTest extension for wxUIActionSimulator? +#define wxUSE_XTEST 1 + // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/univ/setup0.h b/include/wx/univ/setup0.h index 10fa48f47e..666d8cae57 100644 --- a/include/wx/univ/setup0.h +++ b/include/wx/univ/setup0.h @@ -1403,6 +1403,9 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 +// Use the XTest extension for wxUIActionSimulator? +#define wxUSE_XTEST 1 + // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/setup.h.in b/setup.h.in index bca48a99f8..fd8bd66ce7 100644 --- a/setup.h.in +++ b/setup.h.in @@ -570,6 +570,8 @@ #define wxUSE_UIACTIONSIMULATOR 0 +#define wxUSE_XTEST 0 + #define wxUSE_POSTSCRIPT 0 diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index a19a3d2284..4121eb4209 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -20,6 +20,9 @@ #include #include +#if wxUSE_XTEST +#include +#endif #include "wx/unix/utilsx11.h" @@ -48,6 +51,10 @@ void SendButtonEvent(int button, bool isDown) wxX11Display display; wxCHECK_RET(display, "No display available!"); +#if wxUSE_XTEST + XTestFakeButtonEvent(display, xbutton, isDown, 0); + +#else // !wxUSE_XTEST XEvent event; memset(&event, 0x00, sizeof(event)); @@ -71,6 +78,7 @@ void SendButtonEvent(int button, bool isDown) } XSendEvent(display, PointerWindow, True, 0xfff, &event); +#endif // !wxUSE_XTEST } } // anonymous namespace @@ -86,8 +94,13 @@ bool wxUIActionSimulator::MouseMove(long x, long y) wxX11Display display; wxASSERT_MSG(display, "No display available!"); +#if wxUSE_XTEST + XTestFakeMotionEvent(display, -1, x, y, 0); + +#else // !wxUSE_XTEST Window root = display.DefaultRoot(); XWarpPointer(display, None, root, 0, 0, 0, 0, x, y); +#endif // !wxUSE_XTEST // 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 @@ -130,6 +143,14 @@ bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) if ( xkeycode == NoSymbol ) return false; +#if wxUSE_XTEST + wxUnusedVar(modifiers); + wxUnusedVar(mask); + wxUnusedVar(type); + XTestFakeKeyEvent(display, xkeycode, isDown, 0); + return true; + +#else // !wxUSE_XTEST Window focus; int revert; XGetInputFocus(display, &focus, &revert); @@ -164,6 +185,7 @@ bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) XSendEvent(event.display, event.window, True, mask, (XEvent*) &event); return true; +#endif // !wxUSE_XTEST } #endif // wxUSE_UIACTIONSIMULATOR From ec4a41f3b76d1a4c3b35709ff632e36e94116d5c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 21 May 2016 18:37:35 +0200 Subject: [PATCH 04/16] Don't test for XTest when not using X11 wxUIActionSimulator There is no need to check (and give a warning about it being not found) when building wxMSW, wxOSX or even wxQt. --- configure | 20 +++++++++++--------- configure.in | 28 +++++++++++++++------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/configure b/configure index 980a3f4fbe..e9ddba85d4 100755 --- a/configure +++ b/configure @@ -33569,7 +33569,8 @@ if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then $as_echo "#define wxUSE_UIACTIONSIMULATOR 1" >>confdefs.h SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" - if test "$wxUSE_XTEST" = "yes" ; then + if test "$wxUSE_GTK" = 1 -o "$wxUSE_MOTIF" = 1 -o "$wxUSE_X11" = 1; then + if test "$wxUSE_XTEST" = "yes" ; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XTST" >&5 @@ -33630,16 +33631,16 @@ fi echo "$XTST_PKG_ERRORS" >&5 - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 $as_echo "$as_me: WARNING: XTest extension not found" >&2;} - wxUSE_XTEST="no" + wxUSE_XTEST="no" elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 $as_echo "$as_me: WARNING: XTest extension not found" >&2;} - wxUSE_XTEST="no" + wxUSE_XTEST="no" else @@ -33648,13 +33649,14 @@ else { $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 + 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 + fi fi fi diff --git a/configure.in b/configure.in index e284593efc..b71b16a182 100644 --- a/configure.in +++ b/configure.in @@ -6361,19 +6361,21 @@ fi if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then AC_DEFINE(wxUSE_UIACTIONSIMULATOR) SAMPLES_SUBDIRS="$SAMPLES_SUBDIRS uiaction" - 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) - ], - [ - AC_MSG_WARN([XTest extension not found]) - wxUSE_XTEST="no" - ] - ) + 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) + ], + [ + AC_MSG_WARN([XTest extension not found]) + wxUSE_XTEST="no" + ] + ) + fi fi fi From 54a6c44a77e8d02640e920480e449a47d1d6c5ad Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 23 May 2016 01:52:56 +0200 Subject: [PATCH 05/16] Disable wxUIActionSimulator if XTest not found when using GTK+ 3 The implementation using XSendEvent() with classic input events can't work with GTK+ 3 anyhow because it uses XInput2 which is incompatible with them, so warn the user about this and don't compile useless code into the library. --- configure | 27 ++++++++++++++++++--------- configure.in | 15 ++++++++++++--- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/configure b/configure index e9ddba85d4..ee4f2cea75 100755 --- a/configure +++ b/configure @@ -33566,9 +33566,6 @@ if test "$wxUSE_MOUSEWHEEL" = "yes" ; then fi if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then - $as_echo "#define wxUSE_UIACTIONSIMULATOR 1" >>confdefs.h - - 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 @@ -33631,16 +33628,22 @@ fi echo "$XTST_PKG_ERRORS" >&5 - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 -$as_echo "$as_me: WARNING: XTest extension not found" >&2;} - wxUSE_XTEST="no" + 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 - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: XTest extension not found" >&5 -$as_echo "$as_me: WARNING: XTest extension not found" >&2;} - wxUSE_XTEST="no" + 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 @@ -33658,6 +33661,12 @@ $as_echo "yes" >&6; } fi 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 b71b16a182..ffa2ab839e 100644 --- a/configure.in +++ b/configure.in @@ -6359,8 +6359,6 @@ 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, @@ -6371,12 +6369,23 @@ if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then AC_DEFINE(wxUSE_XTEST) ], [ - AC_MSG_WARN([XTest extension not found]) + 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" ] ) 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 From e82c6194028474759c785fb6eebdcde00e6621c0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 21 May 2016 18:42:49 +0200 Subject: [PATCH 06/16] Only define wxUSE_XTEST in Unix-specific headers This symbol is not needed and doesn't make sense in wxMSW. --- include/wx/android/setup.h | 3 --- include/wx/chkconf.h | 8 -------- include/wx/gtk/setup0.h | 3 --- include/wx/motif/setup0.h | 3 --- include/wx/msw/setup0.h | 3 --- include/wx/osx/setup0.h | 3 --- include/wx/setup_inc.h | 3 --- include/wx/univ/setup0.h | 3 --- include/wx/unix/chkconf.h | 8 ++++++++ setup.h.in | 12 ++++++++++-- 10 files changed, 18 insertions(+), 31 deletions(-) diff --git a/include/wx/android/setup.h b/include/wx/android/setup.h index 5fe489dd07..5bf57497a3 100644 --- a/include/wx/android/setup.h +++ b/include/wx/android/setup.h @@ -1403,9 +1403,6 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 -// Use the XTest extension for wxUIActionSimulator? -#define wxUSE_XTEST 1 - // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/chkconf.h b/include/wx/chkconf.h index 73d58fe550..0e4814216d 100644 --- a/include/wx/chkconf.h +++ b/include/wx/chkconf.h @@ -1231,14 +1231,6 @@ # endif #endif /* !defined(wxUSE_XRC) */ -#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) */ - #endif /* wxUSE_GUI */ /* diff --git a/include/wx/gtk/setup0.h b/include/wx/gtk/setup0.h index 369bb40902..277c2e4fd8 100644 --- a/include/wx/gtk/setup0.h +++ b/include/wx/gtk/setup0.h @@ -1404,9 +1404,6 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 -// Use the XTest extension for wxUIActionSimulator? -#define wxUSE_XTEST 1 - // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/motif/setup0.h b/include/wx/motif/setup0.h index ec3e107806..75426b39b5 100644 --- a/include/wx/motif/setup0.h +++ b/include/wx/motif/setup0.h @@ -1404,9 +1404,6 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 -// Use the XTest extension for wxUIActionSimulator? -#define wxUSE_XTEST 1 - // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/msw/setup0.h b/include/wx/msw/setup0.h index 445eaa9cfb..6ccbf11792 100644 --- a/include/wx/msw/setup0.h +++ b/include/wx/msw/setup0.h @@ -1404,9 +1404,6 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 -// Use the XTest extension for wxUIActionSimulator? -#define wxUSE_XTEST 1 - // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/osx/setup0.h b/include/wx/osx/setup0.h index 3793f94809..47046eb9a9 100644 --- a/include/wx/osx/setup0.h +++ b/include/wx/osx/setup0.h @@ -1405,9 +1405,6 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 -// Use the XTest extension for wxUIActionSimulator? -#define wxUSE_XTEST 1 - // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/setup_inc.h b/include/wx/setup_inc.h index 6cee4cbed3..4b404fbed1 100644 --- a/include/wx/setup_inc.h +++ b/include/wx/setup_inc.h @@ -1400,9 +1400,6 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 -// Use the XTest extension for wxUIActionSimulator? -#define wxUSE_XTEST 1 - // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- diff --git a/include/wx/univ/setup0.h b/include/wx/univ/setup0.h index 666d8cae57..10fa48f47e 100644 --- a/include/wx/univ/setup0.h +++ b/include/wx/univ/setup0.h @@ -1403,9 +1403,6 @@ // Compile wxUIActionSimulator class? #define wxUSE_UIACTIONSIMULATOR 1 -// Use the XTest extension for wxUIActionSimulator? -#define wxUSE_XTEST 1 - // ---------------------------------------------------------------------------- // wxDC classes for various output formats // ---------------------------------------------------------------------------- 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/setup.h.in b/setup.h.in index fd8bd66ce7..bb8c2f8014 100644 --- a/setup.h.in +++ b/setup.h.in @@ -570,8 +570,6 @@ #define wxUSE_UIACTIONSIMULATOR 0 -#define wxUSE_XTEST 0 - #define wxUSE_POSTSCRIPT 0 @@ -639,6 +637,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 --- */ From cfc1681b4ceb5fbe54d96977990f48e2607602f6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 21 May 2016 18:34:45 +0200 Subject: [PATCH 07/16] Virtualize wxUIActionSimulator implementation Extract platform-specific code in a wxUIActionSimulatorImpl-derived class instead of keeping it in wxUIActionSimulator itself. This will allow determining which implementation to use dynamically (i.e. at run-time and not compile-time) to use later and already allows to get rid of an __WXOSX__ #ifdef in common code. --- include/wx/private/uiaction.h | 44 +++++++++++++++++++++++++ include/wx/uiaction.h | 22 ++++++------- src/common/uiactioncmn.cpp | 61 +++++++++++++++++++++++++++-------- src/msw/uiaction.cpp | 46 +++++++++++++++++++++++--- src/osx/uiaction_osx.cpp | 56 +++++++++++++++++++++++++++----- src/qt/uiaction.cpp | 50 ++++++++++++++++++++++++---- src/unix/uiactionx11.cpp | 51 +++++++++++++++++++++++++---- 7 files changed, 280 insertions(+), 50 deletions(-) create mode 100644 include/wx/private/uiaction.h 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/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 4121eb4209..a0fe1cb2ee 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,6 +17,8 @@ #include "wx/event.h" #include "wx/evtloop.h" +#include "wx/private/uiaction.h" + #include #include #if wxUSE_XTEST @@ -29,6 +30,31 @@ namespace { +class wxUIActionSimulatorX11Impl : 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 wxUIActionSimulatorX11Impl* Get() + { + static wxUIActionSimulatorX11Impl 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. + wxUIActionSimulatorX11Impl() { } + + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorX11Impl); +}; + void SendButtonEvent(int button, bool isDown) { int xbutton; @@ -83,13 +109,13 @@ void SendButtonEvent(int button, bool isDown) } // anonymous namespace -bool wxUIActionSimulator::MouseDown(int button) +bool wxUIActionSimulatorX11Impl::MouseDown(int button) { SendButtonEvent(button, true); return true; } -bool wxUIActionSimulator::MouseMove(long x, long y) +bool wxUIActionSimulatorX11Impl::MouseMove(long x, long y) { wxX11Display display; wxASSERT_MSG(display, "No display available!"); @@ -114,13 +140,13 @@ bool wxUIActionSimulator::MouseMove(long x, long y) return true; } -bool wxUIActionSimulator::MouseUp(int button) +bool wxUIActionSimulatorX11Impl::MouseUp(int button) { SendButtonEvent(button, false); return true; } -bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) +bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) { wxX11Display display; wxCHECK_MSG(display, false, "No display available!"); @@ -188,4 +214,15 @@ bool wxUIActionSimulator::DoKey(int keycode, int modifiers, bool isDown) #endif // !wxUSE_XTEST } +wxUIActionSimulator::wxUIActionSimulator() + : m_impl(wxUIActionSimulatorX11Impl::Get()) +{ +} + +wxUIActionSimulator::~wxUIActionSimulator() +{ + // We can use a static wxUIActionSimulatorX11Impl object because it's + // stateless, so no need to delete it. +} + #endif // wxUSE_UIACTIONSIMULATOR From d1b537f95354397573438e4608e4f0e088c4f6b0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 21 May 2016 19:04:45 +0200 Subject: [PATCH 08/16] Refactor: move SendButtonEvent() into wxUIActionSimulatorX11Impl Make the function a method of the class in preparation for the next commit. --- src/unix/uiactionx11.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index a0fe1cb2ee..75376523d6 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -52,10 +52,13 @@ private: // This class has no public ctors, use Get() instead. wxUIActionSimulatorX11Impl() { } + // Common implementation of Mouse{Down,Up}() + static void SendButtonEvent(int button, bool isDown); + wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorX11Impl); }; -void SendButtonEvent(int button, bool isDown) +void wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) { int xbutton; switch (button) From 746b91c5d36d61727366a04b22a5e8e4a75a4d1e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 21 May 2016 19:11:27 +0200 Subject: [PATCH 09/16] Open the display only once in wxUIActionSimulatorX11Impl Instead of opening connection to X display in every method, do it once in ctor, as we can store the display across multiple calls after the recent refactoring. This does require allocating a separate "impl" object for each public object, but should still be much less wasteful than opening and closing the display connections all the time. --- src/unix/uiactionx11.cpp | 69 +++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index 75376523d6..2498d98c7b 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -33,14 +33,7 @@ namespace class wxUIActionSimulatorX11Impl : 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 wxUIActionSimulatorX11Impl* Get() - { - static wxUIActionSimulatorX11Impl s_impl; - return &s_impl; - } + wxUIActionSimulatorX11Impl() { } virtual bool MouseMove(long x, long y) wxOVERRIDE; virtual bool MouseDown(int button = wxMOUSE_BTN_LEFT) wxOVERRIDE; @@ -49,17 +42,19 @@ public: virtual bool DoKey(int keycode, int modifiers, bool isDown) wxOVERRIDE; private: - // This class has no public ctors, use Get() instead. - wxUIActionSimulatorX11Impl() { } - // Common implementation of Mouse{Down,Up}() - static void SendButtonEvent(int button, bool isDown); + bool SendButtonEvent(int button, bool isDown); + + wxX11Display m_display; wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorX11Impl); }; -void wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) +bool wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) { + if ( !m_display ) + return false; + int xbutton; switch (button) { @@ -74,14 +69,11 @@ void wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) break; default: wxFAIL_MSG("Unsupported button passed in."); - return; + return false; } - wxX11Display display; - wxCHECK_RET(display, "No display available!"); - #if wxUSE_XTEST - XTestFakeButtonEvent(display, xbutton, isDown, 0); + XTestFakeButtonEvent(m_display, xbutton, isDown, 0); #else // !wxUSE_XTEST XEvent event; @@ -91,7 +83,7 @@ void wxUIActionSimulatorX11Impl::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); @@ -100,35 +92,36 @@ void wxUIActionSimulatorX11Impl::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); + XSendEvent(m_display, PointerWindow, True, 0xfff, &event); #endif // !wxUSE_XTEST + + return true; } } // anonymous namespace bool wxUIActionSimulatorX11Impl::MouseDown(int button) { - SendButtonEvent(button, true); - return true; + return SendButtonEvent(button, true); } bool wxUIActionSimulatorX11Impl::MouseMove(long x, long y) { - wxX11Display display; - wxASSERT_MSG(display, "No display available!"); + if ( !m_display ) + return false; #if wxUSE_XTEST - XTestFakeMotionEvent(display, -1, x, y, 0); + XTestFakeMotionEvent(m_display, -1, x, y, 0); #else // !wxUSE_XTEST - Window root = display.DefaultRoot(); - XWarpPointer(display, None, root, 0, 0, 0, 0, x, y); + Window root = m_display.DefaultRoot(); + XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); #endif // !wxUSE_XTEST // At least with wxGTK we must always process the pending events before the @@ -145,14 +138,13 @@ bool wxUIActionSimulatorX11Impl::MouseMove(long x, long y) bool wxUIActionSimulatorX11Impl::MouseUp(int button) { - SendButtonEvent(button, false); - return true; + return SendButtonEvent(button, false); } bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) { - wxX11Display display; - wxCHECK_MSG(display, false, "No display available!"); + if ( !m_display ) + return false; int mask, type; @@ -168,7 +160,7 @@ bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) } WXKeySym xkeysym = wxCharCodeWXToX(keycode); - KeyCode xkeycode = XKeysymToKeycode(display, xkeysym); + KeyCode xkeycode = XKeysymToKeycode(m_display, xkeysym); if ( xkeycode == NoSymbol ) return false; @@ -176,13 +168,13 @@ bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) wxUnusedVar(modifiers); wxUnusedVar(mask); wxUnusedVar(type); - XTestFakeKeyEvent(display, xkeycode, isDown, 0); + XTestFakeKeyEvent(m_display, xkeycode, isDown, 0); return true; #else // !wxUSE_XTEST Window focus; int revert; - XGetInputFocus(display, &focus, &revert); + XGetInputFocus(m_display, &focus, &revert); if (focus == None) return false; @@ -197,7 +189,7 @@ bool wxUIActionSimulatorX11Impl::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; @@ -218,14 +210,13 @@ bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) } wxUIActionSimulator::wxUIActionSimulator() - : m_impl(wxUIActionSimulatorX11Impl::Get()) + : m_impl(new wxUIActionSimulatorX11Impl()) { } wxUIActionSimulator::~wxUIActionSimulator() { - // We can use a static wxUIActionSimulatorX11Impl object because it's - // stateless, so no need to delete it. + delete m_impl; } #endif // wxUSE_UIACTIONSIMULATOR From 4a0938d2b7e532d9118677a512e2d154f7ee9b74 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 22 May 2016 00:21:22 +0200 Subject: [PATCH 10/16] Allow "moving" wxX11Display objects This is not a real move-ctor but std::auto_ptr<>-like "stealing" ctor. It still allows to pass Display ownership to another function which is all that is needed for our purposes. --- include/wx/unix/utilsx11.h | 7 +++++++ 1 file changed, 7 insertions(+) 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 From 7cf5804465529903507a08e2c2f28cb2bca104c1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 22 May 2016 00:22:08 +0200 Subject: [PATCH 11/16] Determine wxUIActionSimulatorX11Impl version to use dynamically Choose between wxUIActionSimulatorXTestImpl and wxUIActionSimulatorPlainX11Impl dynamically, depending on whether XTest extension is available during run-time or not. Also decouple the two implementation to keep them clearly separated. --- src/unix/uiactionx11.cpp | 203 ++++++++++++++++++++++++++++----------- 1 file changed, 145 insertions(+), 58 deletions(-) diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index 2498d98c7b..e8b47f7579 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -30,10 +30,15 @@ namespace { +// Base class for both available X11 implementations. class wxUIActionSimulatorX11Impl : public wxUIActionSimulatorImpl { public: - wxUIActionSimulatorX11Impl() { } + // 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; @@ -41,15 +46,44 @@ public: virtual bool DoKey(int keycode, int modifiers, bool isDown) wxOVERRIDE; -private: - // Common implementation of Mouse{Down,Up}() - bool SendButtonEvent(int button, bool isDown); +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); }; +// 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 wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) { if ( !m_display ) @@ -72,10 +106,11 @@ bool wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) return false; } -#if wxUSE_XTEST - XTestFakeButtonEvent(m_display, xbutton, isDown, 0); + return DoX11Button(xbutton, isDown); +} -#else // !wxUSE_XTEST +bool wxUIActionSimulatorPlainX11Impl::DoX11Button(int xbutton, bool isDown) +{ XEvent event; memset(&event, 0x00, sizeof(event)); @@ -99,53 +134,22 @@ bool wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) } XSendEvent(m_display, PointerWindow, True, 0xfff, &event); -#endif // !wxUSE_XTEST return true; } -} // anonymous namespace - -bool wxUIActionSimulatorX11Impl::MouseDown(int button) +bool wxUIActionSimulatorPlainX11Impl::DoX11MouseMove(long x, long y) { - return SendButtonEvent(button, true); -} - -bool wxUIActionSimulatorX11Impl::MouseMove(long x, long y) -{ - if ( !m_display ) - return false; - -#if wxUSE_XTEST - XTestFakeMotionEvent(m_display, -1, x, y, 0); - -#else // !wxUSE_XTEST Window root = m_display.DefaultRoot(); XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); -#endif // !wxUSE_XTEST - - // 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) +bool +wxUIActionSimulatorPlainX11Impl::DoX11Key(KeyCode xkeycode, + int modifiers, + bool isDown) { - return SendButtonEvent(button, false); -} - -bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) -{ - if ( !m_display ) - return false; - int mask, type; if ( isDown ) @@ -159,19 +163,6 @@ bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) mask = KeyReleaseMask; } - WXKeySym xkeysym = wxCharCodeWXToX(keycode); - KeyCode xkeycode = XKeysymToKeycode(m_display, xkeysym); - if ( xkeycode == NoSymbol ) - return false; - -#if wxUSE_XTEST - wxUnusedVar(modifiers); - wxUnusedVar(mask); - wxUnusedVar(type); - XTestFakeKeyEvent(m_display, xkeycode, isDown, 0); - return true; - -#else // !wxUSE_XTEST Window focus; int revert; XGetInputFocus(m_display, &focus, &revert); @@ -206,11 +197,107 @@ bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) XSendEvent(event.display, event.window, True, mask, (XEvent*) &event); return true; -#endif // !wxUSE_XTEST +} + +#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) +{ + 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 + int dummy; + if ( XTestQueryExtension(display, &dummy, &dummy, &dummy, &dummy) ) + return new wxUIActionSimulatorXTestImpl(display); +#endif // wxUSE_XTEST + + return new wxUIActionSimulatorPlainX11Impl(display); +} + +} // 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(new wxUIActionSimulatorX11Impl()) + : m_impl(wxUIActionSimulatorX11Impl::New()) { } From e2c98afd5a9d475f04048a94afad321c505d8729 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 23 May 2016 01:56:08 +0200 Subject: [PATCH 12/16] Sync with X server before simulating mouse clicks Adding a call to XSync() to wxUIActionSimulatorX11Impl seems to fix the problems with the focus snapping back to the control having it previously after simulating a mouse click and changing the focus afterwards using wxWindow::SetFocus(), as the uiaction sample did. Admittedly, it's not really clear why does doing it help, but at the very leasy this XSync() call shouldn't do any harm. --- src/unix/uiactionx11.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index e8b47f7579..50c525b333 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -106,6 +106,10 @@ bool wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) return false; } + // 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); } From 38c8a10b79b0606f365ab4534ac6e071c64ff731 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 23 May 2016 02:10:57 +0200 Subject: [PATCH 13/16] Don't even compile in wxUIActionSimulatorX11Impl when using GTK+3 This code can never work anyhow, there is no reason to compile and link it. --- src/unix/uiactionx11.cpp | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index 50c525b333..56af15ebef 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -27,6 +27,12 @@ #include "wx/unix/utilsx11.h" +// 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 { @@ -67,23 +73,6 @@ private: wxDECLARE_NO_COPY_CLASS(wxUIActionSimulatorX11Impl); }; -// 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 wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) { if ( !m_display ) @@ -113,6 +102,25 @@ bool wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) 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; @@ -203,6 +211,8 @@ wxUIActionSimulatorPlainX11Impl::DoX11Key(KeyCode xkeycode, return true; } +#endif // wxUSE_PLAINX_IMPL + #if wxUSE_XTEST // Implementation using XTest extension. @@ -247,12 +257,19 @@ 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 From 594d916dcbe7301a1cc89554fe1e1a1ec38ae1c2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 23 May 2016 02:36:28 +0200 Subject: [PATCH 14/16] Support DPI scaling in wxUIActionSimulator when using GTK+ 3 Scale the coordinates passed to MouseMove() by the GTK scaling factor to ensure that the mouse clicks are really delivered to the correct windows, without this change wxUIActionSimulator was unusable on systems using high DPI (i.e. GDK_SCALE of 2). Per-monitor DPI scaling is still not supported however. --- src/unix/uiactionx11.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index 56af15ebef..4bf33c93b0 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -27,6 +27,11 @@ #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 @@ -239,6 +244,27 @@ bool wxUIActionSimulatorXTestImpl::DoX11Button(int xbutton, bool isDown) 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; } From 8e76aab5441f1c12c963b414d03b0bf921e66983 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 23 May 2016 02:44:00 +0200 Subject: [PATCH 15/16] Demonstrate simulating menu item selection in the uiaction sample This just checks that pressing a key with "Alt" modifier can be used to open a menu and that pressing the menu item letter then selects an item from it. --- samples/uiaction/uiaction.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/samples/uiaction/uiaction.cpp b/samples/uiaction/uiaction.cpp index ad2272d00f..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 ///////////////////////////////////////////////////////////////////////////// @@ -78,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(); } @@ -91,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) @@ -172,6 +174,11 @@ 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 " @@ -210,6 +217,11 @@ void MyFrame::OnRunSimulation(wxCommandEvent& WXUNUSED(event)) // 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())); } From b5dbc0eb739a3598cd903153f8b30820312848b2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 4 Jun 2016 22:43:21 +0200 Subject: [PATCH 16/16] Disable wxUIActionSimulator in wxGTK3 if --without-xtest was given This class can't work without XTest support with GTK+ 3, so just silently disable it if XTest was explicitly disabled by user. --- configure | 2 ++ configure.in | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/configure b/configure index ee4f2cea75..86bff4a4b5 100755 --- a/configure +++ b/configure @@ -33659,6 +33659,8 @@ $as_echo "yes" >&6; } fi + elif test "$WXGTK3" = 1; then + wxUSE_UIACTIONSIMULATOR=no fi fi diff --git a/configure.in b/configure.in index ffa2ab839e..d543ca98d6 100644 --- a/configure.in +++ b/configure.in @@ -6379,6 +6379,11 @@ if test "$wxUSE_UIACTIONSIMULATOR" = "yes" ; then 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