Merge lp:~canonical-dx-team/unity/unity.fix-676040 into lp:unity

Proposed by Mirco Müller
Status: Merged
Approved by: Gord Allott
Approved revision: no longer in the source branch.
Merged at revision: 680
Proposed branch: lp:~canonical-dx-team/unity/unity.fix-676040
Merge into: lp:unity
Diff against target: 734 lines (+552/-63) (has conflicts)
6 files modified
src/QuicklistView.cpp (+89/-62)
src/QuicklistView.h (+11/-1)
tests/CMakeLists.txt (+18/-0)
tests/ui/EventFaker.cpp (+89/-0)
tests/ui/EventFaker.h (+40/-0)
tests/ui/TestQuicklist.cpp (+305/-0)
Text conflict in src/QuicklistView.h
To merge this branch: bzr merge lp:~canonical-dx-team/unity/unity.fix-676040
Reviewer Review Type Date Requested Status
Gord Allott (community) Approve
Review via email: mp+42623@code.launchpad.net

Description of the change

This is the second part of "Quicklist menu item testing". See LP: #676040.

This creates a stand-alone test-application for interaction-tests with Quicklist-items (checkmark, radio, label). It does so by using a separate thread injecting "fake" events (not generated by a user, mouse-clicks in this case) into the event-loop of the main app-context.

The test-application stores three boolean-flags set to false, which get switched to true, if the "item-activated" signal-handlers for the dbusmenu-items underlying the Quicklist-items get correctly called. These are tested at program-exit.

It's currently missing a nicer API for EventFaker, wrapping (and hiding) all the setup needed for the separate event-injecting thread, but I wanted to avoid this being held of longer. The nicer API is in the works and probably lands in this branch before someone actually reviews it :) The program-exit right now has to be triggered by the user. That will be automated as well, since this is meant to be an unattended test.

Note: for this branch to work lp:~canonical-dx-team/nux/nux.event-automation is needed (and should be merged to nux trunk before).

To post a comment you must log in.
Revision history for this message
Gord Allott (gordallott) wrote :

Okay so this test works and the code seems fine approving, :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/QuicklistView.cpp'
2--- src/QuicklistView.cpp 2010-12-02 16:50:33 +0000
3+++ src/QuicklistView.cpp 2010-12-03 13:43:15 +0000
4@@ -92,6 +92,7 @@
5 OnMouseDrag.connect (sigc::mem_fun (this, &QuicklistView::RecvMouseDrag));
6
7 _mouse_down = false;
8+ _enable_quicklist_for_testing = false;
9 }
10
11 QuicklistView::~QuicklistView ()
12@@ -127,6 +128,12 @@
13 _item_list.clear ();
14 }
15
16+void
17+QuicklistView::EnableQuicklistForTesting (bool enable_testing)
18+{
19+ _enable_quicklist_for_testing = enable_testing;
20+}
21+
22 void QuicklistView::ShowQuicklistWithTipAt (int anchor_tip_x, int anchor_tip_y)
23 {
24 int window_width;
25@@ -193,6 +200,26 @@
26 _mouse_down = false;
27 if (IsVisible ())
28 {
29+ if (!_enable_quicklist_for_testing)
30+ {
31+ CancelItemsPrelightStatus ();
32+ CaptureMouseDownAnyWhereElse (false);
33+ ForceStopFocus (1, 1);
34+ UnGrabPointer ();
35+ EnableInputWindow (false);
36+ ShowWindow (false);
37+ }
38+ }
39+ return nux::eMouseEventSolved;
40+ }
41+ }
42+ else if ((ievent.e_event == nux::NUX_MOUSE_RELEASED) && _mouse_down)
43+ {
44+ _mouse_down = false;
45+ if (IsVisible ())
46+ {
47+ if (!_enable_quicklist_for_testing)
48+ {
49 CancelItemsPrelightStatus ();
50 CaptureMouseDownAnyWhereElse (false);
51 ForceStopFocus (1, 1);
52@@ -200,25 +227,10 @@
53 EnableInputWindow (false);
54 ShowWindow (false);
55 }
56- return nux::eMouseEventSolved;
57- }
58- }
59- else if ((ievent.e_event == nux::NUX_MOUSE_RELEASED) && _mouse_down)
60- {
61- _mouse_down = false;
62- if (IsVisible ())
63- {
64- CancelItemsPrelightStatus ();
65- CaptureMouseDownAnyWhereElse (false);
66- ForceStopFocus (1, 1);
67- UnGrabPointer ();
68- EnableInputWindow (false);
69- ShowWindow (false);
70 }
71 return nux::eMouseEventSolved;
72 }
73-
74-
75+
76 return ret;
77 }
78
79@@ -406,12 +418,15 @@
80 // Check if the mouse was released over an item and emit the signal
81 CheckAndEmitItemSignal (x + item->GetBaseX (), y + item->GetBaseY ());
82
83- CancelItemsPrelightStatus ();
84- CaptureMouseDownAnyWhereElse (false);
85- ForceStopFocus (1, 1);
86- UnGrabPointer ();
87- EnableInputWindow (false);
88- ShowWindow (false);
89+ if (!_enable_quicklist_for_testing)
90+ {
91+ CancelItemsPrelightStatus ();
92+ CaptureMouseDownAnyWhereElse (false);
93+ ForceStopFocus (1, 1);
94+ UnGrabPointer ();
95+ EnableInputWindow (false);
96+ ShowWindow (false);
97+ }
98 }
99 }
100
101@@ -459,14 +474,17 @@
102 {
103 // Check if the mouse was released over an item and emit the signal
104 CheckAndEmitItemSignal (x + item->GetBaseX (), y + item->GetBaseY ());
105-
106- CancelItemsPrelightStatus ();
107- CaptureMouseDownAnyWhereElse (false);
108- ForceStopFocus (1, 1);
109- UnGrabPointer ();
110- EnableInputWindow (false);
111- ShowWindow (false);
112- }
113+
114+ if (!_enable_quicklist_for_testing)
115+ {
116+ CancelItemsPrelightStatus ();
117+ CaptureMouseDownAnyWhereElse (false);
118+ ForceStopFocus (1, 1);
119+ UnGrabPointer ();
120+ EnableInputWindow (false);
121+ ShowWindow (false);
122+ }
123+ }
124 }
125
126 void QuicklistView::CancelItemsPrelightStatus ()
127@@ -552,12 +570,15 @@
128 {
129 if (IsVisible ())
130 {
131- CancelItemsPrelightStatus ();
132- CaptureMouseDownAnyWhereElse (false);
133- ForceStopFocus (1, 1);
134- UnGrabPointer ();
135- EnableInputWindow (false);
136- ShowWindow (false);
137+ if (!_enable_quicklist_for_testing)
138+ {
139+ CancelItemsPrelightStatus ();
140+ CaptureMouseDownAnyWhereElse (false);
141+ ForceStopFocus (1, 1);
142+ UnGrabPointer ();
143+ EnableInputWindow (false);
144+ ShowWindow (false);
145+ }
146 }
147 }
148
149@@ -575,12 +596,15 @@
150 {
151 if (IsVisible ())
152 {
153- CancelItemsPrelightStatus ();
154- CaptureMouseDownAnyWhereElse (false);
155- ForceStopFocus (1, 1);
156- UnGrabPointer ();
157- EnableInputWindow (false);
158- ShowWindow (false);
159+ if (!_enable_quicklist_for_testing)
160+ {
161+ CancelItemsPrelightStatus ();
162+ CaptureMouseDownAnyWhereElse (false);
163+ ForceStopFocus (1, 1);
164+ UnGrabPointer ();
165+ EnableInputWindow (false);
166+ ShowWindow (false);
167+ }
168 }
169 }
170
171@@ -1279,26 +1303,29 @@
172 return;
173
174 int size_above_anchor = -1; // equal to sise below
175-
176- if ((_item_list.size () != 0) || (_default_item_list.size () != 0))
177- {
178- _top_size = 4;
179- size_above_anchor = _top_size;
180- int x = _anchorX - _padding;
181- int y = _anchorY - _anchor_height/2 - _top_size - _corner_radius - _padding;
182-
183- SetBaseX (x);
184- SetBaseY (y);
185- }
186- else
187- {
188- _top_size = 0;
189- size_above_anchor = -1;
190- int x = _anchorX - _padding;
191- int y = _anchorY - _anchor_height/2 - _top_size - _corner_radius - _padding;
192-
193- SetBaseX (x);
194- SetBaseY (y);
195+
196+ if (!_enable_quicklist_for_testing)
197+ {
198+ if ((_item_list.size () != 0) || (_default_item_list.size () != 0))
199+ {
200+ _top_size = 4;
201+ size_above_anchor = _top_size;
202+ int x = _anchorX - _padding;
203+ int y = _anchorY - _anchor_height/2 - _top_size - _corner_radius - _padding;
204+
205+ SetBaseX (x);
206+ SetBaseY (y);
207+ }
208+ else
209+ {
210+ _top_size = 0;
211+ size_above_anchor = -1;
212+ int x = _anchorX - _padding;
213+ int y = _anchorY - _anchor_height/2 - _top_size - _corner_radius - _padding;
214+
215+ SetBaseX (x);
216+ SetBaseY (y);
217+ }
218 }
219
220 float blur_coef = 6.0f;
221
222=== modified file 'src/QuicklistView.h'
223--- src/QuicklistView.h 2010-12-02 15:14:22 +0000
224+++ src/QuicklistView.h 2010-12-03 13:43:15 +0000
225@@ -87,11 +87,17 @@
226 std::list<QuicklistMenuItem*> GetChildren ();
227
228 void TestMenuItems (DbusmenuMenuitem* root);
229+<<<<<<< TREE
230
231 // Introspection
232 const gchar* GetName ();
233 void AddProperties (GVariantBuilder *builder);
234
235+=======
236+
237+ void EnableQuicklistForTesting (bool enable_testing);
238+
239+>>>>>>> MERGE-SOURCE
240 private:
241 void RecvCairoTextChanged (QuicklistMenuItem* item);
242 void RecvCairoTextColorChanged (QuicklistMenuItem* item);
243@@ -135,7 +141,11 @@
244 int _top_size; // size of the segment from point 13 to 14. See figure in ql_compute_full_mask_path.
245
246 bool _mouse_down;
247-
248+
249+ //iIf true, suppress the Quicklist behaviour that is expected in Unity.
250+ // Keep the Quicklist on screen for testing and automation.
251+ bool _enable_quicklist_for_testing;
252+
253 cairo_font_options_t* _fontOpts;
254
255 nux::BaseTexture* _texture_bg;
256
257=== modified file 'tests/CMakeLists.txt'
258--- tests/CMakeLists.txt 2010-12-02 17:23:07 +0000
259+++ tests/CMakeLists.txt 2010-12-03 13:43:15 +0000
260@@ -83,6 +83,24 @@
261 ../src/PanelHomeButton.h
262 )
263
264+add_executable (test-quicklist
265+ ui/TestQuicklist.cpp
266+ ui/EventFaker.cpp
267+ ui/EventFaker.h
268+ ../src/QuicklistMenuItem.cpp
269+ ../src/QuicklistMenuItem.h
270+ ../src/QuicklistMenuItemCheckmark.cpp
271+ ../src/QuicklistMenuItemCheckmark.h
272+ ../src/QuicklistMenuItemLabel.cpp
273+ ../src/QuicklistMenuItemLabel.h
274+ ../src/QuicklistMenuItemRadio.cpp
275+ ../src/QuicklistMenuItemRadio.h
276+ ../src/QuicklistMenuItemSeparator.cpp
277+ ../src/QuicklistMenuItemSeparator.h
278+ ../src/QuicklistView.cpp
279+ ../src/QuicklistView.h
280+ )
281+
282 #
283 # check target
284 #
285
286=== added directory 'tests/ui'
287=== added file 'tests/ui/EventFaker.cpp'
288--- tests/ui/EventFaker.cpp 1970-01-01 00:00:00 +0000
289+++ tests/ui/EventFaker.cpp 2010-12-03 13:43:15 +0000
290@@ -0,0 +1,89 @@
291+/*
292+ * Copyright (C) 2010 Canonical Ltd
293+ *
294+ * This program is free software: you can redistribute it and/or modify
295+ * it under the terms of the GNU General Public License version 3 as
296+ * published by the Free Software Foundation.
297+ *
298+ * This program is distributed in the hope that it will be useful,
299+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
300+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
301+ * GNU General Public License for more details.
302+ *
303+ * You should have received a copy of the GNU General Public License
304+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
305+ *
306+ * Authored by: Mirco Müller <mirco.mueller@canonical.com>
307+ */
308+
309+#include "EventFaker.h"
310+
311+#include "NuxGraphics/GLWindowManager.h"
312+#include <X11/Xlib.h>
313+
314+EventFaker::EventFaker (nux::WindowThread* thread)
315+{
316+ _thread = thread;
317+}
318+
319+EventFaker::~EventFaker ()
320+{
321+}
322+
323+void
324+EventFaker::SendClick (nux::View* view)
325+{
326+ Display* display = NULL;
327+ int x = 0;
328+ int y = 0;
329+
330+ // sanity check
331+ if (!view)
332+ return;
333+
334+ display = nux::GetThreadGLWindow()->GetX11Display ();
335+
336+ // get a point inside the view
337+ x = view->GetBaseX () + 1;
338+ y = view->GetBaseY () + 1;
339+
340+ // assemble a button-click event
341+ XButtonEvent buttonEvent = {
342+ ButtonRelease,
343+ 0,
344+ False,
345+ display,
346+ 0,
347+ 0,
348+ 0,
349+ CurrentTime,
350+ x, y,
351+ x, y,
352+ 0,
353+ Button1,
354+ True
355+ };
356+
357+ // send that button-click to the "thread"
358+ doEvent (view, (XEvent*) &buttonEvent);
359+}
360+
361+void
362+EventFaker::doEvent (nux::View* view,
363+ XEvent* event)
364+{
365+ Display* display = NULL;
366+
367+ // sanity check
368+ if (!view || !event)
369+ return;
370+
371+ display = nux::GetThreadGLWindow()->GetX11Display ();
372+ XUngrabPointer (display, CurrentTime);
373+ XFlush (display);
374+
375+ _thread->ProcessForeignEvent (event, NULL);
376+
377+ while (g_main_context_pending (NULL))
378+ g_main_context_iteration (NULL, false);
379+}
380
381=== added file 'tests/ui/EventFaker.h'
382--- tests/ui/EventFaker.h 1970-01-01 00:00:00 +0000
383+++ tests/ui/EventFaker.h 2010-12-03 13:43:15 +0000
384@@ -0,0 +1,40 @@
385+/*
386+ * Copyright (C) 2010 Canonical Ltd
387+ *
388+ * This program is free software: you can redistribute it and/or modify
389+ * it under the terms of the GNU General Public License version 3 as
390+ * published by the Free Software Foundation.
391+ *
392+ * This program is distributed in the hope that it will be useful,
393+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
394+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
395+ * GNU General Public License for more details.
396+ *
397+ * You should have received a copy of the GNU General Public License
398+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
399+ *
400+ * Authored by: Mirco Müller <mirco.mueller@canonical.com>
401+ */
402+
403+#ifndef EVENT_FAKER_H
404+#define EVENT_FAKER_H
405+
406+#include "Nux/Nux.h"
407+#include "Nux/WindowThread.h"
408+
409+class EventFaker
410+{
411+ public:
412+ EventFaker (nux::WindowThread* thread);
413+ ~EventFaker ();
414+
415+ void SendClick (nux::View* view);
416+
417+ void doEvent (nux::View* view,
418+ XEvent* event);
419+
420+ private:
421+ nux::WindowThread* _thread;
422+};
423+
424+#endif // EVENT_FAKER_H
425
426=== added file 'tests/ui/TestQuicklist.cpp'
427--- tests/ui/TestQuicklist.cpp 1970-01-01 00:00:00 +0000
428+++ tests/ui/TestQuicklist.cpp 2010-12-03 13:43:15 +0000
429@@ -0,0 +1,305 @@
430+/*
431+ * Copyright (C) 2010 Canonical Ltd
432+ *
433+ * This program is free software: you can redistribute it and/or modify
434+ * it under the terms of the GNU General Public License version 3 as
435+ * published by the Free Software Foundation.
436+ *
437+ * This program is distributed in the hope that it will be useful,
438+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
439+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
440+ * GNU General Public License for more details.
441+ *
442+ * You should have received a copy of the GNU General Public License
443+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
444+ *
445+ * Authored by: Mirco Müller <mirco.mueller@canonical.com>
446+ */
447+
448+#include <glib.h>
449+#include <gtk/gtk.h>
450+#include <dbus/dbus-glib.h>
451+
452+#include "Nux/Nux.h"
453+#include "Nux/VLayout.h"
454+#include "Nux/WindowThread.h"
455+
456+#include "QuicklistView.h"
457+#include "QuicklistMenuItem.h"
458+#include "QuicklistMenuItemLabel.h"
459+#include "QuicklistMenuItemSeparator.h"
460+#include "QuicklistMenuItemCheckmark.h"
461+#include "QuicklistMenuItemRadio.h"
462+
463+#include "EventFaker.h"
464+#include <X11/Xlib.h>
465+
466+#define WIN_WIDTH 400
467+#define WIN_HEIGHT 300
468+
469+gboolean gResult[3] = {false, false, false};
470+
471+QuicklistView::QuicklistView* gQuicklist = NULL;
472+QuicklistMenuItemCheckmark* gCheckmark = NULL;
473+QuicklistMenuItemRadio* gRadio = NULL;
474+QuicklistMenuItemLabel* gLabel = NULL;
475+
476+void
477+activatedCallback (DbusmenuMenuitem* item,
478+ int time,
479+ gpointer data)
480+{
481+ gboolean* result = (gboolean*) data;
482+
483+ *result = true;
484+
485+ g_print ("Quicklist-item activated\n");
486+}
487+
488+QuicklistMenuItemCheckmark*
489+createCheckmarkItem ()
490+{
491+ DbusmenuMenuitem* item = NULL;
492+ QuicklistMenuItemCheckmark* checkmark = NULL;
493+
494+ item = dbusmenu_menuitem_new ();
495+
496+ dbusmenu_menuitem_property_set (item,
497+ DBUSMENU_MENUITEM_PROP_LABEL,
498+ "Unchecked");
499+
500+ dbusmenu_menuitem_property_set (item,
501+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
502+ DBUSMENU_MENUITEM_TOGGLE_CHECK);
503+
504+ dbusmenu_menuitem_property_set_bool (item,
505+ DBUSMENU_MENUITEM_PROP_ENABLED,
506+ true);
507+
508+ dbusmenu_menuitem_property_set_int (item,
509+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
510+ DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED);
511+
512+ checkmark = new QuicklistMenuItemCheckmark (item, true);
513+
514+ g_signal_connect (item,
515+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
516+ G_CALLBACK (activatedCallback),
517+ &gResult[0]);
518+
519+ return checkmark;
520+}
521+
522+QuicklistMenuItemRadio*
523+createRadioItem ()
524+{
525+ DbusmenuMenuitem* item = NULL;
526+ QuicklistMenuItemRadio* radio = NULL;
527+
528+ item = dbusmenu_menuitem_new ();
529+
530+ dbusmenu_menuitem_property_set (item,
531+ DBUSMENU_MENUITEM_PROP_LABEL,
532+ "Radio Active");
533+
534+ dbusmenu_menuitem_property_set (item,
535+ DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
536+ DBUSMENU_MENUITEM_TOGGLE_RADIO);
537+
538+ dbusmenu_menuitem_property_set_bool (item,
539+ DBUSMENU_MENUITEM_PROP_ENABLED,
540+ true);
541+
542+ dbusmenu_menuitem_property_set_int (item,
543+ DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
544+ DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
545+
546+ radio = new QuicklistMenuItemRadio (item, true);
547+
548+ g_signal_connect (item,
549+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
550+ G_CALLBACK (activatedCallback),
551+ &gResult[1]);
552+
553+ return radio;
554+}
555+
556+QuicklistMenuItemLabel*
557+createLabelItem ()
558+{
559+ DbusmenuMenuitem* item = NULL;
560+ QuicklistMenuItemLabel* label = NULL;
561+
562+ item = dbusmenu_menuitem_new ();
563+
564+ dbusmenu_menuitem_property_set (item,
565+ DBUSMENU_MENUITEM_PROP_LABEL,
566+ "A Label");
567+
568+ dbusmenu_menuitem_property_set_bool (item,
569+ DBUSMENU_MENUITEM_PROP_ENABLED,
570+ true);
571+
572+ label = new QuicklistMenuItemLabel (item, true);
573+
574+ g_signal_connect (item,
575+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
576+ G_CALLBACK (activatedCallback),
577+ &gResult[2]);
578+
579+ return label;
580+}
581+
582+void
583+ThreadWidgetInit (nux::NThread* thread,
584+ void* initData)
585+{
586+ gQuicklist = new QuicklistView::QuicklistView ();
587+ gQuicklist->Reference ();
588+
589+ gCheckmark = createCheckmarkItem ();
590+ gQuicklist->AddMenuItem (gCheckmark);
591+ gRadio = createRadioItem ();
592+ gQuicklist->AddMenuItem (gRadio);
593+ gLabel = createLabelItem ();
594+ gQuicklist->AddMenuItem (gLabel);
595+
596+ gQuicklist->EnableQuicklistForTesting (true);
597+
598+ gQuicklist->SetBaseXY (0, 0);
599+ gQuicklist->ShowWindow (true);
600+}
601+
602+void
603+ControlThread (nux::NThread* thread,
604+ void* data)
605+{
606+ // sleep for 3 seconds
607+ nux::SleepForMilliseconds (3000);
608+ printf ("ControlThread successfully started\n");
609+
610+ nux::WindowThread* mainWindowThread = NUX_STATIC_CAST (nux::WindowThread*,
611+ data);
612+
613+ mainWindowThread->SetFakeEventMode (true);
614+ Display* display = mainWindowThread->GetWindow ().GetX11Display ();
615+
616+ // assemble first button-click event
617+ XEvent buttonPressEvent;
618+ buttonPressEvent.xbutton.type = ButtonPress;
619+ buttonPressEvent.xbutton.serial = 0;
620+ buttonPressEvent.xbutton.send_event = False;
621+ buttonPressEvent.xbutton.display = display;
622+ buttonPressEvent.xbutton.window = 0;
623+ buttonPressEvent.xbutton.root = 0;
624+ buttonPressEvent.xbutton.subwindow = 0;
625+ buttonPressEvent.xbutton.time = CurrentTime;
626+ buttonPressEvent.xbutton.x = 50;
627+ buttonPressEvent.xbutton.y = 30;
628+ buttonPressEvent.xbutton.x_root = 0;
629+ buttonPressEvent.xbutton.y_root = 0;
630+ buttonPressEvent.xbutton.state = 0;
631+ buttonPressEvent.xbutton.button = Button1;
632+ buttonPressEvent.xbutton.same_screen = True;
633+
634+ mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread,
635+ (XEvent*) &buttonPressEvent);
636+
637+ while (!mainWindowThread->ReadyForNextFakeEvent ())
638+ nux::SleepForMilliseconds (10);
639+
640+ XEvent buttonReleaseEvent;
641+ buttonReleaseEvent.xbutton.type = ButtonRelease;
642+ buttonReleaseEvent.xbutton.serial = 0;
643+ buttonReleaseEvent.xbutton.send_event = False;
644+ buttonReleaseEvent.xbutton.display = display;
645+ buttonReleaseEvent.xbutton.window = 0;
646+ buttonReleaseEvent.xbutton.root = 0;
647+ buttonReleaseEvent.xbutton.subwindow = 0;
648+ buttonReleaseEvent.xbutton.time = CurrentTime;
649+ buttonReleaseEvent.xbutton.x = 50;
650+ buttonReleaseEvent.xbutton.y = 30;
651+ buttonReleaseEvent.xbutton.x_root = 0;
652+ buttonReleaseEvent.xbutton.y_root = 0;
653+ buttonReleaseEvent.xbutton.state = 0;
654+ buttonReleaseEvent.xbutton.button = Button1;
655+ buttonReleaseEvent.xbutton.same_screen = True;
656+
657+ mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread,
658+ (XEvent*) &buttonReleaseEvent);
659+
660+ while (!mainWindowThread->ReadyForNextFakeEvent ())
661+ nux::SleepForMilliseconds (10);
662+
663+ // assemble second button-click event
664+ buttonPressEvent.xbutton.time = CurrentTime;
665+ buttonPressEvent.xbutton.x = 50;
666+ buttonPressEvent.xbutton.y = 45;
667+ mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread,
668+ (XEvent*) &buttonPressEvent);
669+ while (!mainWindowThread->ReadyForNextFakeEvent ())
670+ nux::SleepForMilliseconds (10);
671+
672+ buttonReleaseEvent.xbutton.time = CurrentTime;
673+ buttonReleaseEvent.xbutton.x = 50;
674+ buttonReleaseEvent.xbutton.y = 45;
675+ mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread,
676+ (XEvent*) &buttonReleaseEvent);
677+ while (!mainWindowThread->ReadyForNextFakeEvent ())
678+ nux::SleepForMilliseconds (10);
679+
680+ // assemble third button-click event
681+ buttonPressEvent.xbutton.time = CurrentTime;
682+ buttonPressEvent.xbutton.x = 50;
683+ buttonPressEvent.xbutton.y = 60;
684+ mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread,
685+ (XEvent*) &buttonPressEvent);
686+ while (!mainWindowThread->ReadyForNextFakeEvent ())
687+ nux::SleepForMilliseconds (10);
688+
689+ buttonReleaseEvent.xbutton.time = CurrentTime;
690+ buttonReleaseEvent.xbutton.x = 50;
691+ buttonReleaseEvent.xbutton.y = 60;
692+ mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread,
693+ (XEvent*) &buttonReleaseEvent);
694+ while (!mainWindowThread->ReadyForNextFakeEvent ())
695+ nux::SleepForMilliseconds (10);
696+
697+ mainWindowThread->SetFakeEventMode (false);
698+}
699+
700+int
701+main (int argc, char **argv)
702+{
703+ nux::WindowThread* wt = NULL;
704+ nux::SystemThread* st = NULL;
705+
706+ g_type_init ();
707+ g_thread_init (NULL);
708+ gtk_init (&argc, &argv);
709+ dbus_g_thread_init ();
710+ nux::NuxInitialize (0);
711+
712+ wt = nux::CreateGUIThread (TEXT ("Unity Quicklist"),
713+ WIN_WIDTH,
714+ WIN_HEIGHT,
715+ 0,
716+ &ThreadWidgetInit,
717+ NULL);
718+
719+ st = nux::CreateSystemThread (NULL, ControlThread, wt);
720+ if (st)
721+ st->Start (NULL);
722+
723+ wt->Run (NULL);
724+
725+ gQuicklist->UnReference ();
726+ delete st;
727+ delete wt;
728+
729+ g_assert_cmpint (gResult[0], ==, true);
730+ g_assert_cmpint (gResult[1], ==, true);
731+ g_assert_cmpint (gResult[2], ==, true);
732+
733+ return 0;
734+}