1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <comphelper/random.hxx>
11 #include <rtl/string.hxx>
12 #include <sal/log.hxx>
13 #include <vcl/keycodes.hxx>
14 #include <vcl/svapp.hxx>
15 #include <vcl/wrkwin.hxx>
16 #include <vcl/menu.hxx>
17 #include <debugevent.hxx>
19 #include <salwtype.hxx>
21 DebugEventInjector::DebugEventInjector( sal_uInt32 nMaxEvents
) :
22 Timer("debug event injector")
23 , mnEventsLeft( nMaxEvents
)
25 SetTimeout( 1000 /* ms */ );
29 static double getRandom()
31 return comphelper::rng::uniform_real_distribution();
34 vcl::Window
*DebugEventInjector::ChooseWindow()
38 if (getRandom() < 0.80)
39 if (vcl::Window
* pWindow
= Application::GetFocusWindow())
42 if (getRandom() > 0.50 ||
43 !(pParent
= Application::GetActiveTopWindow()))
45 // select a top window at random
46 tools::Long nIdx
= Application::GetTopWindowCount() * getRandom();
47 pParent
= Application::GetTopWindow( nIdx
);
49 assert (pParent
!= nullptr);
51 std::vector
< vcl::Window
*> aChildren
;
52 pParent
->CollectChildren( aChildren
);
54 return aChildren
[ aChildren
.size() * getRandom() ];
58 static void CollectMenuItemIds( Menu
*pMenu
, std::vector
< SalMenuEvent
> &rIds
)
60 sal_uInt16 nItems
= pMenu
->GetItemCount();
61 for (sal_uInt16 i
= 0; i
< nItems
; i
++)
63 if (pMenu
->GetItemType( i
) != MenuItemType::SEPARATOR
|| getRandom() < 0.01)
64 rIds
.emplace_back( pMenu
->GetItemId( i
), pMenu
);
65 PopupMenu
*pPopup
= pMenu
->GetPopupMenu( i
);
67 CollectMenuItemIds( pPopup
, rIds
);
71 void DebugEventInjector::InjectMenuEvent()
73 vcl::Window
*pFocus
= Application::GetFocusWindow();
77 SystemWindow
*pSysWin
= pFocus
->GetSystemWindow();
81 MenuBar
*pMenuBar
= pSysWin
->GetMenuBar();
85 SalEvent nEvents
[] = {
86 SalEvent::MenuCommand
,
87 SalEvent::MenuCommand
,
88 SalEvent::MenuActivate
,
89 SalEvent::MenuDeactivate
,
90 SalEvent::MenuHighlight
,
91 SalEvent::MenuCommand
,
92 SalEvent::MenuCommand
,
93 SalEvent::MenuCommand
,
94 SalEvent::MenuButtonCommand
,
95 SalEvent::MenuButtonCommand
,
98 std::vector
< SalMenuEvent
> aIds
;
99 CollectMenuItemIds( pMenuBar
, aIds
);
101 SalEvent nEvent
= nEvents
[ static_cast<int>(getRandom() * SAL_N_ELEMENTS( nEvents
)) ];
102 SalMenuEvent aEvent
= aIds
[ getRandom() * aIds
.size() ];
103 bool bHandled
= ImplWindowFrameProc( pSysWin
, nEvent
, &aEvent
);
105 SAL_INFO( "vcl.debugevent",
106 "Injected menu event " << aEvent
.mpMenu
107 << " (" << aEvent
.mnId
<< ") '"
108 << static_cast<Menu
*>(aEvent
.mpMenu
)->GetItemText( aEvent
.mnId
) << "' -> "
112 static void InitKeyEvent( SalKeyEvent
&rKeyEvent
)
114 if (getRandom() < 0.01)
115 rKeyEvent
.mnRepeat
= getRandom() * 20;
117 rKeyEvent
.mnRepeat
= 0;
120 void DebugEventInjector::InjectTextEvent()
122 SalKeyEvent aKeyEvent
;
123 vcl::Window
*pWindow
= ChooseWindow();
125 InitKeyEvent( aKeyEvent
);
127 if (getRandom() < 0.10) // Occasionally a truly random event
129 aKeyEvent
.mnCode
= getRandom() * KEY_CODE_MASK
;
130 aKeyEvent
.mnCharCode
= getRandom() * 0xffff;
135 sal_uInt16 nCodeStart
, nCodeEnd
;
137 } const nTextCodes
[] = {
138 { KEY_0
, KEY_9
, '0' },
139 { KEY_A
, KEY_Z
, 'a' }
142 size_t i
= getRandom() * SAL_N_ELEMENTS( nTextCodes
);
143 int offset
= int( getRandom() * ( nTextCodes
[i
].nCodeEnd
- nTextCodes
[i
].nCodeStart
) );
144 aKeyEvent
.mnCode
= nTextCodes
[i
].nCodeStart
+ offset
;
145 aKeyEvent
.mnCharCode
= nTextCodes
[i
].aCharStart
+ offset
;
146 // fprintf( stderr, "Char '%c' offset %d into record %d base '%c'\n",
147 // aKeyEvent.mnCharCode, offset, (int)i, nTextCodes[i].aCharStart );
150 if( getRandom() < 0.05 ) // modifier
151 aKeyEvent
.mnCode
|= static_cast<sal_uInt16
>( getRandom() * KEY_MODIFIERS_MASK
) & KEY_MODIFIERS_MASK
;
153 bool bHandled
= ImplWindowFrameProc( pWindow
, SalEvent::KeyInput
, &aKeyEvent
);
155 SAL_INFO( "vcl.debugevent",
156 "Injected key 0x" << std::hex
<< static_cast<int>(aKeyEvent
.mnCode
) << std::dec
157 << " -> " << bHandled
158 << " win " << pWindow
);
160 ImplWindowFrameProc( pWindow
, SalEvent::KeyUp
, &aKeyEvent
);
164 * The more heuristics we have to inform this the better,
165 * key-bindings, menu entries, allowable entry types etc.
167 void DebugEventInjector::InjectEvent()
169 // fprintf( stderr, "%6d - ", (int)mnEventsLeft );
171 double nRand
= getRandom();
174 int nEvents
= getRandom() * 10;
175 for (int i
= 0; i
< nEvents
; i
++)
178 else if (nRand
< 0.60)
180 else if (nRand
< 0.95)
184 void DebugEventInjector::InjectKeyNavEdit()
186 vcl::Window
*pWindow
= ChooseWindow();
191 } const nWeights
[] = {
192 // edit / escape etc. - 50%
195 { 0.07, KEY_RETURN
},
196 { 0.05, KEY_DELETE
},
197 { 0.05, KEY_BACKSPACE
},
204 { 0.05, KEY_PAGEUP
},
205 { 0.05, KEY_PAGEDOWN
},
208 { 0.01, KEY_INSERT
},
213 double d
= 0.0, nRand
= getRandom();
214 sal_uInt16 nKey
= KEY_SPACE
;
215 for (auto & rWeight
: nWeights
)
221 nKey
= rWeight
.mnKey
;
226 SalKeyEvent aKeyEvent
;
227 InitKeyEvent( aKeyEvent
);
228 aKeyEvent
.mnCode
= nKey
;
230 if (getRandom() < 0.15) // modifier
231 aKeyEvent
.mnCode
|= static_cast<sal_uInt16
>(getRandom() * KEY_MODIFIERS_MASK
) & KEY_MODIFIERS_MASK
;
233 aKeyEvent
.mnCharCode
= 0x0; // hopefully unused.
235 bool bHandled
= ImplWindowFrameProc( pWindow
, SalEvent::KeyInput
, &aKeyEvent
);
237 SAL_INFO( "vcl.debugevent",
238 "Injected edit / move key 0x" << std::hex
<< static_cast<int>(aKeyEvent
.mnCode
) << std::dec
239 << " -> " << bHandled
240 << " win " << pWindow
);
241 ImplWindowFrameProc( pWindow
, SalEvent::KeyUp
, &aKeyEvent
);
244 void DebugEventInjector::Invoke()
248 if (mnEventsLeft
> 0)
257 DebugEventInjector
*DebugEventInjector::getCreate()
260 const char *pEvents
= getenv("VCL_EVENT_INJECTION");
263 nEvents
= OString( pEvents
).toUInt32();
265 return new DebugEventInjector( nEvents
);
270 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */