diff options
Diffstat (limited to 'vcl/source/window/debugevent.cxx')
-rw-r--r-- | vcl/source/window/debugevent.cxx | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/vcl/source/window/debugevent.cxx b/vcl/source/window/debugevent.cxx new file mode 100644 index 000000000..f9f6978f0 --- /dev/null +++ b/vcl/source/window/debugevent.cxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <comphelper/random.hxx> +#include <o3tl/string_view.hxx> +#include <rtl/string.hxx> +#include <sal/log.hxx> +#include <vcl/keycodes.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/menu.hxx> +#include <debugevent.hxx> +#include <window.h> +#include <salwtype.hxx> + +DebugEventInjector::DebugEventInjector( sal_uInt32 nMaxEvents) : + Timer("debug event injector") + , mnEventsLeft( nMaxEvents ) +{ + SetTimeout( 1000 /* ms */ ); + Start(); +} + +static double getRandom() +{ + return comphelper::rng::uniform_real_distribution(); +} + +vcl::Window *DebugEventInjector::ChooseWindow() +{ + vcl::Window *pParent; + + if (getRandom() < 0.80) + if (vcl::Window * pWindow = Application::GetFocusWindow()) + return pWindow; + + if (getRandom() > 0.50 || + !(pParent = Application::GetActiveTopWindow())) + { + // select a top window at random + tools::Long nIdx = Application::GetTopWindowCount() * getRandom(); + pParent = Application::GetTopWindow( nIdx ); + } + assert (pParent != nullptr); + + std::vector< vcl::Window *> aChildren; + pParent->CollectChildren( aChildren ); + + return aChildren[ aChildren.size() * getRandom() ]; +} + + +static void CollectMenuItemIds( Menu *pMenu, std::vector< SalMenuEvent > &rIds ) +{ + sal_uInt16 nItems = pMenu->GetItemCount(); + for (sal_uInt16 i = 0; i < nItems; i++) + { + if (pMenu->GetItemType( i ) != MenuItemType::SEPARATOR || getRandom() < 0.01) + rIds.emplace_back( pMenu->GetItemId( i ), pMenu ); + PopupMenu *pPopup = pMenu->GetPopupMenu( i ); + if (pPopup) + CollectMenuItemIds( pPopup, rIds ); + } +} + +void DebugEventInjector::InjectMenuEvent() +{ + vcl::Window *pFocus = Application::GetFocusWindow(); + if (!pFocus) + return; + + SystemWindow *pSysWin = pFocus->GetSystemWindow(); + if (!pSysWin) + return; + + MenuBar *pMenuBar = pSysWin->GetMenuBar(); + if (!pMenuBar) + return; + + SalEvent nEvents[] = { + SalEvent::MenuCommand, + SalEvent::MenuCommand, + SalEvent::MenuActivate, + SalEvent::MenuDeactivate, + SalEvent::MenuHighlight, + SalEvent::MenuCommand, + SalEvent::MenuCommand, + SalEvent::MenuCommand, + SalEvent::MenuButtonCommand, + SalEvent::MenuButtonCommand, + }; + + std::vector< SalMenuEvent > aIds; + CollectMenuItemIds( pMenuBar, aIds ); + + SalEvent nEvent = nEvents[ static_cast<int>(getRandom() * SAL_N_ELEMENTS( nEvents )) ]; + SalMenuEvent aEvent = aIds[ getRandom() * aIds.size() ]; + bool bHandled = ImplWindowFrameProc( pSysWin, nEvent, &aEvent); + + SAL_INFO( "vcl.debugevent", + "Injected menu event " << aEvent.mpMenu + << " (" << aEvent.mnId << ") '" + << static_cast<Menu *>(aEvent.mpMenu)->GetItemText( aEvent.mnId ) << "' -> " + << bHandled ); +} + +static void InitKeyEvent( SalKeyEvent &rKeyEvent ) +{ + if (getRandom() < 0.01) + rKeyEvent.mnRepeat = getRandom() * 20; + else + rKeyEvent.mnRepeat = 0; +} + +void DebugEventInjector::InjectTextEvent() +{ + SalKeyEvent aKeyEvent; + vcl::Window *pWindow = ChooseWindow(); + + InitKeyEvent( aKeyEvent ); + + if (getRandom() < 0.10) // Occasionally a truly random event + { + aKeyEvent.mnCode = getRandom() * KEY_CODE_MASK; + aKeyEvent.mnCharCode = getRandom() * 0xffff; + } + else + { + static struct { + sal_uInt16 nCodeStart, nCodeEnd; + char aCharStart; + } const nTextCodes[] = { + { KEY_0, KEY_9, '0' }, + { KEY_A, KEY_Z, 'a' } + }; + + size_t i = getRandom() * SAL_N_ELEMENTS( nTextCodes ); + int offset = int( getRandom() * ( nTextCodes[i].nCodeEnd - nTextCodes[i].nCodeStart ) ); + aKeyEvent.mnCode = nTextCodes[i].nCodeStart + offset; + aKeyEvent.mnCharCode = nTextCodes[i].aCharStart + offset; +// fprintf( stderr, "Char '%c' offset %d into record %d base '%c'\n", +// aKeyEvent.mnCharCode, offset, (int)i, nTextCodes[i].aCharStart ); + } + + if( getRandom() < 0.05 ) // modifier + aKeyEvent.mnCode |= static_cast<sal_uInt16>( getRandom() * KEY_MODIFIERS_MASK ) & KEY_MODIFIERS_MASK; + + bool bHandled = ImplWindowFrameProc( pWindow, SalEvent::KeyInput, &aKeyEvent); + + SAL_INFO( "vcl.debugevent", + "Injected key 0x" << std::hex << static_cast<int>(aKeyEvent.mnCode) << std::dec + << " -> " << bHandled + << " win " << pWindow ); + + ImplWindowFrameProc( pWindow, SalEvent::KeyUp, &aKeyEvent ); +} + +/* + * The more heuristics we have to inform this the better, + * key-bindings, menu entries, allowable entry types etc. + */ +void DebugEventInjector::InjectEvent() +{ +// fprintf( stderr, "%6d - ", (int)mnEventsLeft ); + + double nRand = getRandom(); + if (nRand < 0.30) + { + int nEvents = getRandom() * 10; + for (int i = 0; i < nEvents; i++) + InjectTextEvent(); + } + else if (nRand < 0.60) + InjectKeyNavEdit(); + else if (nRand < 0.95) + InjectMenuEvent(); +} + +void DebugEventInjector::InjectKeyNavEdit() +{ + vcl::Window *pWindow = ChooseWindow(); + + static struct { + double mnProb; + sal_uInt16 mnKey; + } const nWeights[] = { + // edit / escape etc. - 50% + { 0.20, KEY_SPACE }, + { 0.10, KEY_TAB }, + { 0.07, KEY_RETURN }, + { 0.05, KEY_DELETE }, + { 0.05, KEY_BACKSPACE }, + + // navigate - 45% + { 0.15, KEY_LEFT }, + { 0.10, KEY_RIGHT }, + { 0.05, KEY_UP }, + { 0.05, KEY_DOWN }, + { 0.05, KEY_PAGEUP }, + { 0.05, KEY_PAGEDOWN }, + + // other + { 0.01, KEY_INSERT }, + { 0.02, KEY_HOME }, + { 0.02, KEY_END }, + }; + + double d = 0.0, nRand = getRandom(); + sal_uInt16 nKey = KEY_SPACE; + for (auto & rWeight : nWeights) + { + d += rWeight.mnProb; + assert (d < 1.01); + if ( nRand < d ) + { + nKey = rWeight.mnKey; + break; + } + } + + SalKeyEvent aKeyEvent; + InitKeyEvent( aKeyEvent ); + aKeyEvent.mnCode = nKey; + + if (getRandom() < 0.15) // modifier + aKeyEvent.mnCode |= static_cast<sal_uInt16>(getRandom() * KEY_MODIFIERS_MASK) & KEY_MODIFIERS_MASK; + + aKeyEvent.mnCharCode = 0x0; // hopefully unused. + + bool bHandled = ImplWindowFrameProc( pWindow, SalEvent::KeyInput, &aKeyEvent ); + + SAL_INFO( "vcl.debugevent", + "Injected edit / move key 0x" << std::hex << static_cast<int>(aKeyEvent.mnCode) << std::dec + << " -> " << bHandled + << " win " << pWindow ); + ImplWindowFrameProc( pWindow, SalEvent::KeyUp, &aKeyEvent ); +} + +void DebugEventInjector::Invoke() +{ + InjectEvent(); + mnEventsLeft--; + if (mnEventsLeft > 0) + { + SetTimeout( 1 ); + Start(); + } + else + Application::Quit(); +} + +DebugEventInjector *DebugEventInjector::getCreate() +{ + sal_uInt32 nEvents; + const char *pEvents = getenv("VCL_EVENT_INJECTION"); + if (!pEvents) + return nullptr; + nEvents = o3tl::toUInt32( pEvents ); + if (nEvents > 0) + return new DebugEventInjector( nEvents ); + else + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |