Status: | Merged |
---|---|
Merged at revision: | 640 |
Proposed branch: | lp:~dandrader/nux/geis |
Merge into: | lp:nux |
Diff against target: |
5504 lines (+4518/-215) (has conflicts) 48 files modified
.bzrignore (+2/-0) Nux/Area.cpp (+33/-0) Nux/Area.h (+19/-0) Nux/BaseWindow.cpp (+23/-0) Nux/BaseWindow.h (+10/-0) Nux/Features.h.in (+14/-0) Nux/GeisAdapter.cpp (+528/-0) Nux/GeisAdapter.h (+122/-0) Nux/Gesture.cpp (+295/-0) Nux/Gesture.h (+170/-0) Nux/GestureBroker.cpp (+200/-0) Nux/GestureBroker.h (+63/-0) Nux/GesturesSubscription.cpp (+379/-0) Nux/GesturesSubscription.h (+181/-0) Nux/InputArea.cpp (+77/-1) Nux/InputArea.h (+79/-0) Nux/Layout.cpp (+20/-0) Nux/Layout.h (+4/-0) Nux/MainLoopGLib.cpp (+22/-2) Nux/Makefile.am (+20/-1) Nux/View.cpp (+23/-0) Nux/View.h (+7/-0) Nux/WindowCompositor.cpp (+69/-0) Nux/WindowCompositor.h (+29/-0) Nux/WindowThread.cpp (+246/-177) Nux/WindowThread.h (+40/-24) NuxCore/Rect.cpp (+6/-0) NuxCore/Rect.h (+1/-0) NuxGraphics/Events.h (+5/-1) NuxGraphics/GestureEvent.cpp (+80/-0) NuxGraphics/GestureEvent.h (+162/-0) NuxGraphics/GraphicsDisplayWin.cpp (+6/-1) NuxGraphics/GraphicsDisplayWin.h (+4/-1) NuxGraphics/GraphicsDisplayX11.cpp (+7/-2) NuxGraphics/GraphicsDisplayX11.h (+4/-1) NuxGraphics/Makefile.am (+14/-2) NuxGraphics/nux-graphics.pc.in (+1/-1) configure.ac (+55/-1) examples/Makefile.am (+8/-0) examples/gestures.cpp (+100/-0) tests/FakeGestureEvent.h (+63/-0) tests/Makefile.am (+7/-0) tests/geis_mock.cpp (+483/-0) tests/geis_mock.h (+148/-0) tests/gtest-nux-geisadapter.cpp (+230/-0) tests/gtest-nux-gesturebroker.cpp (+142/-0) tests/gtest-nux-main.cpp (+17/-0) tests/gtest-nux-windowcompositor.cpp (+300/-0) Text conflict in configure.ac |
To merge this branch: | bzr merge lp:~dandrader/nux/geis |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jay Taoko (community) | Approve | ||
Chase Douglas (community) | Approve | ||
Francis Ginther | continuous-integration | Pending | |
Review via email: mp+108239@code.launchpad.net |
Commit message
Description of the change
Adding gestures support to Nux.
Daniel d'Andrada (dandrader) wrote : | # |
Chase Douglas (chasedouglas) wrote : | # |
* Please use one of the c++ or NUX-specific casts. For example:
GeisAdapterEven
should be:
GeisAdapterEven
* Why is GeisAdapter:
* How do we make sure that a nux area that receives a gesture begin will also receive the gesture updates and end for the same gesture? What if the area moves such that the touches no longer are in it? The gesture events must still go to the area that received the begin event. I don't see code that handles this.
* GET_LATEST_EVENT should be a real function, not a macro. Use anonymous namespaces or static functions.
I have reviewed all the utouch-specific code. I think it's sound other than the above issues. I have not reviewed any code that modifies how Nux works.
Jay Taoko (jaytaoko) wrote : | # |
I get a build error:
make[2]: Entering directory `/home/
CXX libnux_
./GeisAdapter.cpp: In member function 'void nux::GeisAdapte
./GeisAdapter.
Am I missing a library on my system? I am still on Precise. I have libutouch-geis-dev installed.
Daniel d'Andrada (dandrader) wrote : | # |
> I get a build error:
>
> make[2]: Entering directory `/home/
> CXX libnux_
> ./GeisAdapter.cpp: In member function 'void
> nux::GeisAdapte
> nux::EventType)':
> ./GeisAdapter.
> was not declared in this scope
>
> Am I missing a library on my system? I am still on Precise. I have libutouch-
> geis-dev installed.
No. That's a recent addition to utouch-geis API. You will have to get it directly from lp:utouch-geis/trunk.
Daniel d'Andrada (dandrader) wrote : | # |
Back to "work in progress" status as GesturesSubscri
Daniel d'Andrada (dandrader) wrote : | # |
> * How do we make sure that a nux area that receives a gesture begin will also
> receive the gesture updates and end for the same gesture? What if the area
> moves such that the touches no longer are in it? The gesture events must still
> go to the area that received the begin event. I don't see code that handles
> this.
The class Gesture binds a gesture to a target InputArea. That is done on GESTURE_BEGIN event and doesn't change after that.
Daniel d'Andrada (dandrader) wrote : | # |
> * Why is GeisAdapter:
> ProcessGeisEvents?
It's a small optimization to avoid unnecessary memory allocations.
- 620. By Gord Allott
-
removes some executable flags from nux cpp/h files. Fixes: . Approved by Jay Taoko.
- 621. By Steve Baker
-
Cleaning up some coverity warnings.. Fixes: https:/
/bugs.launchpad .net/bugs/ 937564, https:/ /bugs.launchpad .net/bugs/ 937576, https:/ /bugs.launchpad .net/bugs/ 937586, https:/ /bugs.launchpad .net/bugs/ 937588. Approved by Tim Penhey. - 622. By Andrea Azzarone
-
Don't call QueueDraw during the layout process.. Fixes: https:/
/bugs.launchpad .net/bugs/ 994884. Approved by Michal Hruby, Tim Penhey.
Daniel d'Andrada (dandrader) wrote : | # |
> * GET_LATEST_EVENT should be a real function, not a macro. Use anonymous
> namespaces or static functions.
That won't work because that code is called from both a const and a regular method that return a const reference and a regular one respectively. The only other way I see would be to call the const version from withing the non-const one and doing a const_cast on the return value. But I don't see it as a better option.
Daniel d'Andrada (dandrader) wrote : | # |
It's been improved and all comments from Chase have been worked on (either fixed in code or explained).
A patch for unity to make is work with that new Nux is also ready in lp:~dandrader/unity/nux_gestures.
Ready for a new review.
Chase Douglas (chasedouglas) wrote : | # |
> > * Why is GeisAdapter:
> > ProcessGeisEvents?
>
> It's a small optimization to avoid unnecessary memory allocations.
Sorry for not replying to this sooner.
Variables that are only accessed in a function should be local to that function. It's a rule in place in order to keep the source code structured. If a developer needs to change the code that manipulates the object, they would have to check how the object is used elsewhere unless it is local.
The object is a class with one virtual function and a bunch of data. This shouldn't be an issue when allocated on the stack.
Chase Douglas (chasedouglas) wrote : | # |
> > * GET_LATEST_EVENT should be a real function, not a macro. Use anonymous
> > namespaces or static functions.
>
> That won't work because that code is called from both a const and a regular
> method that return a const reference and a regular one respectively. The only
> other way I see would be to call the const version from withing the non-const
> one and doing a const_cast on the return value. But I don't see it as a better
> option.
I guess I'm confused. Why do you need a const version of the method? Why not just return a non-const version, and any callers that want to keep it as const can just assign it to add const-ness.
Chase Douglas (chasedouglas) wrote : | # |
> > * How do we make sure that a nux area that receives a gesture begin will
> also
> > receive the gesture updates and end for the same gesture? What if the area
> > moves such that the touches no longer are in it? The gesture events must
> still
> > go to the area that received the begin event. I don't see code that handles
> > this.
>
> The class Gesture binds a gesture to a target InputArea. That is done on
> GESTURE_BEGIN event and doesn't change after that.
I think I see how it's working now. I'm happy :).
Chase Douglas (chasedouglas) wrote : | # |
I notice that only one gesture is allowed per target area. Theoretically this should be possible. The issue is when there are gestures with overlapping touches. Why now allow for multiple non-overlapping gestures?
Daniel d'Andrada (dandrader) wrote : | # |
> I notice that only one gesture is allowed per target area. Theoretically this
> should be possible. The issue is when there are gestures with overlapping
> touches. Why now allow for multiple non-overlapping gestures?
What's the use for that? I fear this could make the implementation overly complex for no use case in sight.
Daniel d'Andrada (dandrader) wrote : | # |
> > > * GET_LATEST_EVENT should be a real function, not a macro. Use anonymous
> > > namespaces or static functions.
> >
> > That won't work because that code is called from both a const and a regular
> > method that return a const reference and a regular one respectively. The
> only
> > other way I see would be to call the const version from withing the non-
> const
> > one and doing a const_cast on the return value. But I don't see it as a
> better
> > option.
>
> I guess I'm confused. Why do you need a const version of the method?
Because it's called from within other const methods.
> Why not just return a non-const version, and any callers that want to keep it as const
> can just assign it to add const-ness.
I didn't understand you. Could you please write down some example code?
Daniel d'Andrada (dandrader) wrote : | # |
> > > * Why is GeisAdapter:
> > > ProcessGeisEvents?
> >
> > It's a small optimization to avoid unnecessary memory allocations.
>
> Sorry for not replying to this sooner.
>
> Variables that are only accessed in a function should be local to that
> function. It's a rule in place in order to keep the source code structured. If
> a developer needs to change the code that manipulates the object, they would
> have to check how the object is used elsewhere unless it is local.
>
> The object is a class with one virtual function and a bunch of data. This
> shouldn't be an issue when allocated on the stack.
Ok, I will remove that optimization since you insist on it.
Chase Douglas (chasedouglas) wrote : | # |
On 06/15/2012 11:48 AM, Daniel d'Andrada wrote:
>>>> * GET_LATEST_EVENT should be a real function, not a macro. Use anonymous
>>>> namespaces or static functions.
>>>
>>> That won't work because that code is called from both a const and a regular
>>> method that return a const reference and a regular one respectively. The
>> only
>>> other way I see would be to call the const version from withing the non-
>> const
>>> one and doing a const_cast on the return value. But I don't see it as a
>> better
>>> option.
>>
>> I guess I'm confused. Why do you need a const version of the method?
>
> Because it's called from within other const methods.
>
>> Why not just return a non-const version, and any callers that want to keep it as const
>> can just assign it to add const-ness.
>
> I didn't understand you. Could you please write down some example code?
Sorry, I realize there are a few things going on, including me
forgetting about const methods only being able to call const methods.
I think we can resolve all this by removing the non-const
GetLatestEvent() method and making GestureEvent:
GestureEvent:
methods that modify the GestureEvent object.
Daniel d'Andrada (dandrader) wrote : | # |
> On 06/15/2012 11:48 AM, Daniel d'Andrada wrote:
> >>>> * GET_LATEST_EVENT should be a real function, not a macro. Use anonymous
> >>>> namespaces or static functions.
> >>>
> >>> That won't work because that code is called from both a const and a
> regular
> >>> method that return a const reference and a regular one respectively. The
> >> only
> >>> other way I see would be to call the const version from withing the non-
> >> const
> >>> one and doing a const_cast on the return value. But I don't see it as a
> >> better
> >>> option.
> >>
> >> I guess I'm confused. Why do you need a const version of the method?
> >
> > Because it's called from within other const methods.
> >
> >> Why not just return a non-const version, and any callers that want to keep
> it as const
> >> can just assign it to add const-ness.
> >
> > I didn't understand you. Could you please write down some example code?
>
> Sorry, I realize there are a few things going on, including me
> forgetting about const methods only being able to call const methods.
>
> I think we can resolve all this by removing the non-const
> GetLatestEvent() method and making GestureEvent:
> GestureEvent:
> methods that modify the GestureEvent object.
That works but makes for a weird API. Accept() and Reject() doesn't sound like methods that should be const (like getters) even though in their current implementation they can be so.
Chase Douglas (chasedouglas) wrote : | # |
On 06/15/2012 01:34 PM, Daniel d'Andrada wrote:
>> On 06/15/2012 11:48 AM, Daniel d'Andrada wrote:
>>>>>> * GET_LATEST_EVENT should be a real function, not a macro. Use anonymous
>>>>>> namespaces or static functions.
>>>>>
>>>>> That won't work because that code is called from both a const and a
>> regular
>>>>> method that return a const reference and a regular one respectively. The
>>>> only
>>>>> other way I see would be to call the const version from withing the non-
>>>> const
>>>>> one and doing a const_cast on the return value. But I don't see it as a
>>>> better
>>>>> option.
>>>>
>>>> I guess I'm confused. Why do you need a const version of the method?
>>>
>>> Because it's called from within other const methods.
>>>
>>>> Why not just return a non-const version, and any callers that want to keep
>> it as const
>>>> can just assign it to add const-ness.
>>>
>>> I didn't understand you. Could you please write down some example code?
>>
>> Sorry, I realize there are a few things going on, including me
>> forgetting about const methods only being able to call const methods.
>>
>> I think we can resolve all this by removing the non-const
>> GetLatestEvent() method and making GestureEvent:
>> GestureEvent:
>> methods that modify the GestureEvent object.
>
> That works but makes for a weird API. Accept() and Reject() doesn't sound like methods that should be const (like getters) even though in their current implementation they can be so.
I understand the logic, but c++ constness is really an API construct,
not a logic construct. Usually the domains overlap, but not always.
Admittedly, I don't have any good examples, but I feel being true to the
execution when using constness is best.
Please enlighten me if you think I'm wrong. I'm not an expert on C++, I
just hope I know how to not shoot my foot with it :).
Daniel d'Andrada (dandrader) wrote : | # |
> On 06/15/2012 01:34 PM, Daniel d'Andrada wrote:
> >> On 06/15/2012 11:48 AM, Daniel d'Andrada wrote:
> >>>>>> * GET_LATEST_EVENT should be a real function, not a macro. Use
> anonymous
> >>>>>> namespaces or static functions.
> >>>>>
> >>>>> That won't work because that code is called from both a const and a
> >> regular
> >>>>> method that return a const reference and a regular one respectively. The
> >>>> only
> >>>>> other way I see would be to call the const version from withing the non-
> >>>> const
> >>>>> one and doing a const_cast on the return value. But I don't see it as a
> >>>> better
> >>>>> option.
> >>>>
> >>>> I guess I'm confused. Why do you need a const version of the method?
> >>>
> >>> Because it's called from within other const methods.
> >>>
> >>>> Why not just return a non-const version, and any callers that want to
> keep
> >> it as const
> >>>> can just assign it to add const-ness.
> >>>
> >>> I didn't understand you. Could you please write down some example code?
> >>
> >> Sorry, I realize there are a few things going on, including me
> >> forgetting about const methods only being able to call const methods.
> >>
> >> I think we can resolve all this by removing the non-const
> >> GetLatestEvent() method and making GestureEvent:
> >> GestureEvent:
> >> methods that modify the GestureEvent object.
> >
> > That works but makes for a weird API. Accept() and Reject() doesn't sound
> like methods that should be const (like getters) even though in their current
> implementation they can be so.
>
> I understand the logic, but c++ constness is really an API construct,
> not a logic construct. Usually the domains overlap, but not always.
> Admittedly, I don't have any good examples, but I feel being true to the
> execution when using constness is best.
>
> Please enlighten me if you think I'm wrong. I'm not an expert on C++, I
> just hope I know how to not shoot my foot with it :).
Changed from the macro to the const_cast way that people say "Effective C++" book advocates.
Hope you like it better :)
Chase Douglas (chasedouglas) wrote : | # |
> > On 06/15/2012 01:34 PM, Daniel d'Andrada wrote:
> > >> On 06/15/2012 11:48 AM, Daniel d'Andrada wrote:
> > >>>>>> * GET_LATEST_EVENT should be a real function, not a macro. Use
> > anonymous
> > >>>>>> namespaces or static functions.
> > >>>>>
> > >>>>> That won't work because that code is called from both a const and a
> > >> regular
> > >>>>> method that return a const reference and a regular one respectively.
> The
> > >>>> only
> > >>>>> other way I see would be to call the const version from withing the
> non-
> > >>>> const
> > >>>>> one and doing a const_cast on the return value. But I don't see it as
> a
> > >>>> better
> > >>>>> option.
> > >>>>
> > >>>> I guess I'm confused. Why do you need a const version of the method?
> > >>>
> > >>> Because it's called from within other const methods.
> > >>>
> > >>>> Why not just return a non-const version, and any callers that want to
> > keep
> > >> it as const
> > >>>> can just assign it to add const-ness.
> > >>>
> > >>> I didn't understand you. Could you please write down some example code?
> > >>
> > >> Sorry, I realize there are a few things going on, including me
> > >> forgetting about const methods only being able to call const methods.
> > >>
> > >> I think we can resolve all this by removing the non-const
> > >> GetLatestEvent() method and making GestureEvent:
> > >> GestureEvent:
> > >> methods that modify the GestureEvent object.
> > >
> > > That works but makes for a weird API. Accept() and Reject() doesn't sound
> > like methods that should be const (like getters) even though in their
> current
> > implementation they can be so.
> >
> > I understand the logic, but c++ constness is really an API construct,
> > not a logic construct. Usually the domains overlap, but not always.
> > Admittedly, I don't have any good examples, but I feel being true to the
> > execution when using constness is best.
> >
> > Please enlighten me if you think I'm wrong. I'm not an expert on C++, I
> > just hope I know how to not shoot my foot with it :).
>
> Changed from the macro to the const_cast way that people say "Effective C++"
> book advocates.
> Hope you like it better :)
It's better, but I'm not sure it's 100% correct. It feels to me like this shows the design isn't proper, but I admit to having written similar things in the past because I couldn't figure out how to do it "correctly" either. I certainly don't have any better suggestions, so it's good enough for me :).
Everything else I've noted has been taken care of, so I approve :). Yay!
- 623. By Daniel van Vugt
-
Don't allow a View to be queued multiple times on the draw list. It causes
unbounded list growth and the UI to freeze in some cases. (LP: #1014610)
. Fixes: https://bugs.launchpad .net/bugs/ 1014610. Approved by Tim Penhey. - 624. By Brandon Schaefer
-
Fix memory leak in InputMethdIBus:
:OnPreeditUpdat e.. Fixes: . Approved by Marco Trevisan (Treviño). - 625. By Marco Trevisan (Treviño)
-
TextEntry: Handle also Key-Up events so that ibus will work with release hotkeys
InputMethodIBus: keep a list of hotkey events and add IsHotkeyEvent and IsConnected. Fixes: https:/
/bugs.launchpad .net/bugs/ 1016665. Approved by Brandon Schaefer. - 626. By Łukasz Zemczak
-
Updated Nux API version to 3.0. Deprecated nux-image library. The feature by nux-image has been merged into nux-graphics.. Fixes: . Approved by Łukasz Zemczak, Didier Roche, Tim Penhey.
- 627. By Marco Trevisan (Treviño)
-
TextEntry: don't allow invalid character to be written and ignore keypress when holding Alt or Super. Fixes: https:/
/bugs.launchpad .net/bugs/ 1013751. Approved by Brandon Schaefer. - 628. By Marco Trevisan (Treviño)
-
TextEntry: make the clipboard functions virtual to be implemented by clients. Fixes: https:/
/bugs.launchpad .net/bugs/ 1016354. Approved by Thomi Richards, Brandon Schaefer. - 629. By Marco Trevisan (Treviño)
-
TextEntry: ignore meta keypresses when in composition mode. Fixes: https:/
/bugs.launchpad .net/bugs/ 961741. Approved by Andrea Azzarone. - 630. By Marco Trevisan (Treviño)
-
TextEntry: correctly handle a "double-deadkey" pressure
Correctly reset the dead_key mode when terminated.. Fixes: https:/
/bugs.launchpad .net/bugs/ 950740, https:/ /bugs.launchpad .net/bugs/ 961741. Approved by Brandon Schaefer. - 631. By Michal Hruby
-
Make GdkPixbuf conversions faster. Fixes: . Approved by Jay Taoko, Sam Spilsbury.
- 632. By Jay Taoko
-
* Fix in volume texture API. Fixes: . Approved by Gord Allott.
- 633. By Marco Trevisan (Treviño)
-
TextEntryCompos
eSeqs: updated with new sequencies computed using compose key list generator script List based on:
http://cgit.freedeskto p.org/xorg/ lib/libX11/ plain/nls/ en_US.UTF- 8/Compose. pre Updated also the dead_keys_map with valid values and updated to char*. Fixes: https:/
/bugs.launchpad .net/bugs/ 961741. Approved by Brandon Schaefer. - 634. By Jay Taoko
-
* Refactoring Nux timer API: Some cleanup and update were required for the animation framework.. Fixes: . Approved by Jay Taoko.
- 635. By Marco Trevisan (Treviño)
-
TextEntry: refactored the composition code to use KeySym's. Fixes: . Approved by Brandon Schaefer.
- 636. By Brandon Schaefer
-
Fixed some tests.. Fixes: . Approved by Thomi Richards.
- 637. By Jay Taoko
-
* Added support for character hiding in the text entry (password input). Fixes: . Approved by Brandon Schaefer.
- 650. By Daniel d'Andrada
-
Obey event inspectors and do discard events when told so
- 651. By Daniel d'Andrada
-
Split a Geis Tap Update into a Tap Begin and a Tap End
That's to ensure that all gestures behave consistenly. They
should be comprised by a Begin event followed by zero or more
Update events and end with a End event.Tap gestures don't follow this rule as it only sends a single Update
and nothing more. Exposing this to upper layers in the code would require
special casing of Tap gestures everywhere. - 652. By Daniel d'Andrada
-
Make GesturesSubscri
ptions accept multiple gesture classes - 653. By Daniel d'Andrada
-
Use C++ casts instead of C casts in GeisAdapter
- 654. By Daniel d'Andrada
-
Correctly process gesture events that finish their constructions only when ending.
- 655. By Daniel d'Andrada
-
Expose configuration options in GesturesSubscri
ption - 656. By Daniel d'Andrada
-
GeisAdapter: make nux_event_ a local variable
As it's only used iniside ProcessGeisEven
ts().
The code gets clearer and the performance penalty should be irrelevant. - 657. By Daniel d'Andrada
-
Gestures support needs at least version 2.2.10 or utouch-geis
- 658. By Daniel d'Andrada
-
Resolve the const&non-const getters issue the const_cast way
As "Effective C++" advocates
- 659. By Daniel d'Andrada
-
Some refactoring so that unity can use more code from nux
instead of having unity using a modified copy of GestureBroker, Gesture, etc.
- 660. By Daniel d'Andrada
-
Fix build
- 661. By Daniel d'Andrada
-
Use geis from Open Input Framework instead of utouch-geis
- 662. By Daniel d'Andrada
-
Fix build of gtest-nux
- 663. By Daniel d'Andrada
-
Bump ABI version
Jay Taoko (jaytaoko) : | # |
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2011-11-11 00:40:50 +0000 |
3 | +++ .bzrignore 2012-08-01 18:42:21 +0000 |
4 | @@ -63,5 +63,7 @@ |
5 | **/nux.log |
6 | **/nux.log.* |
7 | examples/cairo_wrapper |
8 | +examples/gestures |
9 | examples/tiles_view |
10 | test-graphics-display |
11 | +Nux/Features.h |
12 | |
13 | === modified file 'Nux/Area.cpp' |
14 | --- Nux/Area.cpp 2012-06-12 09:13:06 +0000 |
15 | +++ Nux/Area.cpp 2012-08-01 18:42:21 +0000 |
16 | @@ -29,6 +29,10 @@ |
17 | #include "BaseWindow.h" |
18 | #include "MenuPage.h" |
19 | |
20 | +#ifdef NUX_GESTURES_SUPPORT |
21 | +#include "NuxGraphics/GestureEvent.h" |
22 | +#endif |
23 | + |
24 | namespace nux |
25 | { |
26 | |
27 | @@ -1036,5 +1040,34 @@ |
28 | |
29 | return false; |
30 | } |
31 | + |
32 | +#ifdef NUX_GESTURES_SUPPORT |
33 | + Area* Area::GetInputAreaHitByGesture(const GestureEvent &event) |
34 | + { |
35 | + return nullptr; |
36 | + } |
37 | + |
38 | + bool Area::IsGestureInsideArea(const nux::GestureEvent &event) const |
39 | + { |
40 | + if (event.IsDirectTouch()) |
41 | + { |
42 | + Geometry geometry = GetAbsoluteGeometry(); |
43 | + Point p; |
44 | + for (const auto touch_point : event.GetTouches()) |
45 | + { |
46 | + p.x = (int)touch_point.x; |
47 | + p.y = (int)touch_point.y; |
48 | + if (!geometry.IsInside(p)) |
49 | + return false; |
50 | + } |
51 | + return true; |
52 | + } |
53 | + else |
54 | + { |
55 | + return GetAbsoluteGeometry().IsInside(event.GetFocus()); |
56 | + } |
57 | + } |
58 | + |
59 | +#endif // NUX_GESTURES_SUPPORT |
60 | } |
61 | |
62 | |
63 | === modified file 'Nux/Area.h' |
64 | --- Nux/Area.h 2012-03-12 21:45:30 +0000 |
65 | +++ Nux/Area.h 2012-08-01 18:42:21 +0000 |
66 | @@ -24,6 +24,7 @@ |
67 | #define BASEOBJECT_H |
68 | |
69 | #include <sigc++/sigc++.h> |
70 | +#include "Features.h" |
71 | #include "NuxCore/InitiallyUnownedObject.h" |
72 | #include "NuxGraphics/Events.h" |
73 | #include "Utils.h" |
74 | @@ -33,6 +34,9 @@ |
75 | { |
76 | class WindowThread; |
77 | class GraphicsEngine; |
78 | +#ifdef NUX_GESTURES_SUPPORT |
79 | + class GestureEvent; |
80 | +#endif // NUX_GESTURES_SUPPORT |
81 | |
82 | // In a Horizontal/Vertical Layout, the following enum have the respective meanings: |
83 | // eFull: the object has the full height/width of the parent layout(minus margins) |
84 | @@ -623,6 +627,21 @@ |
85 | //! If this variable is not NULL, then this area is part of the keyboard focus chain. |
86 | Area* next_object_to_key_focus_area_; |
87 | |
88 | + |
89 | +#ifdef NUX_GESTURES_SUPPORT |
90 | + //! Returns the InputArea hit by the given gesture |
91 | + virtual Area* GetInputAreaHitByGesture(const GestureEvent &event); |
92 | + |
93 | + //! Returns whether the gesture from the given event is fully inside this area. |
94 | + /*! |
95 | + For getures from direct devices (e.g. touchscreens), it checks whether all |
96 | + touch points lie within this area's boundaries. For gestures from indirect |
97 | + devices (e.g. touchpads) it checks whether the gesture's focus point lies |
98 | + inside this area. |
99 | + */ |
100 | + bool IsGestureInsideArea(const GestureEvent &event) const; |
101 | +#endif // NUX_GESTURES_SUPPORT |
102 | + |
103 | private: |
104 | void ReconfigureParentLayout(Area *child = 0); |
105 | void CheckMinSize(); |
106 | |
107 | === modified file 'Nux/BaseWindow.cpp' |
108 | --- Nux/BaseWindow.cpp 2012-01-18 19:51:33 +0000 |
109 | +++ Nux/BaseWindow.cpp 2012-08-01 18:42:21 +0000 |
110 | @@ -511,4 +511,27 @@ |
111 | { |
112 | return accept_key_nav_focus_; |
113 | } |
114 | + |
115 | +#ifdef NUX_GESTURES_SUPPORT |
116 | + Area* BaseWindow::GetInputAreaHitByGesture(const nux::GestureEvent &event) |
117 | + { |
118 | + if (!IsVisible()) |
119 | + return nullptr; |
120 | + |
121 | + if (!IsGestureInsideArea(event)) |
122 | + return nullptr; |
123 | + |
124 | + if (m_layout) |
125 | + { |
126 | + Area *area_hit = m_layout->GetInputAreaHitByGesture(event); |
127 | + if (area_hit) |
128 | + return area_hit; |
129 | + } |
130 | + |
131 | + if (HasSubscriptionForGesture(event)) |
132 | + return this; |
133 | + else |
134 | + return nullptr; |
135 | + } |
136 | +#endif |
137 | } |
138 | |
139 | === modified file 'Nux/BaseWindow.h' |
140 | --- Nux/BaseWindow.h 2012-01-17 20:15:44 +0000 |
141 | +++ Nux/BaseWindow.h 2012-08-01 18:42:21 +0000 |
142 | @@ -26,6 +26,7 @@ |
143 | |
144 | #include <boost/scoped_ptr.hpp> |
145 | #include "ScrollView.h" |
146 | +#include "Features.h" |
147 | |
148 | #if defined(NUX_OS_WINDOWS) |
149 | #include "NuxGraphics/Events.h" |
150 | @@ -46,6 +47,10 @@ |
151 | class HLayout; |
152 | class PopUpWindow; |
153 | |
154 | +#ifdef NUX_GESTURES_SUPPORT |
155 | + class GestureEvent; |
156 | +#endif |
157 | + |
158 | //typedef TopView BaseWindow; |
159 | |
160 | /*! |
161 | @@ -186,6 +191,11 @@ |
162 | { |
163 | return _name; |
164 | } |
165 | + |
166 | +#ifdef NUX_GESTURES_SUPPORT |
167 | + virtual Area* GetInputAreaHitByGesture(const nux::GestureEvent &event); |
168 | +#endif |
169 | + |
170 | protected: |
171 | |
172 | void SetAcceptKeyNavFocus(bool accept); |
173 | |
174 | === added file 'Nux/Features.h.in' |
175 | --- Nux/Features.h.in 1970-01-01 00:00:00 +0000 |
176 | +++ Nux/Features.h.in 2012-08-01 18:42:21 +0000 |
177 | @@ -0,0 +1,14 @@ |
178 | +/* This file specifies which optional features are available on this specific |
179 | + build of NUX libraries |
180 | + |
181 | + Generated from Features.h.in by configure. |
182 | + */ |
183 | + |
184 | +#ifndef NUX_FEATURES_H |
185 | +#define NUX_FEATURES_H |
186 | + |
187 | +// Whether NUX was built with gestures support. I.e., whether the gestures |
188 | +// API is available. |
189 | +#define @NUX_GESTURES_SUPPORT@ |
190 | + |
191 | +#endif |
192 | |
193 | === added file 'Nux/GeisAdapter.cpp' |
194 | --- Nux/GeisAdapter.cpp 1970-01-01 00:00:00 +0000 |
195 | +++ Nux/GeisAdapter.cpp 2012-08-01 18:42:21 +0000 |
196 | @@ -0,0 +1,528 @@ |
197 | +/* |
198 | + * Copyright 2012 Canonical Ltd. |
199 | + * |
200 | + * This program is free software: you can redistribute it and/or modify it |
201 | + * under the terms of the GNU Lesser General Public License, as |
202 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
203 | + * of the License. |
204 | + * |
205 | + * This program is distributed in the hope that it will be useful, but |
206 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
207 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
208 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
209 | + * License for more details. |
210 | + * |
211 | + * You should have received a copy of both the GNU Lesser General Public |
212 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
213 | + * |
214 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
215 | + * |
216 | + */ |
217 | + |
218 | +#include <NuxCore/Logger.h> |
219 | +#include <string.h> |
220 | +#include "GeisAdapter.h" |
221 | + |
222 | +#if (defined(NUX_OS_LINUX) || defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) && (!defined(NUX_DISABLE_GLIB_LOOP)) |
223 | +#define NUX_USE_GLIB_LOOP |
224 | +#endif |
225 | + |
226 | +#define GEIS_CLASS_UNREF(c) if (c) {geis_gesture_class_unref(c); c = nullptr;} |
227 | + |
228 | +using namespace nux; |
229 | + |
230 | +namespace |
231 | +{ |
232 | + nux::logging::Logger logger("nux.geisadapter"); |
233 | + |
234 | +#ifdef NUX_USE_GLIB_LOOP |
235 | + struct GeisAdapterEventSource |
236 | + { |
237 | + GSource source; |
238 | + GPollFD event_poll_fd; |
239 | + }; |
240 | + |
241 | + static gboolean geis_source_prepare(GSource *source, gint *timeout) |
242 | + { |
243 | + *timeout = -1; |
244 | + return FALSE; |
245 | + } |
246 | + |
247 | + static gboolean geis_source_check(GSource *source) |
248 | + { |
249 | + gboolean retval; |
250 | + GeisAdapterEventSource *event_source = reinterpret_cast<GeisAdapterEventSource*>(source); |
251 | + |
252 | + if ((event_source->event_poll_fd.revents & G_IO_IN)) |
253 | + { |
254 | + retval = TRUE; |
255 | + } |
256 | + else |
257 | + { |
258 | + retval = FALSE; |
259 | + } |
260 | + |
261 | + return retval; |
262 | + } |
263 | + |
264 | + gboolean geis_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) |
265 | + { |
266 | + GeisAdapter *geis_adapter = NUX_STATIC_CAST(GeisAdapter*, user_data); |
267 | + geis_adapter->ProcessGeisEvents(); |
268 | + return TRUE; |
269 | + } |
270 | + |
271 | + static GSourceFuncs geis_source_funcs = |
272 | + { |
273 | + geis_source_prepare, |
274 | + geis_source_check, |
275 | + geis_source_dispatch, |
276 | + NULL |
277 | + }; |
278 | +#endif |
279 | +} |
280 | + |
281 | +GeisAdapter::GeisAdapter() : |
282 | + geis_(nullptr) |
283 | + , class_drag_(nullptr) |
284 | + , class_pinch_(nullptr) |
285 | + , class_rotate_(nullptr) |
286 | + , class_tap_(nullptr) |
287 | + , class_touch_(nullptr) |
288 | + , is_init_complete_(false) |
289 | +{ |
290 | + geis_ = geis_new(GEIS_INIT_NO_ATOMIC_GESTURES, nullptr); |
291 | + if (!geis_) |
292 | + { |
293 | + LOG_WARNING(logger) << "Couldn't create a Geis instance." |
294 | + " Multitouch gesture support disabled."; |
295 | + } |
296 | +} |
297 | + |
298 | +GeisAdapter::~GeisAdapter() |
299 | +{ |
300 | + GEIS_CLASS_UNREF(class_drag_) |
301 | + GEIS_CLASS_UNREF(class_pinch_) |
302 | + GEIS_CLASS_UNREF(class_rotate_) |
303 | + GEIS_CLASS_UNREF(class_tap_) |
304 | + GEIS_CLASS_UNREF(class_touch_) |
305 | + geis_delete(geis_); |
306 | + |
307 | +#ifdef NUX_USE_GLIB_LOOP |
308 | + g_source_remove_by_funcs_user_data(&geis_source_funcs, this); |
309 | +#endif |
310 | +} |
311 | + |
312 | +#ifdef NUX_USE_GLIB_LOOP |
313 | +void GeisAdapter::CreateGSource(GMainContext *context) |
314 | +{ |
315 | + GSource *source = g_source_new(&geis_source_funcs, |
316 | + sizeof(GeisAdapterEventSource)); |
317 | + GeisAdapterEventSource *event_source = reinterpret_cast<GeisAdapterEventSource*>(source); |
318 | + |
319 | + g_source_set_priority(source, G_PRIORITY_DEFAULT); |
320 | + |
321 | + GeisStatus status = geis_get_configuration(geis_, GEIS_CONFIGURATION_FD, |
322 | + &event_source->event_poll_fd.fd); |
323 | + if (status != GEIS_STATUS_SUCCESS) |
324 | + { |
325 | + LOG_WARNING(logger) << "Failed to get geis file descriptor." |
326 | + " Multitouch gesture support disabled."; |
327 | + g_source_destroy(source); |
328 | + return; |
329 | + } |
330 | + |
331 | + event_source->event_poll_fd.events = G_IO_IN; |
332 | + |
333 | + g_source_add_poll(source, &event_source->event_poll_fd); |
334 | + g_source_set_callback(source, 0, this, 0); |
335 | + |
336 | + g_source_attach(source, context); |
337 | +} |
338 | +#endif // NUX_USE_GLIB_LOOP |
339 | + |
340 | +void GeisAdapter::ProcessGeisEvents() |
341 | +{ |
342 | + GeisEvent event = nullptr; |
343 | + GeisStatus status = GEIS_STATUS_UNKNOWN_ERROR; |
344 | + GestureEvent nux_event; |
345 | + |
346 | + status = geis_dispatch_events(geis_); |
347 | + if (status != GEIS_STATUS_SUCCESS) |
348 | + { |
349 | + LOG_WARNING(logger) << "Failed to dispatch events! Status: " << status; |
350 | + } |
351 | + |
352 | + status = geis_next_event(geis_, &event); |
353 | + if (status != GEIS_STATUS_SUCCESS && status != GEIS_STATUS_CONTINUE |
354 | + && status != GEIS_STATUS_EMPTY) |
355 | + { |
356 | + LOG_WARNING(logger) << "Failed to get next event! Status: " << status; |
357 | + } |
358 | + |
359 | + while (status == GEIS_STATUS_CONTINUE || status == GEIS_STATUS_SUCCESS) |
360 | + { |
361 | + switch (geis_event_type(event)) |
362 | + { |
363 | + case GEIS_EVENT_GESTURE_BEGIN: |
364 | + FillNuxEvent(nux_event, event, EVENT_GESTURE_BEGIN); |
365 | + event_ready.emit(nux_event); |
366 | + break; |
367 | + case GEIS_EVENT_GESTURE_UPDATE: |
368 | + FillNuxEvent(nux_event, event, EVENT_GESTURE_UPDATE); |
369 | + if (nux_event.GetGestureClasses() == TAP_GESTURE) |
370 | + { |
371 | + // Geis sends a single Update event for a tap gesture and nothing more, |
372 | + // but it's better to be consistent with the rule that all gestures |
373 | + // must begin and end (with any number of updates in between). |
374 | + // Otherwise code in upper layers will have to add special cases just |
375 | + // for the tap gesture. |
376 | + nuxAssert(!pending_next_event_); |
377 | + SplitUpdateIntoBeginAndEnd(nux_event); |
378 | + nuxAssert(pending_next_event_); |
379 | + event_ready.emit(nux_event); |
380 | + event_ready.emit(*pending_next_event_); |
381 | + pending_next_event_.reset(); |
382 | + } |
383 | + else |
384 | + event_ready.emit(nux_event); |
385 | + break; |
386 | + case GEIS_EVENT_GESTURE_END: |
387 | + FillNuxEvent(nux_event, event, EVENT_GESTURE_END); |
388 | + event_ready.emit(nux_event); |
389 | + break; |
390 | + case GEIS_EVENT_CLASS_AVAILABLE: |
391 | + this->ProcessEventClassAvailable(event); |
392 | + break; |
393 | + case GEIS_EVENT_DEVICE_AVAILABLE: |
394 | + this->ProcessEventDeviceAvailable(event); |
395 | + break; |
396 | + case GEIS_EVENT_DEVICE_UNAVAILABLE: |
397 | + this->ProcessEventDeviceUnavailable(event); |
398 | + break; |
399 | + case GEIS_EVENT_INIT_COMPLETE: |
400 | + this->is_init_complete_ = true; |
401 | + this->init_complete.emit(); |
402 | + break; |
403 | + default: |
404 | + break; |
405 | + } |
406 | + geis_event_delete(event); |
407 | + status = geis_next_event(this->geis_, &event); |
408 | + } |
409 | +} |
410 | + |
411 | +void GeisAdapter::SplitUpdateIntoBeginAndEnd(GestureEvent &nux_event) |
412 | +{ |
413 | + nux_event.type = EVENT_GESTURE_BEGIN; |
414 | + |
415 | + // Work around a bug in geis. A very quick gesture (e.g. a quick tap) |
416 | + // will end with its is_construction_finished still set to false. |
417 | + // https://bugs.launchpad.net/grail/+bug/1012315 |
418 | + nux_event.is_construction_finished_ = true; |
419 | + |
420 | + pending_next_event_.reset(new GestureEvent); |
421 | + *pending_next_event_ = nux_event; |
422 | + pending_next_event_->type = EVENT_GESTURE_END; |
423 | +} |
424 | + |
425 | +bool GeisAdapter::ProcessNextEvent(GestureEvent *nux_event) |
426 | +{ |
427 | + if (pending_next_event_) |
428 | + { |
429 | + *nux_event = *pending_next_event_; |
430 | + pending_next_event_.reset(); |
431 | + return true; |
432 | + } |
433 | + |
434 | + GeisEvent event = nullptr; |
435 | + GeisStatus status = GEIS_STATUS_UNKNOWN_ERROR; |
436 | + bool filled_nux_event = false; |
437 | + |
438 | + status = geis_dispatch_events(geis_); |
439 | + if (status != GEIS_STATUS_SUCCESS) |
440 | + { |
441 | + LOG_WARNING(logger) << "Failed to dispatch events! Status: " << status; |
442 | + } |
443 | + |
444 | + status = geis_next_event(geis_, &event); |
445 | + if (status != GEIS_STATUS_SUCCESS && status != GEIS_STATUS_CONTINUE |
446 | + && status != GEIS_STATUS_EMPTY) |
447 | + { |
448 | + LOG_WARNING(logger) << "Failed to get next event! Status: " << status; |
449 | + } |
450 | + |
451 | + if (status == GEIS_STATUS_CONTINUE || status == GEIS_STATUS_SUCCESS) |
452 | + { |
453 | + switch (geis_event_type(event)) |
454 | + { |
455 | + case GEIS_EVENT_GESTURE_BEGIN: |
456 | + FillNuxEvent(*nux_event, event, EVENT_GESTURE_BEGIN); |
457 | + filled_nux_event = true; |
458 | + break; |
459 | + case GEIS_EVENT_GESTURE_UPDATE: |
460 | + FillNuxEvent(*nux_event, event, EVENT_GESTURE_UPDATE); |
461 | + if (nux_event->GetGestureClasses() == TAP_GESTURE) |
462 | + { |
463 | + // Geis sends a single Update event for a tap gesture and nothing more, |
464 | + // but it's better to be consistent with the rule that all gestures |
465 | + // must begin and end (with any number of updates in between). |
466 | + // Otherwise code in upper layers will have to add special cases just |
467 | + // for the tap gesture. |
468 | + SplitUpdateIntoBeginAndEnd(*nux_event); |
469 | + } |
470 | + filled_nux_event = true; |
471 | + break; |
472 | + case GEIS_EVENT_GESTURE_END: |
473 | + FillNuxEvent(*nux_event, event, EVENT_GESTURE_END); |
474 | + filled_nux_event = true; |
475 | + break; |
476 | + case GEIS_EVENT_CLASS_AVAILABLE: |
477 | + this->ProcessEventClassAvailable(event); |
478 | + break; |
479 | + case GEIS_EVENT_DEVICE_AVAILABLE: |
480 | + this->ProcessEventDeviceAvailable(event); |
481 | + break; |
482 | + case GEIS_EVENT_DEVICE_UNAVAILABLE: |
483 | + this->ProcessEventDeviceUnavailable(event); |
484 | + break; |
485 | + case GEIS_EVENT_INIT_COMPLETE: |
486 | + this->is_init_complete_ = true; |
487 | + this->init_complete.emit(); |
488 | + break; |
489 | + default: |
490 | + break; |
491 | + } |
492 | + geis_event_delete(event); |
493 | + } |
494 | + |
495 | + if (!filled_nux_event) |
496 | + nux_event->Reset(); |
497 | + |
498 | + return filled_nux_event; |
499 | +} |
500 | + |
501 | +void GeisAdapter::ProcessEventDeviceAvailable(GeisEvent event) |
502 | +{ |
503 | + GeisAttr attr = geis_event_attr_by_name(event, GEIS_EVENT_ATTRIBUTE_DEVICE); |
504 | + if (!attr) |
505 | + { |
506 | + LOG_WARNING(logger) << |
507 | + "Missing device attr in \"Device Available\" event."; |
508 | + return; |
509 | + } |
510 | + |
511 | + GeisDevice geis_device = static_cast<GeisDevice>(geis_attr_value_to_pointer(attr)); |
512 | + if (!geis_device) |
513 | + { |
514 | + LOG_WARNING(logger) << "\"Device Available\" event contains a null device"; |
515 | + return; |
516 | + } |
517 | + |
518 | + Device device; |
519 | + |
520 | + attr = geis_device_attr_by_name(geis_device, GEIS_DEVICE_ATTRIBUTE_DIRECT_TOUCH); |
521 | + if (!attr) |
522 | + { |
523 | + LOG_WARNING(logger) << "Missing \"direct touch\" attr in device."; |
524 | + return; |
525 | + } |
526 | + device.direct_touch = geis_attr_value_to_boolean(attr) == GEIS_TRUE; |
527 | + |
528 | + device.id = geis_device_id(geis_device); |
529 | + devices_[device.id] = device; |
530 | +} |
531 | + |
532 | +void GeisAdapter::ProcessEventDeviceUnavailable(GeisEvent event) |
533 | +{ |
534 | + GeisAttr attr = geis_event_attr_by_name(event, GEIS_EVENT_ATTRIBUTE_DEVICE); |
535 | + if (!attr) |
536 | + { |
537 | + LOG_WARNING(logger) << |
538 | + "Missing device attr in \"Device Available\" event."; |
539 | + return; |
540 | + } |
541 | + |
542 | + GeisDevice geis_device = static_cast<GeisDevice>(geis_attr_value_to_pointer(attr)); |
543 | + if (!geis_device) |
544 | + { |
545 | + LOG_WARNING(logger) << "\"Device Available\" event contains a null device"; |
546 | + return; |
547 | + } |
548 | + |
549 | + devices_.erase(geis_device_id(geis_device)); |
550 | +} |
551 | + |
552 | +void GeisAdapter::FillNuxEvent(GestureEvent &nux_event, |
553 | + GeisEvent event, |
554 | + EventType nux_event_type) |
555 | +{ |
556 | + GeisAttr attr = geis_event_attr_by_name(event, GEIS_EVENT_ATTRIBUTE_GROUPSET); |
557 | + GeisGroupSet group_set = |
558 | + static_cast<GeisGroupSet>(geis_attr_value_to_pointer(attr)); |
559 | + |
560 | + /* I expect only one group. */ |
561 | + if (geis_groupset_group_count(group_set) != 1) |
562 | + { |
563 | + LOG_WARNING(logger) << "Received a gesture event with " |
564 | + << geis_groupset_group_count(group_set) << " groups"; |
565 | + return; |
566 | + } |
567 | + |
568 | + GeisGroup group = geis_groupset_group(group_set, 0); |
569 | + |
570 | + /* I expect only one frame. */ |
571 | + if (geis_group_frame_count(group) != 1) |
572 | + { |
573 | + LOG_WARNING(logger) << "Received a gesture event with a group containing " |
574 | + << geis_group_frame_count(group) << " frames"; |
575 | + return; |
576 | + } |
577 | + |
578 | + nux_event.Reset(); |
579 | + nux_event.type = nux_event_type; |
580 | + nux_event.geis_ = geis_; |
581 | + nux_event.geis_group_ = group; |
582 | + |
583 | + GeisFrame frame = geis_group_frame(group, 0); |
584 | + FillNuxEventGestureAttributes(nux_event, frame); |
585 | + |
586 | + attr = geis_event_attr_by_name(event, GEIS_EVENT_ATTRIBUTE_CONSTRUCTION_FINISHED); |
587 | + nux_event.is_construction_finished_ = geis_attr_value_to_boolean(attr) == GEIS_TRUE; |
588 | + |
589 | + attr = geis_event_attr_by_name(event, GEIS_EVENT_ATTRIBUTE_TOUCHSET); |
590 | + GeisTouchSet touch_set = |
591 | + static_cast<GeisTouchSet>(geis_attr_value_to_pointer(attr)); |
592 | + FillNuxEventTouches(nux_event, touch_set); |
593 | + |
594 | + // OBS: A gesture may belong to more than one gesture class |
595 | + if (class_drag_ && geis_frame_is_class(frame, class_drag_)) |
596 | + { |
597 | + nux_event.gesture_classes_ |= DRAG_GESTURE; |
598 | + } |
599 | + if (class_pinch_ && geis_frame_is_class(frame, class_pinch_)) |
600 | + { |
601 | + nux_event.gesture_classes_ |= PINCH_GESTURE; |
602 | + } |
603 | + if (class_rotate_ && geis_frame_is_class(frame, class_rotate_)) |
604 | + { |
605 | + nux_event.gesture_classes_ |= ROTATE_GESTURE; |
606 | + } |
607 | + if (class_tap_ && geis_frame_is_class(frame, class_tap_)) |
608 | + { |
609 | + nux_event.gesture_classes_ |= TAP_GESTURE; |
610 | + } |
611 | + if (class_touch_ && geis_frame_is_class(frame, class_touch_)) |
612 | + { |
613 | + nux_event.gesture_classes_ |= TOUCH_GESTURE; |
614 | + } |
615 | + |
616 | + // Work around a bug in geis. A very quick gesture (e.g. a quick tap) |
617 | + // will end with its is_construction_finished still set to false. |
618 | + // https://bugs.launchpad.net/grail/+bug/1012315 |
619 | + if (nux_event_type == EVENT_GESTURE_END) |
620 | + nux_event.is_construction_finished_ = true; |
621 | +} |
622 | + |
623 | +void GeisAdapter::FillNuxEventGestureAttributes(GestureEvent &nux_event, GeisFrame frame) |
624 | +{ |
625 | + GeisSize attr_count = geis_frame_attr_count(frame); |
626 | + GeisAttr attr = 0; |
627 | + |
628 | + nux_event.gesture_id_ = geis_frame_id(frame); |
629 | + |
630 | + // Idea: use a hash mapping string attribute names to their correponding |
631 | + // addresses in the GestureEvent class. |
632 | + for (GeisSize i = 0; i < attr_count; ++i) |
633 | + { |
634 | + attr = geis_frame_attr(frame, i); |
635 | + if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_DEVICE_ID)) |
636 | + { |
637 | + int device_id = geis_attr_value_to_integer(attr); |
638 | + nux_event.is_direct_touch_ = devices_[device_id].direct_touch; |
639 | + } |
640 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_TIMESTAMP)) |
641 | + nux_event.timestamp_ = geis_attr_value_to_integer(attr); |
642 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_FOCUS_X)) |
643 | + nux_event.focus_.x = geis_attr_value_to_float(attr); |
644 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_FOCUS_Y)) |
645 | + nux_event.focus_.y = geis_attr_value_to_float(attr); |
646 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_DELTA_X)) |
647 | + nux_event.delta_.x = geis_attr_value_to_float(attr); |
648 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_DELTA_Y)) |
649 | + nux_event.delta_.y = geis_attr_value_to_float(attr); |
650 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_ANGLE)) |
651 | + nux_event.angle_ = geis_attr_value_to_float(attr); |
652 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_ANGLE_DELTA)) |
653 | + nux_event.angle_delta_ = geis_attr_value_to_float(attr); |
654 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_ANGULAR_VELOCITY)) |
655 | + nux_event.angular_velocity_ = geis_attr_value_to_float(attr); |
656 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_TAP_TIME)) |
657 | + nux_event.tap_duration_ = geis_attr_value_to_integer(attr); |
658 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_VELOCITY_X)) |
659 | + nux_event.velocity_.x = geis_attr_value_to_float(attr); |
660 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_VELOCITY_Y)) |
661 | + nux_event.velocity_.y = geis_attr_value_to_float(attr); |
662 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_RADIUS)) |
663 | + nux_event.radius_ = geis_attr_value_to_float(attr); |
664 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_RADIUS_DELTA)) |
665 | + nux_event.radius_delta_ = geis_attr_value_to_float(attr); |
666 | + else if (g_str_equal(geis_attr_name(attr), GEIS_GESTURE_ATTRIBUTE_RADIAL_VELOCITY)) |
667 | + nux_event.radial_velocity_ = geis_attr_value_to_float(attr); |
668 | + } |
669 | +} |
670 | + |
671 | +void GeisAdapter::FillNuxEventTouches(GestureEvent &nux_event, GeisTouchSet touch_set) |
672 | +{ |
673 | + GeisSize count = geis_touchset_touch_count(touch_set); |
674 | + |
675 | + TouchPoint point; |
676 | + for (GeisSize i = 0; i < count; ++i) |
677 | + { |
678 | + GeisTouch geis_touch = geis_touchset_touch(touch_set, i); |
679 | + |
680 | + point.id = geis_attr_value_to_integer( |
681 | + geis_touch_attr_by_name(geis_touch, GEIS_TOUCH_ATTRIBUTE_ID)); |
682 | + point.x = geis_attr_value_to_float( |
683 | + geis_touch_attr_by_name(geis_touch, GEIS_TOUCH_ATTRIBUTE_X)); |
684 | + point.y = geis_attr_value_to_float( |
685 | + geis_touch_attr_by_name(geis_touch, GEIS_TOUCH_ATTRIBUTE_Y)); |
686 | + |
687 | + nux_event.touches_.push_back(point); |
688 | + } |
689 | +} |
690 | + |
691 | +void GeisAdapter::ProcessEventClassAvailable(GeisEvent event) |
692 | +{ |
693 | + GeisGestureClass gesture_class; |
694 | + GeisAttr attr; |
695 | + |
696 | + attr = geis_event_attr_by_name(event, GEIS_EVENT_ATTRIBUTE_CLASS); |
697 | + gesture_class = static_cast<GeisGestureClass>(geis_attr_value_to_pointer(attr)); |
698 | + |
699 | + if (strcmp(GEIS_GESTURE_DRAG, geis_gesture_class_name(gesture_class)) == 0) |
700 | + { |
701 | + class_drag_ = gesture_class; |
702 | + geis_gesture_class_ref(gesture_class); |
703 | + } |
704 | + else if (strcmp(GEIS_GESTURE_PINCH, geis_gesture_class_name(gesture_class)) == 0) |
705 | + { |
706 | + class_pinch_ = gesture_class; |
707 | + geis_gesture_class_ref(gesture_class); |
708 | + } |
709 | + else if (strcmp(GEIS_GESTURE_ROTATE, geis_gesture_class_name(gesture_class)) == 0) |
710 | + { |
711 | + class_rotate_ = gesture_class; |
712 | + geis_gesture_class_ref(gesture_class); |
713 | + } |
714 | + else if (strcmp(GEIS_GESTURE_TAP, geis_gesture_class_name(gesture_class)) == 0) |
715 | + { |
716 | + class_tap_ = gesture_class; |
717 | + geis_gesture_class_ref(gesture_class); |
718 | + } |
719 | + else if (strcmp(GEIS_GESTURE_TOUCH, geis_gesture_class_name(gesture_class)) == 0) |
720 | + { |
721 | + class_touch_ = gesture_class; |
722 | + geis_gesture_class_ref(gesture_class); |
723 | + } |
724 | +} |
725 | |
726 | === added file 'Nux/GeisAdapter.h' |
727 | --- Nux/GeisAdapter.h 1970-01-01 00:00:00 +0000 |
728 | +++ Nux/GeisAdapter.h 2012-08-01 18:42:21 +0000 |
729 | @@ -0,0 +1,122 @@ |
730 | +/* |
731 | + * Copyright 2012 Canonical Ltd. |
732 | + * |
733 | + * This program is free software: you can redistribute it and/or modify it |
734 | + * under the terms of the GNU Lesser General Public License, as |
735 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
736 | + * of the License. |
737 | + * |
738 | + * This program is distributed in the hope that it will be useful, but |
739 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
740 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
741 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
742 | + * License for more details. |
743 | + * |
744 | + * You should have received a copy of both the GNU Lesser General Public |
745 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
746 | + * |
747 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
748 | + * |
749 | + */ |
750 | + |
751 | +#ifndef GEISADAPTER_H |
752 | +#define GEISADAPTER_H |
753 | +#include "Features.h" |
754 | +#ifdef NUX_GESTURES_SUPPORT |
755 | + |
756 | +#include <sigc++/sigc++.h> |
757 | +#include <geis/geis.h> |
758 | +#include <glib.h> |
759 | + |
760 | +#include <map> |
761 | +#include <memory> |
762 | + |
763 | +#include "NuxGraphics/GestureEvent.h" |
764 | + |
765 | +namespace nux |
766 | +{ |
767 | + |
768 | +/*! |
769 | + This class translates events from geis into native nux::GestureEvents |
770 | + */ |
771 | +class GeisAdapter : public sigc::trackable |
772 | +{ |
773 | +public: |
774 | + GeisAdapter(); |
775 | + ~GeisAdapter(); |
776 | + |
777 | +#if (defined(NUX_OS_LINUX) || defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) && (!defined(NUX_DISABLE_GLIB_LOOP)) |
778 | + void CreateGSource(GMainContext *context); |
779 | +#endif |
780 | + |
781 | + /* |
782 | + Returns whether initialization is complete. |
783 | + See also: signal init_complete() |
784 | + */ |
785 | + bool IsInitComplete() const {return is_init_complete_;} |
786 | + |
787 | + /* |
788 | + Initialization has finished. Subscriptions |
789 | + can now be filtered and activated. |
790 | + */ |
791 | + sigc::signal<void> init_complete; |
792 | + |
793 | + //! Emitted when a new GestureEvent is ready to be processed |
794 | + sigc::signal<void, GestureEvent &> event_ready; |
795 | + |
796 | + Geis GetGeisInstance() const {return geis_;} |
797 | + |
798 | + void ProcessGeisEvents(); |
799 | + |
800 | + //! Processes the next pending Geis event, which may yield a GestureEvent |
801 | + /*! |
802 | + Returns true if GestureEvent was filled and false otherwise. |
803 | + */ |
804 | + bool ProcessNextEvent(GestureEvent *event); |
805 | + |
806 | +private: |
807 | + |
808 | + class Device { |
809 | + public: |
810 | + int id; |
811 | + bool direct_touch; |
812 | + }; |
813 | + |
814 | + |
815 | + void ProcessEventDeviceAvailable(GeisEvent event); |
816 | + void ProcessEventDeviceUnavailable(GeisEvent event); |
817 | + void ProcessEventClassAvailable(GeisEvent event); |
818 | + void FillNuxEvent(GestureEvent &nux_event, |
819 | + GeisEvent event, |
820 | + EventType nux_event_type); |
821 | + |
822 | + void FillNuxEventGestureAttributes(GestureEvent &nux_event, GeisFrame frame); |
823 | + void FillNuxEventTouches(GestureEvent &nux_event, GeisTouchSet touch_set); |
824 | + |
825 | + void SplitUpdateIntoBeginAndEnd(GestureEvent &nux_event); |
826 | + |
827 | + Geis geis_; |
828 | + |
829 | + GeisGestureClass class_drag_; |
830 | + GeisGestureClass class_pinch_; |
831 | + GeisGestureClass class_rotate_; |
832 | + GeisGestureClass class_tap_; |
833 | + GeisGestureClass class_touch_; |
834 | + |
835 | + bool is_init_complete_; |
836 | + |
837 | + /* maps a device id to its corresponding device */ |
838 | + std::map<int, Device> devices_; |
839 | + |
840 | + /* Sometimes a single GeisEvent can yield two GestureEvents. When that |
841 | + happens, that second GestureEvent will be temporarily held here until |
842 | + it's consumed. */ |
843 | + std::unique_ptr<GestureEvent> pending_next_event_; |
844 | + |
845 | + friend class GeisAdapterTest; |
846 | +}; |
847 | + |
848 | +} // namespace nux |
849 | + |
850 | +#endif // NUX_GESTURES_SUPPORT |
851 | +#endif // GEISADAPTER_H |
852 | |
853 | === added file 'Nux/Gesture.cpp' |
854 | --- Nux/Gesture.cpp 1970-01-01 00:00:00 +0000 |
855 | +++ Nux/Gesture.cpp 2012-08-01 18:42:21 +0000 |
856 | @@ -0,0 +1,295 @@ |
857 | +/* |
858 | + * Copyright (C) 2012 - Canonical Ltd. |
859 | + * |
860 | + * This program is free software: you can redistribute it and/or modify it |
861 | + * under the terms of the GNU Lesser General Public License, as |
862 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
863 | + * of the License. |
864 | + * |
865 | + * This program is distributed in the hope that it will be useful, but |
866 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
867 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
868 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
869 | + * License for more details. |
870 | + * |
871 | + * You should have received a copy of both the GNU Lesser General Public |
872 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
873 | + * |
874 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
875 | + */ |
876 | + |
877 | +#include "Gesture.h" |
878 | +#include "Nux/InputArea.h" |
879 | + |
880 | +using namespace nux; |
881 | + |
882 | +/***************************************************************************** |
883 | + * InputAreaTarget |
884 | + *****************************************************************************/ |
885 | + |
886 | +InputAreaTarget::InputAreaTarget(InputArea *input_area) |
887 | + : input_area_(input_area) |
888 | +{ |
889 | +} |
890 | + |
891 | +GestureDeliveryRequest InputAreaTarget::GestureEvent(const nux::GestureEvent &event) |
892 | +{ |
893 | + if (input_area_.IsValid()) |
894 | + return input_area_->GestureEvent(event); |
895 | + else |
896 | + return GestureDeliveryRequest::NONE; |
897 | +} |
898 | + |
899 | +bool InputAreaTarget::Equals(const GestureTarget& other) const |
900 | +{ |
901 | + const InputAreaTarget *input_area_target = dynamic_cast<const InputAreaTarget *>(&other); |
902 | + |
903 | + if (input_area_target) |
904 | + { |
905 | + return input_area_ == input_area_target->input_area_; |
906 | + } |
907 | + else |
908 | + { |
909 | + return false; |
910 | + } |
911 | +} |
912 | + |
913 | +/***************************************************************************** |
914 | + * Gesture |
915 | + *****************************************************************************/ |
916 | + |
917 | +Gesture::Gesture(const nux::GestureEvent &event) |
918 | + : event_delivery_enabled_(false), |
919 | + acceptance_status_(AcceptanceStatus::UNDECIDED) |
920 | +{ |
921 | + queued_events_.reserve(20); |
922 | + queued_events_.push_back(event); |
923 | +} |
924 | + |
925 | +void Gesture::AddTarget(ShPtGestureTarget target) |
926 | +{ |
927 | + target_list_.push_back(target); |
928 | +} |
929 | + |
930 | +void Gesture::RemoveTarget(ShPtGestureTarget target) |
931 | +{ |
932 | + auto check_same_target = [&](const ShPtGestureTarget& other_target) |
933 | + { |
934 | + return *other_target == *target; |
935 | + }; |
936 | + |
937 | + target_list_.remove_if(check_same_target); |
938 | +} |
939 | + |
940 | +void Gesture::EnableEventDelivery() |
941 | +{ |
942 | + if (event_delivery_enabled_) |
943 | + return; |
944 | + |
945 | + event_delivery_enabled_ = true; |
946 | + |
947 | + if (queued_events_.size() == 0) |
948 | + return; |
949 | + |
950 | + // Deliver all queued events but keep the last one |
951 | + last_event_ = queued_events_[queued_events_.size()-1]; |
952 | + for (auto event : queued_events_) |
953 | + { |
954 | + DeliverEvent(event); |
955 | + } |
956 | + queued_events_.clear(); |
957 | +} |
958 | + |
959 | +void Gesture::Update(const nux::GestureEvent& event) |
960 | +{ |
961 | + if (event_delivery_enabled_) |
962 | + { |
963 | + DeliverEvent(event); |
964 | + last_event_ = event; |
965 | + } |
966 | + else |
967 | + { |
968 | + queued_events_.push_back(event); |
969 | + } |
970 | +} |
971 | + |
972 | +void Gesture::DeliverEvent(const GestureEvent &event) |
973 | +{ |
974 | + auto it = target_list_.begin(); |
975 | + while (it != target_list_.end()) |
976 | + { |
977 | + GestureDeliveryRequest request = (*it)->GestureEvent(event); |
978 | + |
979 | + switch(request) |
980 | + { |
981 | + case GestureDeliveryRequest::EXCLUSIVITY: |
982 | + ExecuteTargetExclusivityRequest(event, it); |
983 | + break; |
984 | + |
985 | + default: // NONE |
986 | + ++it; |
987 | + } |
988 | + } |
989 | +} |
990 | + |
991 | +void Gesture::ExecuteTargetExclusivityRequest(const GestureEvent &event, |
992 | + std::list<ShPtGestureTarget>::iterator &it_requestor) |
993 | +{ |
994 | + |
995 | + // Don't send gesture lost events for a gesture begin. |
996 | + // Targets can't lose a gesture they never knew about. |
997 | + if (event.type != EVENT_GESTURE_BEGIN) |
998 | + { |
999 | + GestureEvent event_lost = event; |
1000 | + event_lost.type = EVENT_GESTURE_LOST; |
1001 | + auto other_it = target_list_.rbegin(); |
1002 | + while (*other_it != *it_requestor) |
1003 | + { |
1004 | + (*other_it)->GestureEvent(event_lost); |
1005 | + ++other_it; |
1006 | + } |
1007 | + } |
1008 | + |
1009 | + ++it_requestor; |
1010 | + target_list_.erase(it_requestor, target_list_.end()); |
1011 | + it_requestor = target_list_.end(); |
1012 | +} |
1013 | + |
1014 | +nux::GestureEvent &Gesture::GetLatestEvent() |
1015 | +{ |
1016 | + if (event_delivery_enabled_) |
1017 | + { |
1018 | + nuxAssert(queued_events_.size() == 0); |
1019 | + return last_event_; |
1020 | + } |
1021 | + else |
1022 | + { |
1023 | + nuxAssert(queued_events_.size() > 0); |
1024 | + return queued_events_[queued_events_.size()-1]; |
1025 | + } |
1026 | +} |
1027 | + |
1028 | +const nux::GestureEvent &Gesture::GetLatestEvent() const |
1029 | +{ |
1030 | + if (event_delivery_enabled_) |
1031 | + { |
1032 | + nuxAssert(queued_events_.size() == 0); |
1033 | + return last_event_; |
1034 | + } |
1035 | + else |
1036 | + { |
1037 | + nuxAssert(queued_events_.size() > 0); |
1038 | + return queued_events_[queued_events_.size()-1]; |
1039 | + } |
1040 | +} |
1041 | + |
1042 | +bool Gesture::IsConstructionFinished() const |
1043 | +{ |
1044 | + return GetLatestEvent().IsConstructionFinished(); |
1045 | +} |
1046 | + |
1047 | +int Gesture::GetId() const |
1048 | +{ |
1049 | + return GetLatestEvent().GetGestureId(); |
1050 | +} |
1051 | + |
1052 | +const std::vector<TouchPoint> &Gesture::GetTouches() const |
1053 | +{ |
1054 | + return GetLatestEvent().GetTouches(); |
1055 | +} |
1056 | + |
1057 | +bool Gesture::HasTouchesInCommon( |
1058 | + const std::shared_ptr<Gesture> &other_gesture) const |
1059 | +{ |
1060 | + const std::vector<TouchPoint> &my_touches = GetTouches(); |
1061 | + const std::vector<TouchPoint> &other_touches = other_gesture->GetTouches(); |
1062 | + |
1063 | + for (const auto other_touch : other_touches) |
1064 | + { |
1065 | + for (const auto my_touch : my_touches) |
1066 | + { |
1067 | + if (other_touch == my_touch) |
1068 | + return true; |
1069 | + } |
1070 | + } |
1071 | + |
1072 | + return false; |
1073 | +} |
1074 | + |
1075 | +void Gesture::Reject() |
1076 | +{ |
1077 | + g_assert(acceptance_status_ == AcceptanceStatus::UNDECIDED); |
1078 | + GetLatestEvent().Reject(); |
1079 | + acceptance_status_ = AcceptanceStatus::REJECTED; |
1080 | +} |
1081 | + |
1082 | +void Gesture::Accept() |
1083 | +{ |
1084 | + g_assert(acceptance_status_ == AcceptanceStatus::UNDECIDED); |
1085 | + GetLatestEvent().Accept(); |
1086 | + acceptance_status_ = AcceptanceStatus::ACCEPTED; |
1087 | +} |
1088 | + |
1089 | +/***************************************************************************** |
1090 | + * GestureSet |
1091 | + *****************************************************************************/ |
1092 | + |
1093 | +void GestureSet::Add(Gesture *gesture) |
1094 | +{ |
1095 | + map_id_to_gesture_[gesture->GetId()] = std::shared_ptr<Gesture>(gesture); |
1096 | +} |
1097 | + |
1098 | +void GestureSet::Add(std::shared_ptr<Gesture> &gesture) |
1099 | +{ |
1100 | + map_id_to_gesture_[gesture->GetId()] = gesture; |
1101 | +} |
1102 | + |
1103 | +std::shared_ptr<Gesture> GestureSet::FindFromGestureId(int gesture_id) |
1104 | +{ |
1105 | + std::map<int, std::shared_ptr<Gesture> >::iterator it |
1106 | + = map_id_to_gesture_.find(gesture_id); |
1107 | + |
1108 | + if (it != map_id_to_gesture_.end()) |
1109 | + return it->second; |
1110 | + else |
1111 | + return std::shared_ptr<Gesture>(); |
1112 | +} |
1113 | + |
1114 | +std::shared_ptr<Gesture> GestureSet::FindFromTarget(ShPtGestureTarget wanted_target) |
1115 | +{ |
1116 | + for (auto it : map_id_to_gesture_) |
1117 | + { |
1118 | + std::shared_ptr<Gesture> &gesture = it.second; |
1119 | + const std::list<ShPtGestureTarget> &target_list = gesture->GetTargetList(); |
1120 | + for (auto target : target_list) |
1121 | + { |
1122 | + if (*target == *wanted_target) |
1123 | + return gesture; |
1124 | + } |
1125 | + } |
1126 | + return nullptr; |
1127 | +} |
1128 | + |
1129 | +std::vector< std::shared_ptr<Gesture> > |
1130 | + GestureSet::GetConflictingGestures(std::shared_ptr<Gesture> &gesture) |
1131 | +{ |
1132 | + std::vector< std::shared_ptr<Gesture> > conflicting_gestures; |
1133 | + |
1134 | + for (auto it : map_id_to_gesture_) |
1135 | + { |
1136 | + std::shared_ptr<Gesture> &other_gesture = it.second; |
1137 | + |
1138 | + if (other_gesture == gesture) |
1139 | + continue; |
1140 | + |
1141 | + if (other_gesture->HasTouchesInCommon(gesture)) |
1142 | + conflicting_gestures.push_back(other_gesture); |
1143 | + } |
1144 | + |
1145 | + return conflicting_gestures; |
1146 | +} |
1147 | + |
1148 | +void GestureSet::Remove(std::shared_ptr<Gesture> &gesture) |
1149 | +{ |
1150 | + map_id_to_gesture_.erase(gesture->GetId()); |
1151 | +} |
1152 | |
1153 | === added file 'Nux/Gesture.h' |
1154 | --- Nux/Gesture.h 1970-01-01 00:00:00 +0000 |
1155 | +++ Nux/Gesture.h 2012-08-01 18:42:21 +0000 |
1156 | @@ -0,0 +1,170 @@ |
1157 | +/* |
1158 | + * Copyright (C) 2012 - Canonical Ltd. |
1159 | + * |
1160 | + * This program is free software: you can redistribute it and/or modify it |
1161 | + * under the terms of the GNU Lesser General Public License, as |
1162 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
1163 | + * of the License. |
1164 | + * |
1165 | + * This program is distributed in the hope that it will be useful, but |
1166 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1167 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
1168 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
1169 | + * License for more details. |
1170 | + * |
1171 | + * You should have received a copy of both the GNU Lesser General Public |
1172 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
1173 | + * |
1174 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
1175 | + */ |
1176 | + |
1177 | +#ifndef NUX_GESTURE_H |
1178 | +#define NUX_GESTURE_H |
1179 | +#include "Features.h" |
1180 | +#ifdef NUX_GESTURES_SUPPORT |
1181 | + |
1182 | +#include <memory> |
1183 | + |
1184 | +#include "NuxGraphics/GestureEvent.h" |
1185 | + |
1186 | +/* |
1187 | + A collection of helper classes used by WindowCompositor for the task of |
1188 | + delivering GestureEvents to their correct target InputAreas and in the |
1189 | + decision of whether a gesture should be accepted or rejected. |
1190 | + */ |
1191 | +namespace nux |
1192 | +{ |
1193 | +class InputArea; |
1194 | + |
1195 | +/* |
1196 | + Interface for gesture targets. |
1197 | + */ |
1198 | +class GestureTarget |
1199 | +{ |
1200 | + public: |
1201 | + |
1202 | + /*! |
1203 | + Called whenever there's a new gesture event for this target. |
1204 | + \param event GestureEvent to be processed by the target. |
1205 | + \return A request about the delivery of events for the related gesture. |
1206 | + */ |
1207 | + virtual GestureDeliveryRequest GestureEvent(const GestureEvent &event) = 0; |
1208 | + |
1209 | + bool operator ==(const GestureTarget& other) const |
1210 | + { |
1211 | + return Equals(other); |
1212 | + } |
1213 | + |
1214 | + private: |
1215 | + /*! |
1216 | + For some types of target, different instances may wrap the same actual target, |
1217 | + in which case reimplementing this method is necessary. |
1218 | + */ |
1219 | + virtual bool Equals(const GestureTarget& other) const |
1220 | + { |
1221 | + return this == &other; |
1222 | + } |
1223 | +}; |
1224 | +typedef std::shared_ptr<GestureTarget> ShPtGestureTarget; |
1225 | + |
1226 | +class InputAreaTarget : public GestureTarget |
1227 | +{ |
1228 | + public: |
1229 | + InputAreaTarget(InputArea *input_area); |
1230 | + virtual GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event); |
1231 | + |
1232 | + private: |
1233 | + virtual bool Equals(const GestureTarget& other) const; |
1234 | + ObjectWeakPtr<InputArea> input_area_; |
1235 | +}; |
1236 | + |
1237 | +//! A class that relates a multitouch gesture to its target entity |
1238 | +/*! |
1239 | + It relates a gesture to its target, which can be either a window or |
1240 | + unity. |
1241 | + |
1242 | + It's fed with GestureEvents via Update(). |
1243 | + It stores those GestureEvents in a queue until EnableEventDelivery() is |
1244 | + called, when all queued events are finally acted upon. After |
1245 | + that, further events fed via Update() will have an immediate effect over its |
1246 | + target instead of being queued. |
1247 | + */ |
1248 | +class Gesture |
1249 | +{ |
1250 | + public: |
1251 | + |
1252 | + Gesture(const GestureEvent &event); |
1253 | + |
1254 | + void AddTarget(ShPtGestureTarget target); |
1255 | + void RemoveTarget(ShPtGestureTarget target); |
1256 | + const std::list<ShPtGestureTarget> &GetTargetList() const {return target_list_;} |
1257 | + |
1258 | + void EnableEventDelivery(); |
1259 | + void Update(const GestureEvent& event); |
1260 | + bool IsConstructionFinished() const; |
1261 | + bool IsDeliveringEvents() const {return event_delivery_enabled_;} |
1262 | + int GetId() const; |
1263 | + const std::vector<TouchPoint> &GetTouches() const; |
1264 | + |
1265 | + //! Returns whether the given gesture has any touches in common with this one. |
1266 | + bool HasTouchesInCommon(const std::shared_ptr<Gesture> &other_gesture) const; |
1267 | + |
1268 | + //! Rejects the gesture. |
1269 | + /* |
1270 | + After rejection a gesture is no longer valid |
1271 | + */ |
1272 | + void Reject(); |
1273 | + |
1274 | + //! Accepts the gesture |
1275 | + void Accept(); |
1276 | + |
1277 | + enum class AcceptanceStatus |
1278 | + { |
1279 | + UNDECIDED, |
1280 | + ACCEPTED, |
1281 | + REJECTED |
1282 | + }; |
1283 | + |
1284 | + AcceptanceStatus GetAcceptanceStatus() const {return acceptance_status_;} |
1285 | + |
1286 | + private: |
1287 | + const GestureEvent &GetLatestEvent() const; |
1288 | + GestureEvent &GetLatestEvent(); |
1289 | + void DeliverEvent(const GestureEvent &event); |
1290 | + void ExecuteTargetExclusivityRequest(const GestureEvent &event, |
1291 | + std::list<ShPtGestureTarget>::iterator &it_requestor); |
1292 | + |
1293 | + std::list<ShPtGestureTarget> target_list_; |
1294 | + |
1295 | + // events that are waiting to be delivered |
1296 | + std::vector<GestureEvent> queued_events_; |
1297 | + |
1298 | + // last event delivered |
1299 | + GestureEvent last_event_; |
1300 | + |
1301 | + bool event_delivery_enabled_; |
1302 | + AcceptanceStatus acceptance_status_; |
1303 | +}; |
1304 | + |
1305 | +/* |
1306 | + Stores information on all curently active gestures. |
1307 | + */ |
1308 | +class GestureSet |
1309 | +{ |
1310 | + public: |
1311 | + void Add(Gesture *gesture); |
1312 | + void Add(std::shared_ptr<Gesture> &gesture); |
1313 | + std::shared_ptr<Gesture> FindFromGestureId(int gesture_id); |
1314 | + std::shared_ptr<Gesture> FindFromTarget(ShPtGestureTarget target); |
1315 | + void Remove(std::shared_ptr<Gesture> &gesture); |
1316 | + |
1317 | + std::vector< std::shared_ptr<Gesture> > |
1318 | + GetConflictingGestures(std::shared_ptr<Gesture> &gesture); |
1319 | + |
1320 | + private: |
1321 | + std::map<int, std::shared_ptr<Gesture> > map_id_to_gesture_; |
1322 | +}; |
1323 | + |
1324 | +} // namespace nux |
1325 | +#endif // NUX_GESTURES_SUPPORT |
1326 | +#endif // NUX_GESTURE_H |
1327 | |
1328 | === added file 'Nux/GestureBroker.cpp' |
1329 | --- Nux/GestureBroker.cpp 1970-01-01 00:00:00 +0000 |
1330 | +++ Nux/GestureBroker.cpp 2012-08-01 18:42:21 +0000 |
1331 | @@ -0,0 +1,200 @@ |
1332 | +/* |
1333 | + * Copyright (C) 2012 - Canonical Ltd. |
1334 | + * |
1335 | + * This program is free software: you can redistribute it and/or modify it |
1336 | + * under the terms of the GNU Lesser General Public License, as |
1337 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
1338 | + * of the License. |
1339 | + * |
1340 | + * This program is distributed in the hope that it will be useful, but |
1341 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1342 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
1343 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
1344 | + * License for more details. |
1345 | + * |
1346 | + * You should have received a copy of both the GNU Lesser General Public |
1347 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
1348 | + * |
1349 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
1350 | + */ |
1351 | + |
1352 | +#include "GestureBroker.h" |
1353 | +#include "Gesture.h" |
1354 | + |
1355 | +using namespace nux; |
1356 | + |
1357 | +GestureBroker::GestureBroker() |
1358 | +{ |
1359 | +} |
1360 | + |
1361 | +GestureBroker::~GestureBroker() |
1362 | +{ |
1363 | +} |
1364 | + |
1365 | +void GestureBroker::ProcessGestureBegin(nux::GestureEvent &event) |
1366 | +{ |
1367 | + std::vector<ShPtGestureTarget> target_list = FindGestureTargets(event); |
1368 | + |
1369 | + bool gesture_bound_to_target = false; |
1370 | + for (auto target : target_list) |
1371 | + { |
1372 | + bool ok = BindNewGestureToTarget(event, target); |
1373 | + gesture_bound_to_target |= ok; |
1374 | + } |
1375 | + |
1376 | + if (!gesture_bound_to_target) |
1377 | + event.Reject(); |
1378 | +} |
1379 | + |
1380 | +bool GestureBroker::BindNewGestureToTarget(nux::GestureEvent &event, ShPtGestureTarget target) |
1381 | +{ |
1382 | + bool successful; |
1383 | + std::shared_ptr<Gesture> existing_gesture = |
1384 | + gesture_set_.FindFromTarget(target); |
1385 | + |
1386 | + auto create_gesture_for_target = [&]() |
1387 | + { |
1388 | + std::shared_ptr<Gesture> gesture = |
1389 | + gesture_set_.FindFromGestureId(event.GetGestureId()); |
1390 | + |
1391 | + if (!gesture) |
1392 | + gesture = std::shared_ptr<Gesture>(new Gesture(event)); |
1393 | + |
1394 | + gesture->AddTarget(target); |
1395 | + gesture_set_.Add(gesture); |
1396 | + |
1397 | + if (event.IsConstructionFinished()) |
1398 | + { |
1399 | + gesture->Accept(); |
1400 | + gesture->EnableEventDelivery(); |
1401 | + } |
1402 | + }; |
1403 | + |
1404 | + auto remove_target_from_existing_gesture = [&]() |
1405 | + { |
1406 | + existing_gesture->RemoveTarget(target); |
1407 | + if (existing_gesture->GetTargetList().size() == 0) |
1408 | + { |
1409 | + existing_gesture->Reject(); |
1410 | + gesture_set_.Remove(existing_gesture); |
1411 | + } |
1412 | + }; |
1413 | + |
1414 | + if (existing_gesture) |
1415 | + { |
1416 | + // There's a conflict here. |
1417 | + // The target for this new gesture already has a gesture. |
1418 | + |
1419 | + if (existing_gesture->IsDeliveringEvents()) |
1420 | + { |
1421 | + // A target can have only one gesture at a time. |
1422 | + successful = false; |
1423 | + } |
1424 | + else |
1425 | + { |
1426 | + // Since the existing gesture hasn't been delivered to the target yet, |
1427 | + // we can choose which will continue. |
1428 | + // The rule is that the gesture with the most touches has precedence over |
1429 | + // the other. |
1430 | + int existing_num_touches = existing_gesture->GetTouches().size(); |
1431 | + int new_num_touches = event.GetTouches().size(); |
1432 | + if (new_num_touches < existing_num_touches) |
1433 | + { |
1434 | + successful = false; |
1435 | + } |
1436 | + else if (new_num_touches == existing_num_touches) |
1437 | + { |
1438 | + // It means that there are more fingers on the target area |
1439 | + // than the target area can handle |
1440 | + // (i.e. three fingers over an area that handles only two-fingers' |
1441 | + // gestures) |
1442 | + remove_target_from_existing_gesture(); |
1443 | + successful = false; |
1444 | + } |
1445 | + else // new_num_touches > existing_num_touches |
1446 | + { |
1447 | + remove_target_from_existing_gesture(); |
1448 | + create_gesture_for_target(); |
1449 | + successful = true; |
1450 | + } |
1451 | + } |
1452 | + } |
1453 | + else |
1454 | + { |
1455 | + create_gesture_for_target(); |
1456 | + successful = true; |
1457 | + } |
1458 | + |
1459 | + return successful; |
1460 | +} |
1461 | + |
1462 | +void GestureBroker::ProcessGestureUpdate(nux::GestureEvent &event) |
1463 | +{ |
1464 | + std::shared_ptr<Gesture> gesture = gesture_set_.FindFromGestureId(event.GetGestureId()); |
1465 | + if (!gesture) |
1466 | + { |
1467 | + return; |
1468 | + } |
1469 | + |
1470 | + if (event.IsConstructionFinished() && !gesture->IsConstructionFinished()) |
1471 | + { |
1472 | + // This is the first update for this gesture signaling that |
1473 | + // its construction has finished. |
1474 | + gesture->Update(event); |
1475 | + ResolveBufferedGestureThatFinishedConstruction(gesture); |
1476 | + } |
1477 | + else |
1478 | + { |
1479 | + gesture->Update(event); |
1480 | + } |
1481 | +} |
1482 | + |
1483 | +void GestureBroker::ProcessGestureEnd(nux::GestureEvent &event) |
1484 | +{ |
1485 | + std::shared_ptr<Gesture> gesture = gesture_set_.FindFromGestureId(event.GetGestureId()); |
1486 | + if (!gesture) |
1487 | + return; |
1488 | + |
1489 | + if (event.IsConstructionFinished() && !gesture->IsConstructionFinished()) |
1490 | + { |
1491 | + // This is the first update for this gesture signaling that |
1492 | + // its construction has finished. |
1493 | + gesture->Update(event); |
1494 | + ResolveBufferedGestureThatFinishedConstruction(gesture); |
1495 | + } |
1496 | + else |
1497 | + { |
1498 | + gesture->Update(event); |
1499 | + } |
1500 | + |
1501 | + // We no longer have to keep track of it. |
1502 | + gesture_set_.Remove(gesture); |
1503 | + |
1504 | + // We cannot leave a gesture behing without making a decision on its acceptance. |
1505 | + if (gesture->GetAcceptanceStatus() == Gesture::AcceptanceStatus::UNDECIDED) |
1506 | + { |
1507 | + // This will likely only happen if a geture is ending with its construction unfinished, |
1508 | + // which is an odd situation, but just in case... |
1509 | + gesture->Reject(); |
1510 | + } |
1511 | +} |
1512 | + |
1513 | +void GestureBroker::ResolveBufferedGestureThatFinishedConstruction( |
1514 | + std::shared_ptr<Gesture> &gesture) |
1515 | +{ |
1516 | + gesture->Accept(); |
1517 | + gesture->EnableEventDelivery(); // will flush all queued events |
1518 | + |
1519 | + std::vector< std::shared_ptr<Gesture> > conflicting_gestures = |
1520 | + gesture_set_.GetConflictingGestures(gesture); |
1521 | + if (conflicting_gestures.size() != 0) |
1522 | + { |
1523 | + // That shouldn't happen. All conflicting gestures should have been |
1524 | + // dealt with when they begun. |
1525 | + for (auto conflicting_gesture : conflicting_gestures) |
1526 | + { |
1527 | + conflicting_gesture->Reject(); |
1528 | + gesture_set_.Remove(conflicting_gesture); |
1529 | + } |
1530 | + } |
1531 | +} |
1532 | |
1533 | === added file 'Nux/GestureBroker.h' |
1534 | --- Nux/GestureBroker.h 1970-01-01 00:00:00 +0000 |
1535 | +++ Nux/GestureBroker.h 2012-08-01 18:42:21 +0000 |
1536 | @@ -0,0 +1,63 @@ |
1537 | +/* |
1538 | + * Copyright (C) 2012 - Canonical Ltd. |
1539 | + * |
1540 | + * This program is free software: you can redistribute it and/or modify it |
1541 | + * under the terms of the GNU Lesser General Public License, as |
1542 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
1543 | + * of the License. |
1544 | + * |
1545 | + * This program is distributed in the hope that it will be useful, but |
1546 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1547 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
1548 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
1549 | + * License for more details. |
1550 | + * |
1551 | + * You should have received a copy of both the GNU Lesser General Public |
1552 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
1553 | + * |
1554 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
1555 | + */ |
1556 | + |
1557 | +#ifndef NUX_GESTURE_BROKER_H |
1558 | +#define NUX_GESTURE_BROKER_H |
1559 | +#include "Features.h" |
1560 | +#ifdef NUX_GESTURES_SUPPORT |
1561 | + |
1562 | +#include <sigc++/sigc++.h> |
1563 | + |
1564 | +#include "Gesture.h" |
1565 | + |
1566 | +namespace nux |
1567 | +{ |
1568 | +/*! |
1569 | + Receives GestureEvents and delivers them to the appropriate |
1570 | + gesture targets. |
1571 | + */ |
1572 | +class GestureBroker : public sigc::trackable |
1573 | +{ |
1574 | +public: |
1575 | + GestureBroker(); |
1576 | + virtual ~GestureBroker(); |
1577 | + |
1578 | + void ProcessGestureBegin(nux::GestureEvent &event); |
1579 | + void ProcessGestureUpdate(nux::GestureEvent &event); |
1580 | + void ProcessGestureEnd(nux::GestureEvent &event); |
1581 | + |
1582 | +private: |
1583 | + |
1584 | + bool BindNewGestureToTarget(nux::GestureEvent &event, ShPtGestureTarget target); |
1585 | + |
1586 | + void ResolveBufferedGestureThatFinishedConstruction( |
1587 | + std::shared_ptr<Gesture> &gesture); |
1588 | + |
1589 | + /*! |
1590 | + Finds the targets of the gesture from the given gesture event. |
1591 | + */ |
1592 | + std::vector<ShPtGestureTarget> virtual FindGestureTargets(const nux::GestureEvent &event) = 0; |
1593 | + |
1594 | + GestureSet gesture_set_; |
1595 | +}; |
1596 | + |
1597 | +} // namespace nux |
1598 | +#endif // NUX_GESTURES_SUPPORT |
1599 | +#endif // NUX_GESTURE_BROKER_H |
1600 | |
1601 | === added file 'Nux/GesturesSubscription.cpp' |
1602 | --- Nux/GesturesSubscription.cpp 1970-01-01 00:00:00 +0000 |
1603 | +++ Nux/GesturesSubscription.cpp 2012-08-01 18:42:21 +0000 |
1604 | @@ -0,0 +1,379 @@ |
1605 | +/* |
1606 | + * Copyright 2012 Canonical Ltd. |
1607 | + * |
1608 | + * This program is free software: you can redistribute it and/or modify it |
1609 | + * under the terms of the GNU Lesser General Public License, as |
1610 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
1611 | + * of the License. |
1612 | + * |
1613 | + * This program is distributed in the hope that it will be useful, but |
1614 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1615 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
1616 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
1617 | + * License for more details. |
1618 | + * |
1619 | + * You should have received a copy of both the GNU Lesser General Public |
1620 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
1621 | + * |
1622 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
1623 | + * |
1624 | + */ |
1625 | + |
1626 | +#include "GesturesSubscription.h" |
1627 | +#include "Nux.h" |
1628 | +#include "NuxCore/Logger.h" |
1629 | +#include "Nux/GeisAdapter.h" |
1630 | + |
1631 | +namespace nux |
1632 | +{ |
1633 | + |
1634 | +namespace |
1635 | +{ |
1636 | + logging::Logger logger("nux.gestures_subscription"); |
1637 | +} |
1638 | + |
1639 | +GesturesSubscription::GesturesSubscription() |
1640 | + : gesture_classes_(DRAG_GESTURE|PINCH_GESTURE|ROTATE_GESTURE), |
1641 | + num_touches_(2), |
1642 | + window_id_(GetWindowThread()->GetGraphicsDisplay().GetWindowHandle()), |
1643 | + sub_(nullptr), |
1644 | + is_active_(false), |
1645 | + drag_threshold_(0.0026f), |
1646 | + drag_timeout_(0), |
1647 | + pinch_threshold_(1.1f), |
1648 | + pinch_timeout_(0), |
1649 | + rotate_threshold_(2.0f*3.1415926f/50.0f), |
1650 | + rotate_timeout_(0), |
1651 | + tap_threshold_(0.0026f), |
1652 | + tap_timeout_(300) |
1653 | +{ |
1654 | +} |
1655 | + |
1656 | +GesturesSubscription::~GesturesSubscription() |
1657 | +{ |
1658 | + if (sub_) |
1659 | + geis_subscription_delete(sub_); |
1660 | +} |
1661 | + |
1662 | +void GesturesSubscription::Activate() |
1663 | +{ |
1664 | + if (is_active_) |
1665 | + return; |
1666 | + |
1667 | + is_active_ = true; |
1668 | + |
1669 | + if (sub_) |
1670 | + { |
1671 | + GeisStatus status = geis_subscription_activate(sub_); |
1672 | + if (status != GEIS_STATUS_SUCCESS) |
1673 | + { |
1674 | + LOG_ERROR(logger) << "Failed to activate Geis subscription."; |
1675 | + } |
1676 | + // configuration only works on active subscription (should be fixed in geis) |
1677 | + ConfigureGeisSubscription(); |
1678 | + } |
1679 | + else |
1680 | + { |
1681 | + CreateGeisSubscriptionWhenPossible(); |
1682 | + } |
1683 | +} |
1684 | + |
1685 | +void GesturesSubscription::Deactivate() |
1686 | +{ |
1687 | + if (!is_active_) |
1688 | + return; |
1689 | + |
1690 | + is_active_ = false; |
1691 | + |
1692 | + if (sub_) |
1693 | + { |
1694 | + GeisStatus status = geis_subscription_deactivate(sub_); |
1695 | + if (status != GEIS_STATUS_SUCCESS) |
1696 | + { |
1697 | + LOG_ERROR(logger) << "Failed to deactivate Geis subscription."; |
1698 | + } |
1699 | + } |
1700 | +} |
1701 | + |
1702 | +void GesturesSubscription::SetGestureClasses(int gesture_classes) |
1703 | +{ |
1704 | + SetProperty(gesture_classes_, gesture_classes); |
1705 | + |
1706 | + #define NUX_ALL_GESTURES (DRAG_GESTURE|PINCH_GESTURE|TAP_GESTURE|TOUCH_GESTURE); |
1707 | + unwanted_gesture_classes_ = ~gesture_classes & NUX_ALL_GESTURES; |
1708 | + #undef NUX_ALL_GESTURES |
1709 | +} |
1710 | + |
1711 | +void GesturesSubscription::SetNumTouches(unsigned int num_touches) |
1712 | +{ |
1713 | + SetProperty(num_touches_, num_touches); |
1714 | +} |
1715 | + |
1716 | +void GesturesSubscription::SetWindowId(int window_id) |
1717 | +{ |
1718 | + SetProperty(window_id_, window_id); |
1719 | +} |
1720 | + |
1721 | +void GesturesSubscription::SetRecognitionThreshold(GestureClass gesture_class, |
1722 | + float threshold) |
1723 | +{ |
1724 | + if (threshold < 0.0f) |
1725 | + return; |
1726 | + |
1727 | + switch(gesture_class) |
1728 | + { |
1729 | + case DRAG_GESTURE: |
1730 | + SetProperty(drag_threshold_, threshold); |
1731 | + break; |
1732 | + case PINCH_GESTURE: |
1733 | + SetProperty(pinch_threshold_, threshold); |
1734 | + break; |
1735 | + case TAP_GESTURE: |
1736 | + SetProperty(tap_threshold_, threshold); |
1737 | + break; |
1738 | + case ROTATE_GESTURE: |
1739 | + SetProperty(rotate_threshold_, threshold); |
1740 | + break; |
1741 | + default: |
1742 | + break; |
1743 | + } |
1744 | +} |
1745 | + |
1746 | +void GesturesSubscription::SetRecognitionTimeout(GestureClass gesture_class, |
1747 | + int timeout) |
1748 | +{ |
1749 | + if (timeout < 0) |
1750 | + return; |
1751 | + |
1752 | + switch(gesture_class) |
1753 | + { |
1754 | + case DRAG_GESTURE: |
1755 | + SetProperty(drag_timeout_, timeout); |
1756 | + break; |
1757 | + case PINCH_GESTURE: |
1758 | + SetProperty(pinch_timeout_, timeout); |
1759 | + break; |
1760 | + case TAP_GESTURE: |
1761 | + SetProperty(tap_timeout_, timeout); |
1762 | + break; |
1763 | + case ROTATE_GESTURE: |
1764 | + SetProperty(rotate_timeout_, timeout); |
1765 | + break; |
1766 | + default: |
1767 | + break; |
1768 | + } |
1769 | +} |
1770 | + |
1771 | +std::vector<const char *> GesturesSubscription::CreateGeisGestureClasses() |
1772 | +{ |
1773 | + std::vector<const char *> geis_gesture_classes; |
1774 | + |
1775 | + #define ADD_GESTURE(name) \ |
1776 | + if (gesture_classes_ & name##_GESTURE) \ |
1777 | + geis_gesture_classes.push_back(GEIS_GESTURE_##name); |
1778 | + |
1779 | + ADD_GESTURE(DRAG) |
1780 | + ADD_GESTURE(PINCH) |
1781 | + ADD_GESTURE(ROTATE) |
1782 | + ADD_GESTURE(TAP) |
1783 | + ADD_GESTURE(TOUCH) |
1784 | + |
1785 | + #undef ADD_GESTURE |
1786 | + |
1787 | + return geis_gesture_classes; |
1788 | +} |
1789 | + |
1790 | +GeisStatus GesturesSubscription::AddGestureClassAndNumTouchesTerm(GeisFilter filter) |
1791 | +{ |
1792 | + GeisStatus status = GEIS_STATUS_UNKNOWN_ERROR; |
1793 | + std::vector<const char *> geis_gesture_classes = CreateGeisGestureClasses(); |
1794 | + |
1795 | + switch (geis_gesture_classes.size()) |
1796 | + { |
1797 | + case 1: |
1798 | + status = geis_filter_add_term(filter, |
1799 | + GEIS_FILTER_CLASS, |
1800 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[0], |
1801 | + GEIS_GESTURE_ATTRIBUTE_TOUCHES, GEIS_FILTER_OP_EQ, num_touches_, |
1802 | + nullptr); |
1803 | + break; |
1804 | + case 2: |
1805 | + status = geis_filter_add_term(filter, |
1806 | + GEIS_FILTER_CLASS, |
1807 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[0], |
1808 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[1], |
1809 | + GEIS_GESTURE_ATTRIBUTE_TOUCHES, GEIS_FILTER_OP_EQ, num_touches_, |
1810 | + nullptr); |
1811 | + break; |
1812 | + case 3: |
1813 | + status = geis_filter_add_term(filter, |
1814 | + GEIS_FILTER_CLASS, |
1815 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[0], |
1816 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[1], |
1817 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[2], |
1818 | + GEIS_GESTURE_ATTRIBUTE_TOUCHES, GEIS_FILTER_OP_EQ, num_touches_, |
1819 | + nullptr); |
1820 | + break; |
1821 | + case 4: |
1822 | + status = geis_filter_add_term(filter, |
1823 | + GEIS_FILTER_CLASS, |
1824 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[0], |
1825 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[1], |
1826 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[2], |
1827 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[3], |
1828 | + GEIS_GESTURE_ATTRIBUTE_TOUCHES, GEIS_FILTER_OP_EQ, num_touches_, |
1829 | + nullptr); |
1830 | + break; |
1831 | + case 5: |
1832 | + status = geis_filter_add_term(filter, |
1833 | + GEIS_FILTER_CLASS, |
1834 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[0], |
1835 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[1], |
1836 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[2], |
1837 | + GEIS_CLASS_ATTRIBUTE_NAME, GEIS_FILTER_OP_EQ, geis_gesture_classes[4], |
1838 | + GEIS_GESTURE_ATTRIBUTE_TOUCHES, GEIS_FILTER_OP_EQ, num_touches_, |
1839 | + nullptr); |
1840 | + break; |
1841 | + default: |
1842 | + status = geis_filter_add_term(filter, |
1843 | + GEIS_FILTER_CLASS, |
1844 | + GEIS_GESTURE_ATTRIBUTE_TOUCHES, GEIS_FILTER_OP_EQ, num_touches_, |
1845 | + nullptr); |
1846 | + break; |
1847 | + } |
1848 | + |
1849 | + return status; |
1850 | +} |
1851 | + |
1852 | +void GesturesSubscription::CreateGeisSubscription() |
1853 | +{ |
1854 | + GeisStatus status = GEIS_STATUS_UNKNOWN_ERROR; |
1855 | + GeisFilter filter = nullptr; |
1856 | + Geis geis = GetWindowThread()->GetGeisAdapter()->GetGeisInstance(); |
1857 | + |
1858 | + sub_ = geis_subscription_new(geis, "nux", GEIS_SUBSCRIPTION_NONE); |
1859 | + if (!sub_) |
1860 | + { |
1861 | + LOG_ERROR(logger) << "Failed to create Geis subscription."; |
1862 | + goto cleanup; |
1863 | + } |
1864 | + |
1865 | + filter = geis_filter_new(geis, "filter"); |
1866 | + if (!filter) |
1867 | + { |
1868 | + LOG_ERROR(logger) << "Failed to create Geis filter."; |
1869 | + goto cleanup; |
1870 | + } |
1871 | + |
1872 | + status = AddGestureClassAndNumTouchesTerm(filter); |
1873 | + if (status != GEIS_STATUS_SUCCESS) |
1874 | + { |
1875 | + LOG_ERROR(logger) << "Failed to add gesture term to Geis filter."; |
1876 | + goto cleanup; |
1877 | + } |
1878 | + |
1879 | + status = geis_filter_add_term(filter, |
1880 | + GEIS_FILTER_REGION, |
1881 | + GEIS_REGION_ATTRIBUTE_WINDOWID, GEIS_FILTER_OP_EQ, |
1882 | + window_id_, |
1883 | + nullptr); |
1884 | + if (status != GEIS_STATUS_SUCCESS) |
1885 | + { |
1886 | + LOG_ERROR(logger) << "Failed to add region term to Geis filter."; |
1887 | + goto cleanup; |
1888 | + } |
1889 | + |
1890 | + status = geis_subscription_add_filter(sub_, filter); |
1891 | + if (status != GEIS_STATUS_SUCCESS) |
1892 | + { |
1893 | + LOG_ERROR(logger) << "Failed to add filter to subscription."; |
1894 | + goto cleanup; |
1895 | + } |
1896 | + filter = nullptr; // it now belongs to the subscription |
1897 | + |
1898 | + if (is_active_) |
1899 | + { |
1900 | + status = geis_subscription_activate(sub_); |
1901 | + if (status != GEIS_STATUS_SUCCESS) |
1902 | + { |
1903 | + LOG_ERROR(logger) << "Failed to activate Geis subscription."; |
1904 | + goto cleanup; |
1905 | + } |
1906 | + // configuration only works on active subscription (should be fixed in geis) |
1907 | + ConfigureGeisSubscription(); |
1908 | + } |
1909 | + |
1910 | +cleanup: |
1911 | + if (status != GEIS_STATUS_SUCCESS) |
1912 | + { |
1913 | + geis_filter_delete(filter); |
1914 | + geis_subscription_delete(sub_); |
1915 | + sub_ = nullptr; |
1916 | + } |
1917 | +} |
1918 | + |
1919 | +void GesturesSubscription::CreateGeisSubscriptionWhenPossible() |
1920 | +{ |
1921 | + GeisAdapter *geis_adapter = GetWindowThread()->GetGeisAdapter(); |
1922 | + |
1923 | + if (geis_adapter->IsInitComplete()) |
1924 | + CreateGeisSubscription(); |
1925 | + else |
1926 | + geis_adapter->init_complete.connect( |
1927 | + sigc::mem_fun(this, &GesturesSubscription::CreateGeisSubscription)); |
1928 | +} |
1929 | + |
1930 | +void GesturesSubscription::UpdateGeisSubscription() |
1931 | +{ |
1932 | + nuxAssert(sub_ != nullptr); |
1933 | + |
1934 | + if (is_active_) |
1935 | + geis_subscription_deactivate(sub_); |
1936 | + |
1937 | + geis_subscription_delete(sub_); |
1938 | + |
1939 | + // Recreate the subscription only once it's active again |
1940 | + if (is_active_) |
1941 | + CreateGeisSubscription(); |
1942 | +} |
1943 | + |
1944 | +bool GesturesSubscription::MatchesGesture(const GestureEvent &event) const |
1945 | +{ |
1946 | + if (event.GetTouches().size() != num_touches_) |
1947 | + return false; |
1948 | + |
1949 | + if ((event.GetGestureClasses() & gesture_classes_) == 0) |
1950 | + return false; |
1951 | + |
1952 | + if ((event.GetGestureClasses() & unwanted_gesture_classes_) != 0) |
1953 | + return false; |
1954 | + |
1955 | + return true; |
1956 | +} |
1957 | + |
1958 | +void GesturesSubscription::ConfigureGeisSubscription() |
1959 | +{ |
1960 | + nuxAssert(sub_); |
1961 | + GeisStatus status; |
1962 | + |
1963 | + auto setGeisConfig = [&](const char *name, GeisPointer var) |
1964 | + { |
1965 | + status = geis_subscription_set_configuration(sub_, name, var); |
1966 | + if (status != GEIS_STATUS_SUCCESS) |
1967 | + { |
1968 | + LOG_ERROR(logger) << "Failed to set Geis subscription configuration " |
1969 | + << name; |
1970 | + } |
1971 | + }; |
1972 | + |
1973 | + setGeisConfig(GEIS_CONFIG_DRAG_THRESHOLD, &drag_threshold_); |
1974 | + setGeisConfig(GEIS_CONFIG_DRAG_TIMEOUT, &drag_timeout_); |
1975 | + setGeisConfig(GEIS_CONFIG_PINCH_THRESHOLD, &pinch_threshold_); |
1976 | + setGeisConfig(GEIS_CONFIG_PINCH_TIMEOUT, &pinch_timeout_); |
1977 | + setGeisConfig(GEIS_CONFIG_TAP_THRESHOLD, &tap_threshold_); |
1978 | + setGeisConfig(GEIS_CONFIG_TAP_TIMEOUT, &tap_timeout_); |
1979 | + setGeisConfig(GEIS_CONFIG_ROTATE_THRESHOLD, &rotate_threshold_); |
1980 | + setGeisConfig(GEIS_CONFIG_ROTATE_TIMEOUT, &rotate_timeout_); |
1981 | +} |
1982 | + |
1983 | +} // namespace nux |
1984 | |
1985 | === added file 'Nux/GesturesSubscription.h' |
1986 | --- Nux/GesturesSubscription.h 1970-01-01 00:00:00 +0000 |
1987 | +++ Nux/GesturesSubscription.h 2012-08-01 18:42:21 +0000 |
1988 | @@ -0,0 +1,181 @@ |
1989 | +/* |
1990 | + * Copyright 2012 Canonical Ltd. |
1991 | + * |
1992 | + * This program is free software: you can redistribute it and/or modify it |
1993 | + * under the terms of the GNU Lesser General Public License, as |
1994 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
1995 | + * of the License. |
1996 | + * |
1997 | + * This program is distributed in the hope that it will be useful, but |
1998 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1999 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
2000 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
2001 | + * License for more details. |
2002 | + * |
2003 | + * You should have received a copy of both the GNU Lesser General Public |
2004 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
2005 | + * |
2006 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
2007 | + * |
2008 | + */ |
2009 | + |
2010 | +#ifndef NUX_GESTURES_SUBSCRIPTION_H |
2011 | +#define NUX_GESTURES_SUBSCRIPTION_H |
2012 | +#include "Features.h" |
2013 | +#ifdef NUX_GESTURES_SUPPORT |
2014 | + |
2015 | +#include <geis/geis.h> |
2016 | +#include <memory> |
2017 | + |
2018 | +// For the GestureClass enumeration. |
2019 | +#include "NuxGraphics/GestureEvent.h" |
2020 | + |
2021 | +namespace nux |
2022 | +{ |
2023 | + //! Represents a subscription to multitouch gestures |
2024 | + /*! |
2025 | + Typical usage: |
2026 | + GesturesSubscription *subscription = new GesturesSubscription; |
2027 | + subscription->SetGestureClass(TAP_GESTURE); |
2028 | + subscription->SetNumTouches(2); |
2029 | + subscription->Activate(); |
2030 | + */ |
2031 | + /* |
2032 | + Wraps all the complexity involved in directly manipulating a GeisSubscription |
2033 | + and exposes a simpler, C++, API. |
2034 | + */ |
2035 | + class GesturesSubscription |
2036 | + { |
2037 | + public: |
2038 | + //! Creates a new, inactive, gestures subscription |
2039 | + GesturesSubscription(); |
2040 | + |
2041 | + //! Destroys this gestures subscription. |
2042 | + virtual ~GesturesSubscription(); |
2043 | + |
2044 | + //! Activates the subscription |
2045 | + void Activate(); |
2046 | + |
2047 | + //! Deactivates the subscription |
2048 | + void Deactivate(); |
2049 | + |
2050 | + //! Returns whether the subscription is active |
2051 | + /*! |
2052 | + Nux only get gesture events that matches active subscriptions. |
2053 | + |
2054 | + Subscriptions are inactive by default. |
2055 | + */ |
2056 | + bool IsActive() const {return is_active_;} |
2057 | + |
2058 | + //! Sets the classes of gestures this subscription is interested on |
2059 | + void SetGestureClasses(int gesture_classes); |
2060 | + |
2061 | + //! Returns the gesture class that this subscription is interested on |
2062 | + /*! |
2063 | + By default it's set to DRAG_GESTURE|PINCH_GESTURE|ROTATE_GESTURE |
2064 | + */ |
2065 | + int GetGestureClasses() const {return gesture_classes_;} |
2066 | + |
2067 | + //! The subscription will be interested in gestures with the given number of touches |
2068 | + void SetNumTouches(unsigned int num_touches); |
2069 | + |
2070 | + //! Returns the number of touches that this subscription is interested on |
2071 | + /*! |
2072 | + By default it is set to two touches. |
2073 | + */ |
2074 | + unsigned int GetNumTouches() const {return num_touches_;} |
2075 | + |
2076 | + //! Specifies the native window from which touch events will be gathered |
2077 | + /*! |
2078 | + See also GetWindowId() |
2079 | + */ |
2080 | + void SetWindowId(int window_id); |
2081 | + |
2082 | + //! Returns the native window id from which touch events are gathered. |
2083 | + /*! |
2084 | + By default this is the same native window that Nux uses for rendering. |
2085 | + But in cases they differ you should speficy the native window that will |
2086 | + provide the touch events with SetWindowId(). |
2087 | + */ |
2088 | + int GetWindowId() const {return window_id_;} |
2089 | + |
2090 | + //! Sets the recognition threshold for a given gesture class |
2091 | + /*! |
2092 | + For the pinch gesture class, the threshold is the current |
2093 | + radius divided by the initial radius and should be bigger than one. |
2094 | + Its default value is 1.1. |
2095 | + |
2096 | + For the drag gesture class, the threshold is in meters and represents |
2097 | + the minimum amount of movement required for a drag to be recognized. |
2098 | + Its default value is 0.0026 |
2099 | + |
2100 | + For the tap gesture class, the threshold is in meters and represents |
2101 | + the maximum amount of movement allowed for a tap to be recognized. |
2102 | + Its default value is 0.0026 |
2103 | + |
2104 | + For the rotate gesture class, the threshold is in radians and represents |
2105 | + the mimimum amout of rotation required for a rotation gesture to be |
2106 | + recognized. Its default value is (2*PI)/50. |
2107 | + |
2108 | + It has no effect or meaning for the touch gesture class. |
2109 | + */ |
2110 | + void SetRecognitionThreshold(GestureClass gesture_class, float threshold); |
2111 | + |
2112 | + //! Sets the recognition timeout for a given gesture class |
2113 | + /*! |
2114 | + The timeout is in milliseconds. |
2115 | + Setting 0 (zero) means that the recognition never times out. |
2116 | + |
2117 | + The default timeout for drag, pinch and rotate gesture classes is 0 (zero). |
2118 | + Fot taps it's 300. |
2119 | + |
2120 | + It has no effect or meaning for the touch gesture class. |
2121 | + */ |
2122 | + void SetRecognitionTimeout(GestureClass gesture_class, int timeout); |
2123 | + |
2124 | + //! Returns whether the gesture from the given event matches this subscription |
2125 | + bool MatchesGesture(const GestureEvent &event) const; |
2126 | + |
2127 | + private: |
2128 | + std::vector<const char *> CreateGeisGestureClasses(); |
2129 | + GeisStatus AddGestureClassAndNumTouchesTerm(GeisFilter filter); |
2130 | + void CreateGeisSubscription(); |
2131 | + void CreateGeisSubscriptionWhenPossible(); |
2132 | + void UpdateGeisSubscription(); |
2133 | + void ConfigureGeisSubscription(); |
2134 | + |
2135 | + template<typename T> |
2136 | + void SetProperty(T& prop, T new_value) |
2137 | + { |
2138 | + if (prop == new_value) |
2139 | + return; |
2140 | + |
2141 | + prop = new_value; |
2142 | + |
2143 | + if (sub_) |
2144 | + UpdateGeisSubscription(); |
2145 | + } |
2146 | + |
2147 | + int gesture_classes_; |
2148 | + int unwanted_gesture_classes_; |
2149 | + unsigned int num_touches_; |
2150 | + int window_id_; |
2151 | + GeisSubscription sub_; |
2152 | + bool is_active_; |
2153 | + |
2154 | + float drag_threshold_; |
2155 | + int drag_timeout_; |
2156 | + float pinch_threshold_; |
2157 | + int pinch_timeout_; |
2158 | + float rotate_threshold_; |
2159 | + int rotate_timeout_; |
2160 | + float tap_threshold_; |
2161 | + int tap_timeout_; |
2162 | + }; |
2163 | + |
2164 | + typedef std::shared_ptr<GesturesSubscription> ShGesturesSubscription; |
2165 | + |
2166 | +} // namespace nux |
2167 | + |
2168 | +#endif // NUX_GESTURES_SUPPORT |
2169 | +#endif // NUX_GESTURES_SUBSCRIPTION_H |
2170 | |
2171 | === modified file 'Nux/InputArea.cpp' |
2172 | --- Nux/InputArea.cpp 2012-02-19 05:41:42 +0000 |
2173 | +++ Nux/InputArea.cpp 2012-08-01 18:42:21 +0000 |
2174 | @@ -27,11 +27,16 @@ |
2175 | |
2176 | #include "NuxCore/Logger.h" |
2177 | |
2178 | +#include "Features.h" |
2179 | #include "Nux.h" |
2180 | #include "InputArea.h" |
2181 | #include "NuxGraphics/GraphicsEngine.h" |
2182 | #include "WindowCompositor.h" |
2183 | |
2184 | +#ifdef NUX_GESTURES_SUPPORT |
2185 | +#include "GesturesSubscription.h" |
2186 | +#endif |
2187 | + |
2188 | namespace nux |
2189 | { |
2190 | namespace { |
2191 | @@ -411,5 +416,76 @@ |
2192 | |
2193 | return true; |
2194 | } |
2195 | + |
2196 | +#ifdef NUX_GESTURES_SUPPORT |
2197 | + void InputArea::CreateGesturesSubscription(int gesture_classes, |
2198 | + unsigned int num_touches) |
2199 | + { |
2200 | + GesturesSubscription *subscription = new GesturesSubscription; |
2201 | + subscription->SetGestureClasses(gesture_classes); |
2202 | + subscription->SetNumTouches(num_touches); |
2203 | + subscription->Activate(); |
2204 | + |
2205 | + gestures_subscriptions_.push_back(ShGesturesSubscription(subscription)); |
2206 | + } |
2207 | + |
2208 | + void InputArea::AddGesturesSubscription( |
2209 | + std::shared_ptr<nux::GesturesSubscription> &subscription) |
2210 | + { |
2211 | + for (auto sub : gestures_subscriptions_) |
2212 | + { |
2213 | + if (sub.get() == subscription.get()) |
2214 | + return; |
2215 | + } |
2216 | + |
2217 | + gestures_subscriptions_.push_back(subscription); |
2218 | + } |
2219 | + |
2220 | + void InputArea::RemoveGesturesSubscription( |
2221 | + std::shared_ptr<nux::GesturesSubscription> &subscription) |
2222 | + { |
2223 | + std::list<std::shared_ptr<nux::GesturesSubscription> >::iterator it; |
2224 | + for (it = gestures_subscriptions_.begin(); |
2225 | + it != gestures_subscriptions_.end(); ++it) |
2226 | + { |
2227 | + if (it->get() == subscription.get()) |
2228 | + { |
2229 | + gestures_subscriptions_.erase(it); |
2230 | + return; |
2231 | + } |
2232 | + ++it; |
2233 | + } |
2234 | + } |
2235 | + |
2236 | + const std::list<ShGesturesSubscription>& |
2237 | + InputArea::GetGesturesSubscriptions() const |
2238 | + { |
2239 | + return gestures_subscriptions_; |
2240 | + } |
2241 | + |
2242 | + bool InputArea::HasSubscriptionForGesture(const nux::GestureEvent &event) const |
2243 | + { |
2244 | + for (const auto subscription : gestures_subscriptions_) |
2245 | + { |
2246 | + if (subscription->MatchesGesture(event)) |
2247 | + return true; |
2248 | + } |
2249 | + |
2250 | + return false; |
2251 | + } |
2252 | + |
2253 | + Area* InputArea::GetInputAreaHitByGesture(const nux::GestureEvent &event) |
2254 | + { |
2255 | + if (!IsVisible()) |
2256 | + return nullptr; |
2257 | + |
2258 | + if (!HasSubscriptionForGesture(event)) |
2259 | + return nullptr; |
2260 | + |
2261 | + if (!IsGestureInsideArea(event)) |
2262 | + return nullptr; |
2263 | + |
2264 | + return this; |
2265 | + } |
2266 | +#endif // NUX_GESTURES_SUPPORT |
2267 | } |
2268 | - |
2269 | |
2270 | === modified file 'Nux/InputArea.h' |
2271 | --- Nux/InputArea.h 2012-02-19 05:41:42 +0000 |
2272 | +++ Nux/InputArea.h 2012-08-01 18:42:21 +0000 |
2273 | @@ -23,6 +23,8 @@ |
2274 | #ifndef INPUTAREA_H |
2275 | #define INPUTAREA_H |
2276 | |
2277 | +#include "Features.h" |
2278 | + |
2279 | #include "Area.h" |
2280 | |
2281 | #if defined(NUX_OS_WINDOWS) |
2282 | @@ -32,8 +34,17 @@ |
2283 | #endif |
2284 | #include "NuxGraphics/GraphicsDisplay.h" |
2285 | |
2286 | +#ifdef NUX_GESTURES_SUPPORT |
2287 | +#include <list> |
2288 | +#include <memory> |
2289 | +#include "Nux/GesturesSubscription.h" |
2290 | +#endif |
2291 | + |
2292 | namespace nux |
2293 | { |
2294 | +#ifdef NUX_GESTURES_SUPPORT |
2295 | + class GestureEvent; |
2296 | +#endif |
2297 | class WindowCompositor; |
2298 | class InputArea; |
2299 | |
2300 | @@ -194,6 +205,52 @@ |
2301 | #endif |
2302 | |
2303 | public: |
2304 | + |
2305 | +#ifdef NUX_GESTURES_SUPPORT |
2306 | + //! Creates a new gesture subscription for this input area and activates it |
2307 | + /* |
2308 | + Convenience function. |
2309 | + It's the same as doing the following: |
2310 | + ShGesturesSubscription sub(new GesturesSubscription); |
2311 | + sub->SetGestureClasses(gesture_classes); |
2312 | + sub->SetNumTouches(num_touches); |
2313 | + sub->Activate(); |
2314 | + input_area->AddGesturesSubscription(sub); |
2315 | + */ |
2316 | + void CreateGesturesSubscription(int gesture_classes, |
2317 | + unsigned int num_touches); |
2318 | + |
2319 | + //! Adds a gestures subscription to this input area. |
2320 | + /*! |
2321 | + A single subscription can be shared among multiple InputAreas. |
2322 | + */ |
2323 | + void AddGesturesSubscription(ShGesturesSubscription &subscription); |
2324 | + |
2325 | + //! Removes a gestures subscription from this input area. |
2326 | + void RemoveGesturesSubscription(ShGesturesSubscription &subscription); |
2327 | + |
2328 | + //! Returns all multitouch gestures subscriptions that this area cares about. |
2329 | + /*! |
2330 | + An input area will receive GestureEvents for multitouch gestures that |
2331 | + hit his area and matches any active subscription present in this list. |
2332 | + */ |
2333 | + const std::list<ShGesturesSubscription> &GetGesturesSubscriptions() const; |
2334 | + |
2335 | + //! Returns whether this area has a subscription that matches the gesture event |
2336 | + bool HasSubscriptionForGesture(const GestureEvent &event) const; |
2337 | + |
2338 | + //! Returns the InputArea hit by the given gesture |
2339 | + /*! |
2340 | + If this area or any of its children is hit by the gesture from the given |
2341 | + event, that area will be returned. Otherwise, it returns nullptr. |
2342 | + */ |
2343 | + virtual Area* GetInputAreaHitByGesture(const GestureEvent &event); |
2344 | + |
2345 | + private: |
2346 | + std::list<ShGesturesSubscription> gestures_subscriptions_; |
2347 | +#endif // NUX_GESTURES_SUPPORT |
2348 | + |
2349 | + public: |
2350 | //! Signal emitted when the Mouse moves over the InputArea surface. |
2351 | sigc::signal<void, int, int, int, int, unsigned long, unsigned long> mouse_move; // send the current position inside the area |
2352 | |
2353 | @@ -310,6 +367,28 @@ |
2354 | sigc::signal<void, InputArea*> start_keyboard_grab; |
2355 | sigc::signal<void, InputArea*> end_keyboard_grab; |
2356 | |
2357 | +#ifdef NUX_GESTURES_SUPPORT |
2358 | + //! Called whenever a GestureEvent is received |
2359 | + /*! |
2360 | + You should reimplement this method to handle gestures. Typically you would |
2361 | + add code to make this area react to the gestures received. |
2362 | + |
2363 | + In order to receive events from a given kind of gesture, an area has to |
2364 | + have a corresponding gesture subscription. See CreateGesturesSubscription() |
2365 | + and AddGesturesSubscription(). |
2366 | + |
2367 | + This method is called only with gestures that have already |
2368 | + been accepted. Therefore you shouldn't call GestureEvent::Accept() |
2369 | + or GestureEvent::Reject() from here as there's no point in doing so. |
2370 | + |
2371 | + Default implementation just returns GestureDeliveryRequest::NONE. |
2372 | + */ |
2373 | + virtual GestureDeliveryRequest GestureEvent(const GestureEvent &event) |
2374 | + { |
2375 | + return GestureDeliveryRequest::NONE; |
2376 | + } |
2377 | +#endif |
2378 | + |
2379 | protected: |
2380 | |
2381 | virtual bool AcceptKeyNavFocus(); |
2382 | |
2383 | === modified file 'Nux/Layout.cpp' |
2384 | --- Nux/Layout.cpp 2011-11-25 04:59:29 +0000 |
2385 | +++ Nux/Layout.cpp 2012-08-01 18:42:21 +0000 |
2386 | @@ -599,4 +599,24 @@ |
2387 | { |
2388 | return false; |
2389 | } |
2390 | + |
2391 | +#ifdef NUX_GESTURES_SUPPORT |
2392 | + Area* Layout::GetInputAreaHitByGesture(const GestureEvent &event) |
2393 | + { |
2394 | + if (!IsVisible()) |
2395 | + return nullptr; |
2396 | + |
2397 | + if (!IsGestureInsideArea(event)) |
2398 | + return nullptr; |
2399 | + |
2400 | + for (const auto area : _layout_element_list) |
2401 | + { |
2402 | + Area *area_hit = area->GetInputAreaHitByGesture(event); |
2403 | + if (area_hit) |
2404 | + return area_hit; |
2405 | + } |
2406 | + |
2407 | + return nullptr; |
2408 | + } |
2409 | +#endif |
2410 | } |
2411 | |
2412 | === modified file 'Nux/Layout.h' |
2413 | --- Nux/Layout.h 2011-11-24 17:54:41 +0000 |
2414 | +++ Nux/Layout.h 2012-08-01 18:42:21 +0000 |
2415 | @@ -249,6 +249,10 @@ |
2416 | sigc::signal<void, Layout*, Area*> ViewAdded; |
2417 | sigc::signal<void, Layout*, Area*> ViewRemoved; |
2418 | |
2419 | +#ifdef NUX_GESTURES_SUPPORT |
2420 | + virtual Area* GetInputAreaHitByGesture(const GestureEvent &event); |
2421 | +#endif |
2422 | + |
2423 | protected: |
2424 | virtual bool AcceptKeyNavFocus(); |
2425 | |
2426 | |
2427 | === modified file 'Nux/MainLoopGLib.cpp' |
2428 | --- Nux/MainLoopGLib.cpp 2012-07-04 16:36:46 +0000 |
2429 | +++ Nux/MainLoopGLib.cpp 2012-08-01 18:42:21 +0000 |
2430 | @@ -1,3 +1,4 @@ |
2431 | +#include "Features.h" |
2432 | #include "Nux.h" |
2433 | #include "Layout.h" |
2434 | #include "NuxCore/Logger.h" |
2435 | @@ -61,7 +62,9 @@ |
2436 | } |
2437 | else |
2438 | { |
2439 | - return_code = dd->window_thread->ExecutionLoop(0); |
2440 | + Event event; |
2441 | + dd->window_thread->GetGraphicsDisplay().GetSystemEvent(&event); |
2442 | + return_code = dd->window_thread->ProcessEvent(event); |
2443 | } |
2444 | |
2445 | if ((return_code == 0) && !dd->window_thread->IsEmbeddedWindow()) |
2446 | @@ -125,7 +128,10 @@ |
2447 | { |
2448 | nux_glib_threads_lock(); |
2449 | WindowThread *window_thread = NUX_STATIC_CAST(WindowThread *, user_data); |
2450 | - unsigned int return_code = window_thread->ExecutionLoop(0); |
2451 | + |
2452 | + Event event; |
2453 | + window_thread->GetGraphicsDisplay().GetSystemEvent(&event); |
2454 | + unsigned int return_code = window_thread->ProcessEvent(event); |
2455 | |
2456 | if (return_code == 0 && !window_thread->IsEmbeddedWindow()) |
2457 | { |
2458 | @@ -256,6 +262,20 @@ |
2459 | else |
2460 | g_source_attach(source, main_loop_glib_context_); |
2461 | |
2462 | +#ifdef NUX_GESTURES_SUPPORT |
2463 | + //TODO: Some people say that it's much faster to share a GSource among many |
2464 | + // file descriptors than having a separate GSource for each file descriptor. |
2465 | + // See if it would be better to have GeisAdapter sharing the same GSource with |
2466 | + // WindowThread/Nux/X |
2467 | + if (IsEmbeddedWindow()) |
2468 | + geis_adapter_->CreateGSource(nullptr); |
2469 | + else |
2470 | + geis_adapter_->CreateGSource(main_loop_glib_context_); |
2471 | + |
2472 | + geis_adapter_->event_ready.connect( |
2473 | + sigc::mem_fun(this, &WindowThread::ProcessGestureEvent)); |
2474 | +#endif |
2475 | + |
2476 | if (_Timelines->size() > 0) |
2477 | StartMasterClock(); |
2478 | |
2479 | |
2480 | === modified file 'Nux/Makefile.am' |
2481 | --- Nux/Makefile.am 2012-05-25 20:36:09 +0000 |
2482 | +++ Nux/Makefile.am 2012-08-01 18:42:21 +0000 |
2483 | @@ -16,13 +16,15 @@ |
2484 | $(GCC_FLAGS) \ |
2485 | $(NUX_CFLAGS) \ |
2486 | $(IBUS_CFLAGS) \ |
2487 | + $(GEIS_CFLAGS) \ |
2488 | $(MAINTAINER_CFLAGS) |
2489 | |
2490 | libnux_@NUX_API_VERSION@_la_LIBADD = \ |
2491 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
2492 | $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \ |
2493 | $(NUX_LIBS) \ |
2494 | - $(IBUS_LIBS) |
2495 | + $(IBUS_LIBS) \ |
2496 | + $(GEIS_LIBS) |
2497 | |
2498 | libnux_@NUX_API_VERSION@_la_LDFLAGS = \ |
2499 | $(NUX_LT_LDFLAGS) |
2500 | @@ -112,6 +114,14 @@ |
2501 | $(srcdir)/WindowCompositor.cpp \ |
2502 | $(srcdir)/WindowThread.cpp |
2503 | |
2504 | +if HAVE_GEIS |
2505 | +source_cpp += \ |
2506 | + $(srcdir)/GeisAdapter.cpp \ |
2507 | + $(srcdir)/Gesture.cpp \ |
2508 | + $(srcdir)/GestureBroker.cpp \ |
2509 | + $(srcdir)/GesturesSubscription.cpp |
2510 | +endif |
2511 | + |
2512 | source_h = \ |
2513 | $(builddir)/ABI.h \ |
2514 | $(srcdir)/AbstractButton.h \ |
2515 | @@ -139,6 +149,7 @@ |
2516 | $(srcdir)/Dialog.h \ |
2517 | $(srcdir)/DoubleValidator.h \ |
2518 | $(srcdir)/EditTextBox.h \ |
2519 | + $(builddir)/Features.h \ |
2520 | $(srcdir)/FileSelector.h \ |
2521 | $(srcdir)/FloatingWindow.h \ |
2522 | $(srcdir)/GridHLayout.h \ |
2523 | @@ -199,6 +210,14 @@ |
2524 | $(srcdir)/WindowCompositor.h \ |
2525 | $(srcdir)/WindowThread.h |
2526 | |
2527 | +if HAVE_GEIS |
2528 | +source_h += \ |
2529 | + $(srcdir)/GeisAdapter.h \ |
2530 | + $(srcdir)/Gesture.h \ |
2531 | + $(srcdir)/GestureBroker.h \ |
2532 | + $(srcdir)/GesturesSubscription.h |
2533 | +endif |
2534 | + |
2535 | nuxprogramframework_cpp = \ |
2536 | $(srcdir)/ProgramFramework/ProgramTemplate.cpp \ |
2537 | $(srcdir)/ProgramFramework/TestView.cpp \ |
2538 | |
2539 | === modified file 'Nux/View.cpp' |
2540 | --- Nux/View.cpp 2012-06-18 12:03:53 +0000 |
2541 | +++ Nux/View.cpp 2012-08-01 18:42:21 +0000 |
2542 | @@ -484,4 +484,27 @@ |
2543 | { |
2544 | return true; |
2545 | } |
2546 | + |
2547 | +#ifdef NUX_GESTURES_SUPPORT |
2548 | + Area* View::GetInputAreaHitByGesture(const nux::GestureEvent &event) |
2549 | + { |
2550 | + if (!IsVisible()) |
2551 | + return nullptr; |
2552 | + |
2553 | + if (!IsGestureInsideArea(event)) |
2554 | + return nullptr; |
2555 | + |
2556 | + if (view_layout_) |
2557 | + { |
2558 | + Area *area = view_layout_->GetInputAreaHitByGesture(event); |
2559 | + if (area) |
2560 | + return area; |
2561 | + } |
2562 | + |
2563 | + if (HasSubscriptionForGesture(event)) |
2564 | + return this; |
2565 | + else |
2566 | + return nullptr; |
2567 | + } |
2568 | +#endif |
2569 | } |
2570 | |
2571 | === modified file 'Nux/View.h' |
2572 | --- Nux/View.h 2012-02-09 05:57:20 +0000 |
2573 | +++ Nux/View.h 2012-08-01 18:42:21 +0000 |
2574 | @@ -23,6 +23,7 @@ |
2575 | #ifndef ABSTRACTOBJECTBASE_H |
2576 | #define ABSTRACTOBJECTBASE_H |
2577 | |
2578 | +#include "Features.h" |
2579 | #include "Nux.h" |
2580 | #include "NuxCore/Property.h" |
2581 | |
2582 | @@ -31,6 +32,9 @@ |
2583 | namespace nux |
2584 | { |
2585 | class Layout; |
2586 | +#ifdef NUX_GESTURES_SUPPORT |
2587 | + class GestureEvent; |
2588 | +#endif |
2589 | |
2590 | class View: public InputArea |
2591 | { |
2592 | @@ -147,6 +151,9 @@ |
2593 | |
2594 | void IsHitDetectionSkipingChildren(bool skip_children); |
2595 | |
2596 | +#ifdef NUX_GESTURES_SUPPORT |
2597 | + virtual Area* GetInputAreaHitByGesture(const nux::GestureEvent &event); |
2598 | +#endif |
2599 | |
2600 | protected: |
2601 | |
2602 | |
2603 | === modified file 'Nux/WindowCompositor.cpp' |
2604 | --- Nux/WindowCompositor.cpp 2012-07-17 22:22:45 +0000 |
2605 | +++ Nux/WindowCompositor.cpp 2012-08-01 18:42:21 +0000 |
2606 | @@ -91,6 +91,10 @@ |
2607 | m_MenuRemoved = false; |
2608 | m_ModalWindow = NULL; |
2609 | m_Background = new ColorLayer(Color(0xFF4D4D4D)); |
2610 | + |
2611 | +#ifdef NUX_GESTURES_SUPPORT |
2612 | + gesture_broker_.reset(new DefaultGestureBroker(this)); |
2613 | +#endif |
2614 | } |
2615 | |
2616 | void WindowCompositor::BeforeDestructor() |
2617 | @@ -1086,6 +1090,20 @@ |
2618 | { |
2619 | DndEventCycle(event); |
2620 | } |
2621 | +#ifdef NUX_GESTURES_SUPPORT |
2622 | + else if (event.type == EVENT_GESTURE_BEGIN) |
2623 | + { |
2624 | + gesture_broker_->ProcessGestureBegin(static_cast<GestureEvent&>(event)); |
2625 | + } |
2626 | + else if (event.type == EVENT_GESTURE_UPDATE) |
2627 | + { |
2628 | + gesture_broker_->ProcessGestureUpdate(static_cast<GestureEvent&>(event)); |
2629 | + } |
2630 | + else if (event.type == EVENT_GESTURE_END) |
2631 | + { |
2632 | + gesture_broker_->ProcessGestureEnd(static_cast<GestureEvent&>(event)); |
2633 | + } |
2634 | +#endif |
2635 | } |
2636 | inside_event_cycle_ = false; |
2637 | } |
2638 | @@ -2412,5 +2430,56 @@ |
2639 | |
2640 | return ok; |
2641 | } |
2642 | + |
2643 | +#ifdef NUX_GESTURES_SUPPORT |
2644 | + void WindowCompositor::SetGestureBroker(std::unique_ptr<GestureBroker> gesture_broker) |
2645 | + { |
2646 | + gesture_broker_ = std::move(gesture_broker); |
2647 | + } |
2648 | + |
2649 | + InputArea *WindowCompositor::LocateGestureTarget(const GestureEvent &event) |
2650 | + { |
2651 | + InputArea *input_area = nullptr; |
2652 | + |
2653 | + for (auto window : _view_window_list) |
2654 | + { |
2655 | + if (!window.IsValid()) |
2656 | + continue; |
2657 | + |
2658 | + input_area = static_cast<InputArea*>(window->GetInputAreaHitByGesture(event)); |
2659 | + if (input_area) |
2660 | + break; |
2661 | + } |
2662 | + |
2663 | + // If a target InputArea wasn't found in any of the BaseWindows, then check |
2664 | + // the InputAreas in the layout of the main window. |
2665 | + if (!input_area) |
2666 | + { |
2667 | + Layout* main_window_layout = window_thread_->GetLayout(); |
2668 | + if (main_window_layout) |
2669 | + input_area = static_cast<InputArea*>( |
2670 | + main_window_layout->GetInputAreaHitByGesture(event)); |
2671 | + } |
2672 | + |
2673 | + return input_area; |
2674 | + } |
2675 | + |
2676 | + DefaultGestureBroker::DefaultGestureBroker(WindowCompositor *window_compositor) |
2677 | + : window_compositor_(window_compositor) |
2678 | + { |
2679 | + } |
2680 | + |
2681 | + std::vector<ShPtGestureTarget> |
2682 | + DefaultGestureBroker::FindGestureTargets(const nux::GestureEvent &event) |
2683 | + { |
2684 | + std::vector<ShPtGestureTarget> targets; |
2685 | + |
2686 | + InputArea *target_area = window_compositor_->LocateGestureTarget(event); |
2687 | + if (target_area) |
2688 | + targets.push_back(ShPtGestureTarget(new InputAreaTarget(target_area))); |
2689 | + |
2690 | + return targets; |
2691 | + } |
2692 | +#endif |
2693 | } |
2694 | |
2695 | |
2696 | === modified file 'Nux/WindowCompositor.h' |
2697 | --- Nux/WindowCompositor.h 2012-06-11 14:27:57 +0000 |
2698 | +++ Nux/WindowCompositor.h 2012-08-01 18:42:21 +0000 |
2699 | @@ -23,6 +23,7 @@ |
2700 | #ifndef WINDOWCOMPOSITOR_H |
2701 | #define WINDOWCOMPOSITOR_H |
2702 | |
2703 | +#include "Features.h" |
2704 | #include "BaseWindow.h" |
2705 | |
2706 | #include <sigc++/trackable.h> |
2707 | @@ -30,6 +31,12 @@ |
2708 | |
2709 | #include <NuxCore/ObjectPtr.h> |
2710 | |
2711 | +#ifdef NUX_GESTURES_SUPPORT |
2712 | +#include <unordered_map> |
2713 | +#include "Gesture.h" |
2714 | +#include "GestureBroker.h" |
2715 | +#endif |
2716 | + |
2717 | namespace nux |
2718 | { |
2719 | class MenuPage; |
2720 | @@ -407,6 +414,12 @@ |
2721 | */ |
2722 | RenderTargetTextures& GetWindowBuffer(BaseWindow* window); |
2723 | |
2724 | +#ifdef NUX_GESTURES_SUPPORT |
2725 | + InputArea *LocateGestureTarget(const GestureEvent &event); |
2726 | + |
2727 | + void SetGestureBroker(std::unique_ptr<GestureBroker> gesture_broker); |
2728 | +#endif |
2729 | + |
2730 | private: |
2731 | //! Render the interface. |
2732 | void Draw(bool SizeConfigurationEvent, bool force_draw); |
2733 | @@ -642,6 +655,10 @@ |
2734 | private: |
2735 | WindowThread* window_thread_; //!< The WindowThread to which this object belongs. |
2736 | |
2737 | +#ifdef NUX_GESTURES_SUPPORT |
2738 | + std::unique_ptr<GestureBroker> gesture_broker_; |
2739 | +#endif |
2740 | + |
2741 | //! Perform some action before destruction. |
2742 | /*! |
2743 | Perform some action before destruction. This function should only be |
2744 | @@ -667,6 +684,18 @@ |
2745 | friend class View; |
2746 | }; |
2747 | |
2748 | +#ifdef NUX_GESTURES_SUPPORT |
2749 | + class DefaultGestureBroker : public GestureBroker |
2750 | + { |
2751 | + public: |
2752 | + DefaultGestureBroker(WindowCompositor *window_compositor); |
2753 | + private: |
2754 | + std::vector<ShPtGestureTarget> |
2755 | + virtual FindGestureTargets(const nux::GestureEvent &event); |
2756 | + |
2757 | + WindowCompositor *window_compositor_; |
2758 | + }; |
2759 | +#endif |
2760 | } |
2761 | #endif // WINDOWCOMPOSITOR_H |
2762 | |
2763 | |
2764 | === modified file 'Nux/WindowThread.cpp' |
2765 | --- Nux/WindowThread.cpp 2012-07-04 16:36:46 +0000 |
2766 | +++ Nux/WindowThread.cpp 2012-08-01 18:42:21 +0000 |
2767 | @@ -19,7 +19,7 @@ |
2768 | * |
2769 | */ |
2770 | |
2771 | - |
2772 | +#include "Features.h" |
2773 | #include "Nux.h" |
2774 | #include "Layout.h" |
2775 | #include "NuxCore/Logger.h" |
2776 | @@ -61,6 +61,9 @@ |
2777 | , embedded_window_(false) |
2778 | , window_size_configuration_event_(false) |
2779 | , force_rendering_(false) |
2780 | +#ifdef NUX_GESTURES_SUPPORT |
2781 | + , geis_adapter_(new GeisAdapter) |
2782 | +#endif |
2783 | { |
2784 | // Thread specific objects |
2785 | graphics_display_ = NULL; |
2786 | @@ -493,13 +496,51 @@ |
2787 | |
2788 | extern EventToNameStruct EventToName[]; |
2789 | |
2790 | -#if (defined(NUX_OS_LINUX) || defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) && (!defined(NUX_DISABLE_GLIB_LOOP)) |
2791 | - unsigned int WindowThread::ExecutionLoop(unsigned int timer_id) |
2792 | -#else |
2793 | +#if (!defined(NUX_OS_LINUX) && !defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) || defined(NUX_DISABLE_GLIB_LOOP) |
2794 | +#ifdef NUX_GESTURES_SUPPORT |
2795 | + Event *WindowThread::FetchNextEvent() |
2796 | + { |
2797 | + Event *event; |
2798 | + |
2799 | + if (check_geis_first_) |
2800 | + { |
2801 | + if (geis_adapter_->ProcessNextEvent(&gesture_event_)) |
2802 | + { |
2803 | + event = &gesture_event_; |
2804 | + } |
2805 | + else |
2806 | + { |
2807 | + graphics_display_->GetSystemEvent(&input_event_); |
2808 | + event = &input_event; |
2809 | + } |
2810 | + } |
2811 | + else |
2812 | + { |
2813 | + if (graphics_display_->GetSystemEvent(&input_event_)) |
2814 | + { |
2815 | + event = &input_event_; |
2816 | + } |
2817 | + else |
2818 | + { |
2819 | + geis_adapter_->ProcessNextEvent(&gesture_event_); |
2820 | + event = &gesture_event_; |
2821 | + } |
2822 | + } |
2823 | + |
2824 | + // If we are running in a loop and both event buffers (X and GEIS) happens |
2825 | + // to have pending events, let's take one from each alternately so that no |
2826 | + // buffer overflows. |
2827 | + check_geis_first_ = !check_geis_first_; |
2828 | + |
2829 | + return event; |
2830 | + } |
2831 | +#endif // NUX_GESTURES_SUPPORT |
2832 | + |
2833 | unsigned int WindowThread::ExecutionLoop() |
2834 | -#endif |
2835 | { |
2836 | +#ifndef NUX_GESTURES_SUPPORT |
2837 | Event event; |
2838 | +#endif |
2839 | |
2840 | if (!IsEmbeddedWindow() && graphics_display_->IsPauseThreadGraphicsRendering()) |
2841 | { |
2842 | @@ -507,190 +548,218 @@ |
2843 | return 0; |
2844 | } |
2845 | |
2846 | -#if (!defined(NUX_OS_LINUX) && !defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) || defined(NUX_DISABLE_GLIB_LOOP) |
2847 | while (true) |
2848 | -#endif |
2849 | { |
2850 | - _inside_main_loop = true; |
2851 | - |
2852 | - if (first_pass_) |
2853 | - { |
2854 | - // Reset the timers that were called before the mainloop got initialized. |
2855 | - GetTimer().StartEarlyTimerObjects(); |
2856 | - } |
2857 | - |
2858 | - memset(&event, 0, sizeof(Event)); |
2859 | +#ifdef NUX_GESTURES_SUPPORT |
2860 | + int result = DoProcessEvent(*FetchNextEvent()); |
2861 | +#else |
2862 | graphics_display_->GetSystemEvent(&event); |
2863 | - |
2864 | - if ((event.type == NUX_DND_ENTER_WINDOW) || |
2865 | + int result = DoProcessEvent(event); |
2866 | +#endif |
2867 | + if (result != 1) |
2868 | + return result; |
2869 | + } |
2870 | + |
2871 | + return 1; |
2872 | + } |
2873 | +#endif // GLIB loop or not |
2874 | + |
2875 | + unsigned int WindowThread::ProcessEvent(Event &event) |
2876 | + { |
2877 | + if (!IsEmbeddedWindow() && graphics_display_->IsPauseThreadGraphicsRendering()) |
2878 | + { |
2879 | + // Do not sleep. Just return and let the GraphicsDisplay::SwapBuffer do the sleep if necessary. |
2880 | + return 0; |
2881 | + } |
2882 | + |
2883 | + return DoProcessEvent(event); |
2884 | + } |
2885 | + |
2886 | + unsigned int WindowThread::DoProcessEvent(Event &event) |
2887 | + { |
2888 | + _inside_main_loop = true; |
2889 | + |
2890 | + if (first_pass_) |
2891 | + { |
2892 | + // Reset the timers that were called before the mainloop got initialized. |
2893 | + GetTimer().StartEarlyTimerObjects(); |
2894 | + } |
2895 | + |
2896 | + if ((event.type == NUX_DND_ENTER_WINDOW) || |
2897 | (event.type == NUX_DND_LEAVE_WINDOW)) |
2898 | - { |
2899 | - GetWindowCompositor().ResetDnDArea(); |
2900 | - } |
2901 | - |
2902 | - // Call event inspectors. |
2903 | - CallEventInspectors(&event); |
2904 | - |
2905 | - if ((event.type == NUX_TERMINATE_APP) || (this->GetThreadState() == THREADSTOP)) |
2906 | - { |
2907 | - return 0; |
2908 | - } |
2909 | - |
2910 | - if (event.type == NUX_SIZE_CONFIGURATION) |
2911 | - { |
2912 | - window_size_configuration_event_ = true; |
2913 | - Rect r = graphics_display_->GetWindowGeometry(); |
2914 | - window_configuration.emit(r.x, r.y, r.width, r.height); |
2915 | - } |
2916 | - |
2917 | - int w, h; |
2918 | - // Call gGfx_OpenGL.getWindowSize after the gGfx_OpenGL.get_event. |
2919 | - // Otherwise, w and h may not be correct for the current frame if a resizing happened. |
2920 | - graphics_display_->GetWindowSize(w, h); |
2921 | - |
2922 | - if ((event.type == NUX_MOUSE_PRESSED) || |
2923 | + { |
2924 | + GetWindowCompositor().ResetDnDArea(); |
2925 | + } |
2926 | + |
2927 | + // Call event inspectors. |
2928 | + bool event_discarded = CallEventInspectors(&event); |
2929 | + if (event_discarded) |
2930 | + { |
2931 | + return 1; |
2932 | + } |
2933 | + |
2934 | + if ((event.type == NUX_TERMINATE_APP) || (this->GetThreadState() == THREADSTOP)) |
2935 | + { |
2936 | + return 0; |
2937 | + } |
2938 | + |
2939 | + if (event.type == NUX_SIZE_CONFIGURATION) |
2940 | + { |
2941 | + window_size_configuration_event_ = true; |
2942 | + Rect r = graphics_display_->GetWindowGeometry(); |
2943 | + window_configuration.emit(r.x, r.y, r.width, r.height); |
2944 | + } |
2945 | + |
2946 | + int w, h; |
2947 | + // Call gGfx_OpenGL.getWindowSize after the gGfx_OpenGL.get_event. |
2948 | + // Otherwise, w and h may not be correct for the current frame if a resizing happened. |
2949 | + graphics_display_->GetWindowSize(w, h); |
2950 | + |
2951 | + if ((event.type == NUX_MOUSE_PRESSED) || |
2952 | + (event.type == NUX_MOUSE_RELEASED) || |
2953 | + (event.type == NUX_MOUSE_DOUBLECLICK) || |
2954 | + (event.type == NUX_MOUSE_MOVE) || |
2955 | + (event.type == NUX_SIZE_CONFIGURATION) || |
2956 | + (event.type == NUX_KEYDOWN) || |
2957 | + (event.type == NUX_KEYUP) || |
2958 | + (event.type == NUX_NC_WINDOW_CONFIGURATION) || |
2959 | + (event.type == NUX_WINDOW_ENTER_FOCUS) || |
2960 | + (event.type == NUX_WINDOW_EXIT_FOCUS) || |
2961 | + (event.type == NUX_WINDOW_MOUSELEAVE) || |
2962 | + (event.type == NUX_DND_MOVE) || |
2963 | + (event.type == NUX_DND_DROP) || |
2964 | + (event.type == NUX_DND_ENTER) || |
2965 | + (event.type == NUX_DND_LEAVE) || |
2966 | + (event.type == NUX_MOUSE_WHEEL) || |
2967 | + event.type == EVENT_GESTURE_BEGIN || |
2968 | + event.type == EVENT_GESTURE_UPDATE || |
2969 | + event.type == EVENT_GESTURE_END) |
2970 | + { |
2971 | + //DISPATCH EVENT HERE |
2972 | + //event.Application = Application; |
2973 | + window_compositor_->ProcessEvent(event); |
2974 | + } |
2975 | + |
2976 | + if (event.type == NUX_SIZE_CONFIGURATION) |
2977 | + { |
2978 | + if (!graphics_display_->isWindowMinimized()) |
2979 | + { |
2980 | + graphics_display_->SetViewPort(0, 0, event.width, event.height); |
2981 | + ReconfigureLayout(); |
2982 | + window_compositor_->FormatRenderTargets(event.width, event.height); |
2983 | + } |
2984 | + window_compositor_->FloatingAreaConfigureNotify(event.width, event.height); |
2985 | + window_size_configuration_event_ = true; |
2986 | + } |
2987 | + |
2988 | + // Some action may have caused layouts and areas to request a recompute. |
2989 | + // Process them here before the Draw section. |
2990 | + if (!graphics_display_->isWindowMinimized() && !IsEmbeddedWindow()) |
2991 | + { |
2992 | + if (queue_main_layout_) |
2993 | + { |
2994 | + ReconfigureLayout(); |
2995 | + } |
2996 | + else |
2997 | + { |
2998 | + // Compute the layouts that have been queued. |
2999 | + ComputeQueuedLayout(); |
3000 | + } |
3001 | + } |
3002 | + |
3003 | + _inside_main_loop = false; |
3004 | + |
3005 | + if (!graphics_display_->IsPauseThreadGraphicsRendering() || IsEmbeddedWindow()) |
3006 | + { |
3007 | + bool SwapGLBuffer = false; |
3008 | + |
3009 | + // Warn the host window manager to initiate a draw cycle. |
3010 | + bool request_draw_cycle_to_host_wm = false; |
3011 | + |
3012 | + if (first_pass_) |
3013 | + { |
3014 | + if (IsEmbeddedWindow()) |
3015 | + { |
3016 | + request_draw_cycle_to_host_wm = true; |
3017 | + force_rendering_ = true; |
3018 | + } |
3019 | + else |
3020 | + { |
3021 | + window_compositor_->Draw(window_size_configuration_event_, true); |
3022 | + } |
3023 | + first_pass_ = false; |
3024 | + } |
3025 | + else |
3026 | + { |
3027 | + bool b = (event.type == NUX_MOUSE_PRESSED) || |
3028 | (event.type == NUX_MOUSE_RELEASED) || |
3029 | (event.type == NUX_MOUSE_DOUBLECLICK) || |
3030 | - (event.type == NUX_MOUSE_MOVE) || |
3031 | + //(event.type == NUX_MOUSE_MOVE) || |
3032 | (event.type == NUX_SIZE_CONFIGURATION) || |
3033 | (event.type == NUX_KEYDOWN) || |
3034 | (event.type == NUX_KEYUP) || |
3035 | (event.type == NUX_NC_WINDOW_CONFIGURATION) || |
3036 | (event.type == NUX_WINDOW_ENTER_FOCUS) || |
3037 | (event.type == NUX_WINDOW_EXIT_FOCUS) || |
3038 | - (event.type == NUX_WINDOW_MOUSELEAVE) || |
3039 | - (event.type == NUX_DND_MOVE) || |
3040 | - (event.type == NUX_DND_DROP) || |
3041 | - (event.type == NUX_DND_ENTER) || |
3042 | - (event.type == NUX_DND_LEAVE) || |
3043 | - (event.type == NUX_MOUSE_WHEEL)) |
3044 | - { |
3045 | - //DISPATCH EVENT HERE |
3046 | - //event.Application = Application; |
3047 | - window_compositor_->ProcessEvent(event); |
3048 | - } |
3049 | - |
3050 | - if (event.type == NUX_SIZE_CONFIGURATION) |
3051 | - { |
3052 | - if (!graphics_display_->isWindowMinimized()) |
3053 | - { |
3054 | - graphics_display_->SetViewPort(0, 0, event.width, event.height); |
3055 | - ReconfigureLayout(); |
3056 | - window_compositor_->FormatRenderTargets(event.width, event.height); |
3057 | - } |
3058 | - window_compositor_->FloatingAreaConfigureNotify(event.width, event.height); |
3059 | - window_size_configuration_event_ = true; |
3060 | - } |
3061 | - |
3062 | - // Some action may have caused layouts and areas to request a recompute. |
3063 | - // Process them here before the Draw section. |
3064 | - if (!graphics_display_->isWindowMinimized() && !IsEmbeddedWindow()) |
3065 | - { |
3066 | - if (queue_main_layout_) |
3067 | - { |
3068 | - ReconfigureLayout(); |
3069 | - } |
3070 | - else |
3071 | - { |
3072 | - // Compute the layouts that have been queued. |
3073 | - ComputeQueuedLayout(); |
3074 | - } |
3075 | - } |
3076 | - |
3077 | - _inside_main_loop = false; |
3078 | - |
3079 | - if (!graphics_display_->IsPauseThreadGraphicsRendering() || IsEmbeddedWindow()) |
3080 | - { |
3081 | - bool SwapGLBuffer = false; |
3082 | - |
3083 | - // Warn the host window manager to initiate a draw cycle. |
3084 | - bool request_draw_cycle_to_host_wm = false; |
3085 | - |
3086 | - if (first_pass_) |
3087 | - { |
3088 | - if (IsEmbeddedWindow()) |
3089 | - { |
3090 | - request_draw_cycle_to_host_wm = true; |
3091 | - force_rendering_ = true; |
3092 | - } |
3093 | - else |
3094 | - { |
3095 | - window_compositor_->Draw(window_size_configuration_event_, true); |
3096 | - } |
3097 | - first_pass_ = false; |
3098 | - } |
3099 | - else |
3100 | - { |
3101 | - bool b = (event.type == NUX_MOUSE_PRESSED) || |
3102 | - (event.type == NUX_MOUSE_RELEASED) || |
3103 | - (event.type == NUX_MOUSE_DOUBLECLICK) || |
3104 | - //(event.type == NUX_MOUSE_MOVE) || |
3105 | - (event.type == NUX_SIZE_CONFIGURATION) || |
3106 | - (event.type == NUX_KEYDOWN) || |
3107 | - (event.type == NUX_KEYUP) || |
3108 | - (event.type == NUX_NC_WINDOW_CONFIGURATION) || |
3109 | - (event.type == NUX_WINDOW_ENTER_FOCUS) || |
3110 | - (event.type == NUX_WINDOW_EXIT_FOCUS) || |
3111 | - (event.type == NUX_WINDOW_DIRTY); |
3112 | - |
3113 | - if (b && window_compositor_->IsTooltipActive()) |
3114 | - { |
3115 | - // Cancel the tooltip since an event that should cause the tooltip to disappear has occurred. |
3116 | - window_compositor_->CancelTooltip(); |
3117 | - b |= true; |
3118 | - } |
3119 | - |
3120 | - if (!window_compositor_->ValidateMouseInsideTooltipArea(event.x, event.y) && window_compositor_->IsTooltipActive()) |
3121 | - { |
3122 | - // Cancel the tooltip since an event that should cause the tooltip to disappear has occurred. |
3123 | - window_compositor_->CancelTooltip(); |
3124 | - b |= true; |
3125 | - } |
3126 | - |
3127 | - if (b || IsRedrawNeeded()) |
3128 | - { |
3129 | - if (IsEmbeddedWindow()) |
3130 | - { |
3131 | - request_draw_cycle_to_host_wm = true; |
3132 | - } |
3133 | - else |
3134 | - { |
3135 | - window_compositor_->Draw(window_size_configuration_event_, false); |
3136 | - } |
3137 | - SwapGLBuffer = true; |
3138 | - } |
3139 | - else if (window_compositor_->GetWidgetDrawingOverlay() != 0) |
3140 | - { |
3141 | - if (IsEmbeddedWindow()) |
3142 | - { |
3143 | - request_draw_cycle_to_host_wm = true; |
3144 | - } |
3145 | - else |
3146 | - { |
3147 | - window_compositor_->Draw(window_size_configuration_event_, false); |
3148 | - } |
3149 | - SwapGLBuffer = false; |
3150 | - } |
3151 | - |
3152 | - } |
3153 | - |
3154 | - if (!IsEmbeddedWindow()) |
3155 | - { |
3156 | - if (SwapGLBuffer) |
3157 | - { |
3158 | - // Something was rendered! Swap the rendering buffer! |
3159 | - graphics_display_->SwapBuffer(true); |
3160 | - } |
3161 | - |
3162 | - ClearRedrawFlag(); |
3163 | - GetWindowThread()->GetGraphicsEngine().ResetStats(); |
3164 | - } |
3165 | - else if (IsEmbeddedWindow() && (_draw_requested_to_host_wm == false) && request_draw_cycle_to_host_wm) |
3166 | - { |
3167 | - RequestRedraw(); |
3168 | - } |
3169 | - window_size_configuration_event_ = false; |
3170 | - } |
3171 | + (event.type == NUX_WINDOW_DIRTY); |
3172 | + |
3173 | + if (b && window_compositor_->IsTooltipActive()) |
3174 | + { |
3175 | + // Cancel the tooltip since an event that should cause the tooltip to disappear has occurred. |
3176 | + window_compositor_->CancelTooltip(); |
3177 | + b |= true; |
3178 | + } |
3179 | + |
3180 | + if (!window_compositor_->ValidateMouseInsideTooltipArea(event.x, event.y) && window_compositor_->IsTooltipActive()) |
3181 | + { |
3182 | + // Cancel the tooltip since an event that should cause the tooltip to disappear has occurred. |
3183 | + window_compositor_->CancelTooltip(); |
3184 | + b |= true; |
3185 | + } |
3186 | + |
3187 | + if (b || IsRedrawNeeded()) |
3188 | + { |
3189 | + if (IsEmbeddedWindow()) |
3190 | + { |
3191 | + request_draw_cycle_to_host_wm = true; |
3192 | + } |
3193 | + else |
3194 | + { |
3195 | + window_compositor_->Draw(window_size_configuration_event_, false); |
3196 | + } |
3197 | + SwapGLBuffer = true; |
3198 | + } |
3199 | + else if (window_compositor_->GetWidgetDrawingOverlay() != 0) |
3200 | + { |
3201 | + if (IsEmbeddedWindow()) |
3202 | + { |
3203 | + request_draw_cycle_to_host_wm = true; |
3204 | + } |
3205 | + else |
3206 | + { |
3207 | + window_compositor_->Draw(window_size_configuration_event_, false); |
3208 | + } |
3209 | + SwapGLBuffer = false; |
3210 | + } |
3211 | + |
3212 | + } |
3213 | + |
3214 | + if (!IsEmbeddedWindow()) |
3215 | + { |
3216 | + if (SwapGLBuffer) |
3217 | + { |
3218 | + // Something was rendered! Swap the rendering buffer! |
3219 | + graphics_display_->SwapBuffer(true); |
3220 | + } |
3221 | + |
3222 | + ClearRedrawFlag(); |
3223 | + GetWindowThread()->GetGraphicsEngine().ResetStats(); |
3224 | + } |
3225 | + else if (IsEmbeddedWindow() && (_draw_requested_to_host_wm == false) && request_draw_cycle_to_host_wm) |
3226 | + { |
3227 | + RequestRedraw(); |
3228 | + } |
3229 | + window_size_configuration_event_ = false; |
3230 | } |
3231 | |
3232 | return 1; |
3233 | |
3234 | === modified file 'Nux/WindowThread.h' |
3235 | --- Nux/WindowThread.h 2012-06-11 14:27:57 +0000 |
3236 | +++ Nux/WindowThread.h 2012-08-01 18:42:21 +0000 |
3237 | @@ -24,6 +24,11 @@ |
3238 | #define WINDOWTHREAD_H |
3239 | |
3240 | #include "TimerProc.h" |
3241 | +#include "Features.h" |
3242 | + |
3243 | +#ifdef NUX_GESTURES_SUPPORT |
3244 | +#include "GeisAdapter.h" |
3245 | +#endif |
3246 | |
3247 | namespace nux |
3248 | { |
3249 | @@ -313,6 +318,15 @@ |
3250 | |
3251 | std::vector<Geometry> GetDrawList(); |
3252 | |
3253 | +#ifdef NUX_GESTURES_SUPPORT |
3254 | + /*! |
3255 | + Simple wrapper for ProcessEvent for connection with GeisAdapter::event_ready |
3256 | + */ |
3257 | + void ProcessGestureEvent(GestureEvent &event) { ProcessEvent(event); } |
3258 | + |
3259 | + GeisAdapter *GetGeisAdapter() const {return geis_adapter_.get();} |
3260 | +#endif |
3261 | + |
3262 | protected: |
3263 | |
3264 | /*! |
3265 | @@ -421,33 +435,21 @@ |
3266 | */ |
3267 | void DisableMouseKeyboardInput(); |
3268 | |
3269 | -#if (defined(NUX_OS_LINUX) || defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) && (!defined(NUX_DISABLE_GLIB_LOOP)) |
3270 | - |
3271 | - /*! |
3272 | - Entire frame of goes through this function. It does the following: |
3273 | - * get the input events from the mouse, keyboard, touchpad |
3274 | - * processes the input events |
3275 | - * resizes views |
3276 | - * draw the frame |
3277 | - This function is called when there is an input event or when a timer has expired. |
3278 | - |
3279 | - @param timer_id The id of the timer that has has expired. |
3280 | - */ |
3281 | - unsigned int ExecutionLoop(unsigned int timer_id); |
3282 | -#else |
3283 | - /*! |
3284 | - Entire frame of goes through this function. It does the following: |
3285 | - * get the input events from the mouse, keyboard, touchpad |
3286 | - * processes the input events |
3287 | - * resizes views |
3288 | - * draw the frame |
3289 | - This function is called when there is an input event or when a timer has expired. |
3290 | - |
3291 | - @param timer_id The id of the timer that has has expired. |
3292 | - */ |
3293 | +#if (!defined(NUX_OS_LINUX) && !defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) || defined(NUX_DISABLE_GLIB_LOOP) |
3294 | + //! Calls ProcessEvent in a loop |
3295 | unsigned int ExecutionLoop(); |
3296 | #endif |
3297 | |
3298 | + /*! |
3299 | + It does the following: |
3300 | + * processes the input events |
3301 | + * resizes views |
3302 | + * draw the frame |
3303 | + This function is called when there is an input event, a gesture event or |
3304 | + when a timer has expired. |
3305 | + */ |
3306 | + unsigned int ProcessEvent(Event &event); |
3307 | + |
3308 | virtual ThreadState StartChildThread(AbstractThread *thread, bool Modal); |
3309 | virtual void AddChildThread(AbstractThread *); |
3310 | virtual void RemoveChildThread(AbstractThread *); |
3311 | @@ -461,6 +463,12 @@ |
3312 | WindowThread *modal_window_thread_; |
3313 | |
3314 | private: |
3315 | + /*! |
3316 | + Internally called by ProcessEvent end ExecutionLopp after early return |
3317 | + check. |
3318 | + */ |
3319 | + unsigned int DoProcessEvent(Event &event); |
3320 | + |
3321 | //! Execute the main loop of this thread. |
3322 | /*! |
3323 | Execute the main loop of this thread. |
3324 | @@ -608,6 +616,14 @@ |
3325 | bool AddChildWindowGlibLoop(WindowThread* wnd_thread); |
3326 | |
3327 | unsigned int AddGLibTimeout(unsigned int duration); |
3328 | +#else // no GLIB loop |
3329 | + Event input_event_; |
3330 | + GestureEvent gesture_event_; |
3331 | + Event *FetchNextEvent(); |
3332 | +#endif |
3333 | + |
3334 | +#ifdef NUX_GESTURES_SUPPORT |
3335 | + std::unique_ptr<GeisAdapter> geis_adapter_; |
3336 | #endif |
3337 | |
3338 | /*! |
3339 | |
3340 | === modified file 'NuxCore/Rect.cpp' |
3341 | --- NuxCore/Rect.cpp 2011-09-28 19:18:29 +0000 |
3342 | +++ NuxCore/Rect.cpp 2012-08-01 18:42:21 +0000 |
3343 | @@ -120,6 +120,12 @@ |
3344 | (y <= p.y) && (y + height > p.y) ); |
3345 | } |
3346 | |
3347 | + bool Rect::IsInside(const Point2D<float> &p) const |
3348 | + { |
3349 | + return ( (x <= (int)p.x) && (x + width > (int)p.x) && |
3350 | + (y <= (int)p.y) && (y + height > (int)p.y) ); |
3351 | + } |
3352 | + |
3353 | bool Rect::IsPointInside (int x_, int y_) const |
3354 | { |
3355 | return ( (x <= x_) && (x + width > x_) && |
3356 | |
3357 | === modified file 'NuxCore/Rect.h' |
3358 | --- NuxCore/Rect.h 2011-09-09 21:47:54 +0000 |
3359 | +++ NuxCore/Rect.h 2012-08-01 18:42:21 +0000 |
3360 | @@ -48,6 +48,7 @@ |
3361 | bool IsNull() const; |
3362 | |
3363 | bool IsInside(const Point &p) const; |
3364 | + bool IsInside(const Point2D<float> &p) const; |
3365 | Rect Intersect(const Rect &) const; |
3366 | |
3367 | // expand the width by factor_x and the height by factor_y |
3368 | |
3369 | === modified file 'NuxGraphics/Events.h' |
3370 | --- NuxGraphics/Events.h 2012-02-07 22:51:46 +0000 |
3371 | +++ NuxGraphics/Events.h 2012-08-01 18:42:21 +0000 |
3372 | @@ -230,6 +230,10 @@ |
3373 | EVENT_DND_LEAVE, //!< Synthetic event generated when the dnd action leaves an InputArea. This is not the event sent when the dnd action leaves the window. |
3374 | EVENT_DND_ENTER_WINDOW, //!< Emitted when the DND action goes inside(XdndLeave) a window. |
3375 | EVENT_DND_LEAVE_WINDOW, //!< Emitted when the DND action goes outside(XdndEnter) a window. |
3376 | + EVENT_GESTURE_BEGIN, //!< Emitted when a gesture begins. Event class is GestureEvent. |
3377 | + EVENT_GESTURE_UPDATE, //!< Emitted when a gesture is updated. Event class is GestureEvent. |
3378 | + EVENT_GESTURE_END, //!< Emitted when a gesture ends. Event class is GestureEvent. |
3379 | + EVENT_GESTURE_LOST, //!< Emitted when a gesture target loses a gesture. Event class is GestureEvent. |
3380 | |
3381 | |
3382 | // The following values are deprecated. Use the ones above. |
3383 | @@ -306,7 +310,7 @@ |
3384 | |
3385 | // Because an event is save in event_type instead of calling immediately the handling function, |
3386 | // we must clear the previous event each time before we test for new event in Gfx_OpenGLImpl::get_event. |
3387 | - void Reset(); |
3388 | + /*virtual*/ void Reset(); |
3389 | |
3390 | int GetX() const; |
3391 | int GetY() const; |
3392 | |
3393 | === added file 'NuxGraphics/GestureEvent.cpp' |
3394 | --- NuxGraphics/GestureEvent.cpp 1970-01-01 00:00:00 +0000 |
3395 | +++ NuxGraphics/GestureEvent.cpp 2012-08-01 18:42:21 +0000 |
3396 | @@ -0,0 +1,80 @@ |
3397 | +/* |
3398 | + * Copyright 2012 Canonical Ltd. |
3399 | + * |
3400 | + * This program is free software: you can redistribute it and/or modify it |
3401 | + * under the terms of the GNU Lesser General Public License, as |
3402 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
3403 | + * of the License. |
3404 | + * |
3405 | + * This program is distributed in the hope that it will be useful, but |
3406 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3407 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
3408 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
3409 | + * License for more details. |
3410 | + * |
3411 | + * You should have received a copy of both the GNU Lesser General Public |
3412 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
3413 | + * |
3414 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
3415 | + * |
3416 | + */ |
3417 | + |
3418 | +#include "GestureEvent.h" |
3419 | +#include "NuxCore/Logger.h" |
3420 | + |
3421 | +using namespace nux; |
3422 | + |
3423 | +namespace |
3424 | +{ |
3425 | + nux::logging::Logger logger("nux.gestureevent"); |
3426 | +} |
3427 | + |
3428 | +GestureEvent::GestureEvent() |
3429 | +{ |
3430 | + // that's how many fingers you have on your hand (duh). |
3431 | + // Can't have gestures with more than that many touch points. |
3432 | + touches_.reserve(5); |
3433 | +} |
3434 | + |
3435 | +void GestureEvent::Accept() |
3436 | +{ |
3437 | + GeisStatus status; |
3438 | + status = geis_gesture_accept(geis_, geis_group_, gesture_id_); |
3439 | + if (status != GEIS_STATUS_SUCCESS) |
3440 | + { |
3441 | + LOG_WARNING(logger) << "Failed to accept gesture with id " << gesture_id_; |
3442 | + } |
3443 | +} |
3444 | + |
3445 | +void GestureEvent::Reject() |
3446 | +{ |
3447 | + GeisStatus status; |
3448 | + status = geis_gesture_reject(geis_, geis_group_, gesture_id_); |
3449 | + if (status != GEIS_STATUS_SUCCESS) |
3450 | + { |
3451 | + LOG_WARNING(logger) << "Failed to reject gesture with id " << gesture_id_; |
3452 | + } |
3453 | +} |
3454 | + |
3455 | +void GestureEvent::Reset() |
3456 | +{ |
3457 | + Event::Reset(); |
3458 | + |
3459 | + gesture_id_ = -1; |
3460 | + gesture_classes_ = 0; |
3461 | + timestamp_ = -1; |
3462 | + focus_.x = focus_.y = 0.0f; |
3463 | + delta_.x = delta_.y = 0.0f; |
3464 | + angle_ = 0.0f; |
3465 | + angle_delta_ = 0.0f; |
3466 | + angular_velocity_ = 0.0f; |
3467 | + tap_duration_ = -1; |
3468 | + velocity_.x = velocity_.y = 0.0f; |
3469 | + radius_ = 0.0f; |
3470 | + radius_delta_ = 0.0f; |
3471 | + radial_velocity_ = 0.0f; |
3472 | + is_construction_finished_ = false; |
3473 | + touches_.clear(); |
3474 | + geis_ = nullptr; |
3475 | + geis_group_ = nullptr; |
3476 | +} |
3477 | |
3478 | === added file 'NuxGraphics/GestureEvent.h' |
3479 | --- NuxGraphics/GestureEvent.h 1970-01-01 00:00:00 +0000 |
3480 | +++ NuxGraphics/GestureEvent.h 2012-08-01 18:42:21 +0000 |
3481 | @@ -0,0 +1,162 @@ |
3482 | +/* |
3483 | + * Copyright 2012 Canonical Ltd. |
3484 | + * |
3485 | + * This program is free software: you can redistribute it and/or modify it |
3486 | + * under the terms of the GNU Lesser General Public License, as |
3487 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
3488 | + * of the License. |
3489 | + * |
3490 | + * This program is distributed in the hope that it will be useful, but |
3491 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3492 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
3493 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
3494 | + * License for more details. |
3495 | + * |
3496 | + * You should have received a copy of both the GNU Lesser General Public |
3497 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
3498 | + * |
3499 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
3500 | + * |
3501 | + */ |
3502 | + |
3503 | +#ifndef NUX_GESTURE_EVENT_H |
3504 | +#define NUX_GESTURE_EVENT_H |
3505 | +#include "Nux/Features.h" |
3506 | +#ifdef NUX_GESTURES_SUPPORT |
3507 | + |
3508 | +#include <geis/geis.h> |
3509 | +#include <vector> |
3510 | + |
3511 | +#include "Events.h" |
3512 | +#include "NuxCore/Math/Point2D.h" |
3513 | + |
3514 | +namespace nux |
3515 | +{ |
3516 | + //! Enumerates all possible gesture classes. |
3517 | + enum GestureClass |
3518 | + { |
3519 | + DRAG_GESTURE = 1, //! A drag gesture |
3520 | + PINCH_GESTURE = 2, //! A pinch gesture |
3521 | + ROTATE_GESTURE = 4, //! A rotation gesture |
3522 | + TAP_GESTURE = 8, //! A tap gesture. |
3523 | + TOUCH_GESTURE = 16 /*! A touch gesture. It merely groups two or more touch |
3524 | + points and send their information, without interpreting |
3525 | + what gestures the touch points are performing. */ |
3526 | + }; |
3527 | + |
3528 | + class TouchPoint |
3529 | + { |
3530 | + public: |
3531 | + TouchPoint() : id(-1), x(0.0f), y(0.0f) {} |
3532 | + TouchPoint(int id, float x, float y) : id(id), x(x), y(y) {} |
3533 | + |
3534 | + bool operator ==(const TouchPoint& other) const |
3535 | + { |
3536 | + return id == other.id; |
3537 | + } |
3538 | + |
3539 | + int id; |
3540 | + float x; |
3541 | + float y; |
3542 | + }; |
3543 | + |
3544 | + //! Gesture Event class |
3545 | + class GestureEvent : public Event |
3546 | + { |
3547 | + public: |
3548 | + //! Constructs a GestureEvent |
3549 | + GestureEvent(); |
3550 | + |
3551 | + //! Accepts the gesture. |
3552 | + void Accept(); |
3553 | + //! Rejects the gesture. |
3554 | + void Reject(); |
3555 | + |
3556 | + //! The gesture classes that this gesture belongs to. |
3557 | + /*! |
3558 | + A bitwise "or" of one or more gesture classes from |
3559 | + GestureClass enumeration. |
3560 | + |
3561 | + A single physical gesture can comply/belong to more than |
3562 | + one gesture class, such as a rotating pinch. |
3563 | + */ |
3564 | + int GetGestureClasses() const {return gesture_classes_;} |
3565 | + |
3566 | + //! Whether all gestures for the related touch points have been presented. |
3567 | + /*! |
3568 | + This property allows the client to determine if all the possible gestures |
3569 | + from the set of touches in this event have already been presented. When |
3570 | + this value is true, the client will have received all the information needed |
3571 | + to make a gesture accept and reject decision based on potentially |
3572 | + overlapping gestures. An example is when both one and two touch gestures are |
3573 | + subscribed on the same window with the same gesture classes and thresholds. |
3574 | + When this property is true for one touch gesture events, the client can be |
3575 | + sure there are no other touches unless a two touch gesture event has already |
3576 | + been sent. |
3577 | + Another example is when you subscribe for three touches Touch and four |
3578 | + touches Drag. As soon as a third finger is detected a three touches Touch |
3579 | + gesture will begin, but you cannot be sure a fourth finger isn't coming |
3580 | + right after (that can eventually cause a four touches Drag) until this |
3581 | + property is true. |
3582 | + */ |
3583 | + bool IsConstructionFinished() const { return is_construction_finished_;} |
3584 | + |
3585 | + int GetGestureId() const {return gesture_id_;} |
3586 | + bool IsDirectTouch() const {return is_direct_touch_;} |
3587 | + int GetTimestamp() const {return timestamp_;} |
3588 | + const Point2D<float> &GetFocus() const {return focus_;} |
3589 | + const Point2D<float> &GetDelta() const {return delta_;} |
3590 | + float GetAngle() const {return angle_;} |
3591 | + float GetAngleDelta() const {return angle_delta_;} |
3592 | + float GetAngularVelocity() const {return angular_velocity_;} |
3593 | + |
3594 | + //! Duration of a tap gesture, in milliseconds |
3595 | + int GetTapDuration() const {return tap_duration_;} |
3596 | + |
3597 | + const Point2D<float> &GetVelocity() {return velocity_;} |
3598 | + float GetRadius() const {return radius_;} |
3599 | + float GetRadiusDelta() const {return radius_delta_;} |
3600 | + float GetRadialVelocity() const {return radial_velocity_;} |
3601 | + const std::vector<TouchPoint> &GetTouches() const {return touches_;} |
3602 | + |
3603 | + virtual void Reset(); |
3604 | + |
3605 | + private: |
3606 | + int gesture_id_; |
3607 | + int gesture_classes_; |
3608 | + bool is_direct_touch_; |
3609 | + int timestamp_; |
3610 | + Point2D<float> focus_; |
3611 | + Point2D<float> delta_; |
3612 | + float angle_; |
3613 | + float angle_delta_; |
3614 | + float angular_velocity_; |
3615 | + int tap_duration_; |
3616 | + Point2D<float> velocity_; |
3617 | + float radius_; |
3618 | + float radius_delta_; |
3619 | + float radial_velocity_; |
3620 | + std::vector<TouchPoint> touches_; |
3621 | + bool is_construction_finished_; |
3622 | + |
3623 | + Geis geis_; |
3624 | + GeisGroup geis_group_; |
3625 | + friend class GeisAdapter; |
3626 | + friend class FakeGestureEvent; |
3627 | + }; |
3628 | + |
3629 | + //! Enumerates possible requests regarding delivery of gesture events. |
3630 | + enum class GestureDeliveryRequest |
3631 | + { |
3632 | + NONE, /*!< No request. Continue with current delivery policy. */ |
3633 | + EXCLUSIVITY /*!< From this moment onwards, deliver events from the related |
3634 | + gesture only to this target. Other targets for this gesture |
3635 | + will receive an event of type EVENT_GESTURE_LOST instead of |
3636 | + this event. |
3637 | + Note that it doesn't affect targets that come before this |
3638 | + one in the order of delivery. */ |
3639 | + }; |
3640 | +} // namespace nux |
3641 | + |
3642 | +#endif // NUX_GESTURES_SUPPORT |
3643 | +#endif // NUX_GESTURE_EVENT_H |
3644 | |
3645 | === modified file 'NuxGraphics/GraphicsDisplayWin.cpp' |
3646 | --- NuxGraphics/GraphicsDisplayWin.cpp 2012-02-26 03:55:44 +0000 |
3647 | +++ NuxGraphics/GraphicsDisplayWin.cpp 2012-08-01 18:42:21 +0000 |
3648 | @@ -1130,12 +1130,13 @@ |
3649 | } |
3650 | |
3651 | //--------------------------------------------------------------------------------------------------------- |
3652 | - void GraphicsDisplay::GetSystemEvent(Event *evt) |
3653 | + bool GraphicsDisplay::GetSystemEvent(Event *evt) |
3654 | { |
3655 | MSG msg; |
3656 | event_->Reset(); |
3657 | // Erase mouse event and mouse doubleclick states. Keep the mouse states. |
3658 | event_->mouse_state &= 0x0F000000; |
3659 | + bool got_event; |
3660 | |
3661 | // Always set the second parameter of PeekMessage to NULL. Indeed, many services creates |
3662 | // windows on the program behalf. If pass the main window as filter, we will miss all the |
3663 | @@ -1153,10 +1154,12 @@ |
3664 | DispatchMessage(&msg); |
3665 | |
3666 | memcpy(evt, event_, sizeof(Event)); |
3667 | + got_event = true; |
3668 | } |
3669 | else |
3670 | { |
3671 | memcpy(evt, event_, sizeof(Event)); |
3672 | + got_event = false; |
3673 | } |
3674 | |
3675 | if (msg.message == WM_QUIT) |
3676 | @@ -1174,6 +1177,8 @@ |
3677 | event_->type = NUX_NO_EVENT; |
3678 | memcpy(evt, event_, sizeof(Event)); |
3679 | } |
3680 | + |
3681 | + return got_event; |
3682 | } |
3683 | |
3684 | void GraphicsDisplay::ProcessForeignWin32Event(HWND hWnd, MSG msg, WPARAM wParam, LPARAM lParam, Event *event) |
3685 | |
3686 | === modified file 'NuxGraphics/GraphicsDisplayWin.h' |
3687 | --- NuxGraphics/GraphicsDisplayWin.h 2011-10-21 22:06:35 +0000 |
3688 | +++ NuxGraphics/GraphicsDisplayWin.h 2012-08-01 18:42:21 +0000 |
3689 | @@ -155,7 +155,10 @@ |
3690 | void SwapBuffer(bool glswap = true); |
3691 | |
3692 | // Event methods |
3693 | - void GetSystemEvent(Event *evt); |
3694 | + /*! |
3695 | + Returns true if there was a pending event to be fetched and false otherwise |
3696 | + */ |
3697 | + bool GetSystemEvent(Event *evt); |
3698 | Event &GetCurrentEvent(); |
3699 | |
3700 | bool isWindowMinimized() const |
3701 | |
3702 | === modified file 'NuxGraphics/GraphicsDisplayX11.cpp' |
3703 | --- NuxGraphics/GraphicsDisplayX11.cpp 2012-04-16 18:37:21 +0000 |
3704 | +++ NuxGraphics/GraphicsDisplayX11.cpp 2012-08-01 18:42:21 +0000 |
3705 | @@ -1216,12 +1216,13 @@ |
3706 | return state; |
3707 | } |
3708 | |
3709 | - void GraphicsDisplay::GetSystemEvent(Event *evt) |
3710 | + bool GraphicsDisplay::GetSystemEvent(Event *evt) |
3711 | { |
3712 | m_pEvent->Reset(); |
3713 | // Erase mouse event and mouse doubleclick states. Keep the mouse states. |
3714 | m_pEvent->mouse_state &= 0x0F000000; |
3715 | bool bProcessEvent = true; |
3716 | + bool got_event; |
3717 | |
3718 | // Process event matching this window |
3719 | XEvent xevent; |
3720 | @@ -1238,7 +1239,7 @@ |
3721 | if (result) |
3722 | { |
3723 | memcpy(evt, m_pEvent, sizeof(Event)); |
3724 | - return; |
3725 | + return true; |
3726 | } |
3727 | } |
3728 | } |
3729 | @@ -1289,11 +1290,15 @@ |
3730 | |
3731 | memcpy(evt, m_pEvent, sizeof(Event)); |
3732 | |
3733 | + got_event = true; |
3734 | } |
3735 | else |
3736 | { |
3737 | memcpy(evt, m_pEvent, sizeof(Event)); |
3738 | + got_event = false; |
3739 | } |
3740 | + |
3741 | + return got_event; |
3742 | } |
3743 | |
3744 | #if defined(NUX_OS_LINUX) |
3745 | |
3746 | === modified file 'NuxGraphics/GraphicsDisplayX11.h' |
3747 | --- NuxGraphics/GraphicsDisplayX11.h 2012-01-26 23:01:57 +0000 |
3748 | +++ NuxGraphics/GraphicsDisplayX11.h 2012-08-01 18:42:21 +0000 |
3749 | @@ -224,7 +224,10 @@ |
3750 | void SwapBuffer(bool glswap = true); |
3751 | |
3752 | // Event methods |
3753 | - void GetSystemEvent(Event *evt); |
3754 | + /*! |
3755 | + Returns true if there was a pending event to be fetched and false otherwise |
3756 | + */ |
3757 | + bool GetSystemEvent(Event *evt); |
3758 | |
3759 | // Os specific |
3760 | int GetGlXMajor() const; |
3761 | |
3762 | === modified file 'NuxGraphics/Makefile.am' |
3763 | --- NuxGraphics/Makefile.am 2012-05-25 20:36:09 +0000 |
3764 | +++ NuxGraphics/Makefile.am 2012-08-01 18:42:21 +0000 |
3765 | @@ -16,11 +16,13 @@ |
3766 | -DG_LOG_DOMAIN=\"NuxGraphics\" \ |
3767 | $(GCC_FLAGS) \ |
3768 | $(NUX_GRAPHICS_CFLAGS) \ |
3769 | - $(MAINTAINER_CFLAGS) |
3770 | + $(MAINTAINER_CFLAGS) \ |
3771 | + $(GEIS_CFLAGS) |
3772 | |
3773 | libnux_graphics_@NUX_API_VERSION@_la_LIBADD = \ |
3774 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
3775 | - $(NUX_GRAPHICS_LIBS) |
3776 | + $(NUX_GRAPHICS_LIBS) \ |
3777 | + $(GEIS_LIBS) |
3778 | |
3779 | libnux_graphics_@NUX_API_VERSION@_la_LDFLAGS = \ |
3780 | $(NUX_LT_LDFLAGS) |
3781 | @@ -90,6 +92,11 @@ |
3782 | $(srcdir)/VirtualKeyCodesX11.h \ |
3783 | $(srcdir)/XInputWindow.h |
3784 | |
3785 | +if HAVE_GEIS |
3786 | +source_h += \ |
3787 | + $(srcdir)/GestureEvent.h |
3788 | +endif |
3789 | + |
3790 | source_cpp = \ |
3791 | $(srcdir)/BitmapFormats.cpp \ |
3792 | $(srcdir)/CairoGraphics.cpp \ |
3793 | @@ -152,6 +159,11 @@ |
3794 | $(srcdir)/RunTimeStats.cpp \ |
3795 | $(srcdir)/XInputWindow.cpp |
3796 | |
3797 | +if HAVE_GEIS |
3798 | +source_cpp += \ |
3799 | + $(srcdir)/GestureEvent.cpp |
3800 | +endif |
3801 | + |
3802 | libnux_graphics_@NUX_API_VERSION@_la_SOURCES = \ |
3803 | $(source_cpp) \ |
3804 | $(source_h) |
3805 | |
3806 | === modified file 'NuxGraphics/nux-graphics.pc.in' |
3807 | --- NuxGraphics/nux-graphics.pc.in 2012-05-25 20:36:09 +0000 |
3808 | +++ NuxGraphics/nux-graphics.pc.in 2012-08-01 18:42:21 +0000 |
3809 | @@ -9,4 +9,4 @@ |
3810 | Libs: -L${libdir} -lnux-graphics-@NUX_API_VERSION@ |
3811 | Cflags: -I${includedir}/Nux-@NUX_API_VERSION@ |
3812 | |
3813 | -Requires: glib-2.0 cairo libpng gdk-pixbuf-2.0 nux-core-@NUX_API_VERSION@ @GL_PKGS@ xxf86vm xinerama libutouch-geis |
3814 | +Requires: glib-2.0 cairo libpng gdk-pixbuf-2.0 nux-core-@NUX_API_VERSION@ @GL_PKGS@ xxf86vm xinerama libgeis |
3815 | |
3816 | === modified file 'configure.ac' |
3817 | --- configure.ac 2012-07-26 00:50:33 +0000 |
3818 | +++ configure.ac 2012-08-01 18:42:21 +0000 |
3819 | @@ -22,7 +22,11 @@ |
3820 | # The number format is : year/month/day |
3821 | # e.g.: december 5th, 2011 is: 20111205 |
3822 | # To make more than one API change in a day, add a number to the date. Like 20111205.xx |
3823 | +<<<<<<< TREE |
3824 | m4_define([nux_abi_version], [20120725.01]) |
3825 | +======= |
3826 | +m4_define([nux_abi_version], [20120801.01]) |
3827 | +>>>>>>> MERGE-SOURCE |
3828 | |
3829 | m4_define([nux_version], |
3830 | [nux_major_version.nux_minor_version.nux_micro_version]) |
3831 | @@ -155,7 +159,6 @@ |
3832 | xxf86vm |
3833 | sigc++-2.0 |
3834 | xinerama |
3835 | - libutouch-geis |
3836 | ) |
3837 | AC_SUBST(NUX_GRAPHICS_CFLAGS) |
3838 | AC_SUBST(NUX_GRAPHICS_LIBS) |
3839 | @@ -193,6 +196,55 @@ |
3840 | AC_SUBST(IBUS_CFLAGS) |
3841 | AC_SUBST(IBUS_LIBS) |
3842 | |
3843 | +dnl ********************************************************* |
3844 | +dnl Enable/disable gestures (geis, from Open Input Framework) |
3845 | +dnl ********************************************************* |
3846 | + |
3847 | +AC_ARG_ENABLE(gestures, |
3848 | + AC_HELP_STRING(--disable-gestures, Disables multitouch gestures support (default: auto-detect)), |
3849 | + [], |
3850 | + [enable_gestures=auto]) |
3851 | + |
3852 | + |
3853 | +# Check for geis as an optional dependency |
3854 | +AS_IF([test "x$enable_gestures" = "xauto"], |
3855 | + [ |
3856 | + PKG_CHECK_MODULES(GEIS, |
3857 | + [libgeis >= 2.2.10], |
3858 | + [have_geis=yes], |
3859 | + [have_geis=no]) |
3860 | + ]) |
3861 | + |
3862 | + |
3863 | +AS_IF([test "x$enable_gestures" = "xyes"], |
3864 | + [ |
3865 | + PKG_CHECK_MODULES(GEIS, |
3866 | + [libgeis >= 2.2.10], |
3867 | + [have_geis=yes], |
3868 | + [ |
3869 | + AC_MSG_ERROR([libgeis not found!]) |
3870 | + have_geis=no |
3871 | + ]) |
3872 | + ]) |
3873 | + |
3874 | +AS_IF([test "x$enable_gestures" = "xno"], |
3875 | + [have_geis=no]) |
3876 | + |
3877 | +AS_IF([test "x$have_geis" = "xyes"], |
3878 | + [ |
3879 | + NUX_GESTURES_SUPPORT="NUX_GESTURES_SUPPORT" |
3880 | + AM_CONDITIONAL(HAVE_GEIS, true) |
3881 | + ], |
3882 | + [ |
3883 | + NUX_GESTURES_SUPPORT="NUX_NO_GESTURES_SUPPORT" |
3884 | + AM_CONDITIONAL(HAVE_GEIS, false) |
3885 | + ]) |
3886 | + |
3887 | +AC_SUBST(NUX_GESTURES_SUPPORT) |
3888 | + |
3889 | +AC_SUBST(GEIS_CFLAGS) |
3890 | +AC_SUBST(GEIS_LIBS) |
3891 | + |
3892 | dnl ************************************ |
3893 | dnl Enable/disable tests |
3894 | dnl ************************************ |
3895 | @@ -336,6 +388,7 @@ |
3896 | Nux/Makefile |
3897 | Nux/nux.pc |
3898 | Nux/ABI.h |
3899 | + Nux/Features.h |
3900 | examples/Makefile |
3901 | tests/Makefile |
3902 | tools/Makefile |
3903 | @@ -371,6 +424,7 @@ |
3904 | echo -e " Build Examples : ${BOLD_WHITE}${enable_examples}${RESET}" |
3905 | echo -e " Build Gpu Tests : ${BOLD_WHITE}${enable_gputests}${RESET}" |
3906 | echo -e " Build Nux Tests : ${BOLD_WHITE}${enable_tests}${RESET}" |
3907 | +echo -e " Gestures support : ${BOLD_WHITE}${have_geis}${RESET}" |
3908 | echo "" |
3909 | |
3910 | echo -e "${RESET}" |
3911 | |
3912 | === modified file 'examples/Makefile.am' |
3913 | --- examples/Makefile.am 2012-05-25 20:36:09 +0000 |
3914 | +++ examples/Makefile.am 2012-08-01 18:42:21 +0000 |
3915 | @@ -31,6 +31,9 @@ |
3916 | # timeline \ |
3917 | # tooltip |
3918 | |
3919 | +if HAVE_GEIS |
3920 | +noinst_PROGRAMS += gestures |
3921 | +endif |
3922 | |
3923 | # We only have to do this AM_ once to affect all the binaries we build from |
3924 | # this Makefile |
3925 | @@ -107,6 +110,11 @@ |
3926 | coverflow_SOURCES = coverflow.cpp |
3927 | coverflow_LDADD = $(ALL_LIBS) |
3928 | |
3929 | +if HAVE_GEIS |
3930 | +gestures_SOURCES = gestures.cpp |
3931 | +gestures_LDADD = $(ALL_LIBS) |
3932 | +endif |
3933 | + |
3934 | # To distribute source add the source code here |
3935 | #sourceexampledir = "$(pkgdatadir)/examples" |
3936 | #sourceexample_DATA = $(combobox_SOURCES) \ |
3937 | |
3938 | === added file 'examples/gestures.cpp' |
3939 | --- examples/gestures.cpp 1970-01-01 00:00:00 +0000 |
3940 | +++ examples/gestures.cpp 2012-08-01 18:42:21 +0000 |
3941 | @@ -0,0 +1,100 @@ |
3942 | +/* |
3943 | + * Copyright 2012 Canonical Ltd. |
3944 | + * |
3945 | + * This program is free software: you can redistribute it and/or modify it |
3946 | + * under the terms of the GNU Lesser General Public License, as |
3947 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
3948 | + * of the License. |
3949 | + * |
3950 | + * This program is distributed in the hope that it will be useful, but |
3951 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3952 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
3953 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
3954 | + * License for more details. |
3955 | + * |
3956 | + * You should have received a copy of both the GNU Lesser General Public |
3957 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
3958 | + * |
3959 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
3960 | + * |
3961 | + */ |
3962 | + |
3963 | +#include "Nux/FloatingWindow.h" |
3964 | +#include "Nux/GesturesSubscription.h" |
3965 | +#include "Nux/Nux.h" |
3966 | +#include "Nux/WindowThread.h" |
3967 | +#include <iostream> |
3968 | + |
3969 | +using namespace nux; |
3970 | + |
3971 | +BaseWindow *window = nullptr; |
3972 | + |
3973 | +class GesturalWindow : public FloatingWindow |
3974 | +{ |
3975 | + NUX_DECLARE_OBJECT_TYPE(GesturalWindow, FloatingWindow); |
3976 | + public: |
3977 | + GesturalWindow(const char *window_name = "", NUX_FILE_LINE_PROTO) |
3978 | + : FloatingWindow(window_name, NUX_FILE_LINE_PARAM) |
3979 | + { |
3980 | + CreateGesturesSubscription(TOUCH_GESTURE, 3); |
3981 | + event_count = 0; |
3982 | + } |
3983 | + |
3984 | + virtual ~GesturalWindow() {} |
3985 | + |
3986 | + virtual GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event) |
3987 | + { |
3988 | + std::cout << "GesturalWindow got GestureEvent " |
3989 | + << ++event_count << "\n"; |
3990 | + |
3991 | + return GestureDeliveryRequest::NONE; |
3992 | + } |
3993 | + |
3994 | + int event_count; |
3995 | +}; |
3996 | +NUX_IMPLEMENT_OBJECT_TYPE(GesturalWindow); |
3997 | + |
3998 | +int GesturesEventInspector(Area* area, Event* event, void* data) |
3999 | +{ |
4000 | + if (event->type < EVENT_GESTURE_BEGIN || event->type > EVENT_GESTURE_END) |
4001 | + return false; |
4002 | + |
4003 | + GestureEvent *gesture_event = static_cast<GestureEvent*>(event); |
4004 | + |
4005 | + if (gesture_event->type == EVENT_GESTURE_BEGIN) |
4006 | + { |
4007 | + std::cout << "Gesture begun\n"; |
4008 | + } |
4009 | + else if (gesture_event->type == EVENT_GESTURE_UPDATE) |
4010 | + std::cout << "Gesture updated\n"; |
4011 | + else |
4012 | + std::cout << "Gesture ended\n"; |
4013 | + |
4014 | + return false; |
4015 | +} |
4016 | + |
4017 | +void UserInterfaceInitialization(NThread* thread, void* InitData) |
4018 | +{ |
4019 | + window = new GesturalWindow(TEXT("Gestural Window"), NUX_TRACKER_LOCATION); |
4020 | + window->SetBaseXY(100, 20); |
4021 | + window->ShowWindow(true); |
4022 | +} |
4023 | + |
4024 | +int main() |
4025 | +{ |
4026 | + // Initialize Nux. |
4027 | + NuxInitialize(0); |
4028 | + |
4029 | + // Create a window thread. |
4030 | + WindowThread* window_thread = CreateGUIThread( |
4031 | + "Gestures", 640, 300, 0, &UserInterfaceInitialization, 0); |
4032 | + |
4033 | + window_thread->InstallEventInspector(GesturesEventInspector, nullptr); |
4034 | + |
4035 | + // Start the main loop. |
4036 | + window_thread->Run(NULL); |
4037 | + |
4038 | + window->Dispose(); // will cause the window to be deleted (as ref count is zeroed) |
4039 | + delete window_thread; |
4040 | + return 0; |
4041 | +} |
4042 | |
4043 | === added file 'tests/FakeGestureEvent.h' |
4044 | --- tests/FakeGestureEvent.h 1970-01-01 00:00:00 +0000 |
4045 | +++ tests/FakeGestureEvent.h 2012-08-01 18:42:21 +0000 |
4046 | @@ -0,0 +1,63 @@ |
4047 | +#ifndef FAKE_GESTURE_EVENT_H |
4048 | +#define FAKE_GESTURE_EVENT_H |
4049 | + |
4050 | +#include <NuxGraphics/GestureEvent.h> |
4051 | +#include <map> |
4052 | + |
4053 | +namespace nux { |
4054 | +class FakeGestureEvent |
4055 | +{ |
4056 | + public: |
4057 | + nux::EventType type; |
4058 | + |
4059 | + int gesture_id; |
4060 | + int gesture_classes; |
4061 | + bool is_direct_touch; |
4062 | + int timestamp; |
4063 | + nux::Point2D<float> focus; |
4064 | + nux::Point2D<float> delta; |
4065 | + float angle; |
4066 | + float angle_delta; |
4067 | + float angular_velocity; |
4068 | + int tap_duration; |
4069 | + nux::Point2D<float> velocity; |
4070 | + float radius; |
4071 | + float radius_delta; |
4072 | + float radial_velocity; |
4073 | + std::vector<nux::TouchPoint> touches; |
4074 | + bool is_construction_finished; |
4075 | + |
4076 | + nux::GestureEvent &ToGestureEvent() |
4077 | + { |
4078 | + event_.type = type; |
4079 | + |
4080 | + event_.gesture_id_ = gesture_id; |
4081 | + event_.gesture_classes_ = gesture_classes; |
4082 | + event_.is_direct_touch_ = is_direct_touch; |
4083 | + event_.timestamp_ = timestamp; |
4084 | + event_.focus_ = focus; |
4085 | + event_.delta_ = delta; |
4086 | + event_.angle_ = angle; |
4087 | + event_.angle_delta_ = angle_delta; |
4088 | + event_.angular_velocity_ = angular_velocity; |
4089 | + event_.tap_duration_ = tap_duration; |
4090 | + event_.velocity_ = velocity; |
4091 | + event_.radius_ = radius; |
4092 | + event_.radius_delta_ = radius_delta; |
4093 | + event_.radial_velocity_ = radial_velocity; |
4094 | + event_.touches_ = touches; |
4095 | + event_.is_construction_finished_ = is_construction_finished; |
4096 | + |
4097 | + return event_; |
4098 | + } |
4099 | + |
4100 | + private: |
4101 | + nux::GestureEvent event_; |
4102 | +}; |
4103 | +} // namespace nux |
4104 | + |
4105 | +// maps a gesture id to its acceptance |
4106 | +// each Accept() increments by one and each Reject() decrements by one. |
4107 | +extern std::map<int, int> g_gesture_acceptance; |
4108 | + |
4109 | +#endif // FAKE_GESTURE_EVENT_H |
4110 | |
4111 | === modified file 'tests/Makefile.am' |
4112 | --- tests/Makefile.am 2012-07-03 11:04:59 +0000 |
4113 | +++ tests/Makefile.am 2012-08-01 18:42:21 +0000 |
4114 | @@ -78,6 +78,13 @@ |
4115 | gtest-nux-windowcompositor.cpp \ |
4116 | gtest-nux-windowthread.cpp |
4117 | |
4118 | +if HAVE_GEIS |
4119 | +gtest_nux_SOURCES += \ |
4120 | + geis_mock.cpp \ |
4121 | + gtest-nux-geisadapter.cpp \ |
4122 | + gtest-nux-gesturebroker.cpp |
4123 | +endif |
4124 | + |
4125 | gtest_nux_CPPFLAGS = \ |
4126 | -I$(srcdir) \ |
4127 | -I$(top_srcdir) \ |
4128 | |
4129 | === added file 'tests/geis_mock.cpp' |
4130 | --- tests/geis_mock.cpp 1970-01-01 00:00:00 +0000 |
4131 | +++ tests/geis_mock.cpp 2012-08-01 18:42:21 +0000 |
4132 | @@ -0,0 +1,483 @@ |
4133 | +/* |
4134 | + * Copyright 2012 Canonical Ltd. |
4135 | + * |
4136 | + * This program is free software: you can redistribute it and/or modify it |
4137 | + * under the terms of the GNU Lesser General Public License, as |
4138 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
4139 | + * of the License. |
4140 | + * |
4141 | + * This program is distributed in the hope that it will be useful, but |
4142 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
4143 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
4144 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
4145 | + * License for more details. |
4146 | + * |
4147 | + * You should have received a copy of both the GNU Lesser General Public |
4148 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
4149 | + * |
4150 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
4151 | + * |
4152 | + */ |
4153 | + |
4154 | +#include <geis/geis.h> |
4155 | +#include "geis_mock.h" |
4156 | + |
4157 | +#include <stdio.h> |
4158 | +#include <string.h> |
4159 | +#include <sys/eventfd.h> |
4160 | +#include <unistd.h> |
4161 | + |
4162 | +//#define DEBUG_PRINT printf("%s mock called!\n", __func__); |
4163 | +#define DEBUG_PRINT |
4164 | + |
4165 | +/******** GeisAttributes helper class *********/ |
4166 | + |
4167 | +GeisAttr GeisAttributes::GetByName(GeisString name) |
4168 | +{ |
4169 | + for (size_t i = 0; i < vector.size(); ++i) |
4170 | + { |
4171 | + GeisAttr attr = vector[i].get(); |
4172 | + if (attr->name.compare(name) == 0) |
4173 | + return attr; |
4174 | + } |
4175 | + return nullptr; |
4176 | +} |
4177 | + |
4178 | +GeisAttr GeisAttributes::GetByIndex(GeisSize index) |
4179 | +{ |
4180 | + if (vector.size() > index) |
4181 | + { |
4182 | + return vector[index].get(); |
4183 | + } |
4184 | + else |
4185 | + { |
4186 | + return nullptr; |
4187 | + } |
4188 | +} |
4189 | + |
4190 | +GeisSize GeisAttributes::Count() |
4191 | +{ |
4192 | + return vector.size(); |
4193 | +} |
4194 | + |
4195 | +void GeisAttributes::AddBoolean(const char *name, GeisBoolean value) |
4196 | +{ |
4197 | + std::unique_ptr<struct _GeisAttr> attr(new struct _GeisAttr); |
4198 | + attr->type = GEIS_ATTR_TYPE_BOOLEAN; |
4199 | + attr->name = name; |
4200 | + attr->b = value; |
4201 | + vector.push_back(std::move(attr)); |
4202 | +} |
4203 | + |
4204 | +void GeisAttributes::AddFloat(const char *name, GeisFloat value) |
4205 | +{ |
4206 | + std::unique_ptr<struct _GeisAttr> attr(new struct _GeisAttr); |
4207 | + attr->type = GEIS_ATTR_TYPE_FLOAT; |
4208 | + attr->name = name; |
4209 | + attr->f = value; |
4210 | + vector.push_back(std::move(attr)); |
4211 | +} |
4212 | + |
4213 | +void GeisAttributes::AddInteger(const char *name, GeisInteger value) |
4214 | +{ |
4215 | + std::unique_ptr<struct _GeisAttr> attr(new struct _GeisAttr); |
4216 | + attr->type = GEIS_ATTR_TYPE_INTEGER; |
4217 | + attr->name = name; |
4218 | + attr->i = value; |
4219 | + vector.push_back(std::move(attr)); |
4220 | +} |
4221 | + |
4222 | +void GeisAttributes::AddPointer(const char *name, void *value) |
4223 | +{ |
4224 | + std::unique_ptr<struct _GeisAttr> attr(new struct _GeisAttr); |
4225 | + attr->type = GEIS_ATTR_TYPE_POINTER; |
4226 | + attr->name = name; |
4227 | + attr->p = value; |
4228 | + vector.push_back(std::move(attr)); |
4229 | +} |
4230 | + |
4231 | +/******* Geis *******/ |
4232 | + |
4233 | +Geis geis_new(GeisString init_arg_name, ...) |
4234 | +{ |
4235 | + DEBUG_PRINT |
4236 | + Geis geis = new struct _Geis; |
4237 | + geis->fd = eventfd(0, EFD_NONBLOCK); |
4238 | + return geis; |
4239 | +} |
4240 | + |
4241 | +GeisStatus geis_delete(Geis geis) |
4242 | +{ |
4243 | + DEBUG_PRINT |
4244 | + close(geis->fd); |
4245 | + delete geis; |
4246 | + return GEIS_STATUS_SUCCESS; |
4247 | +} |
4248 | + |
4249 | +GeisStatus geis_get_configuration(Geis geis, |
4250 | + GeisString configuration_item_name, |
4251 | + void *configuration_item_value) |
4252 | +{ |
4253 | + DEBUG_PRINT |
4254 | + if (strcmp(configuration_item_name, GEIS_CONFIGURATION_FD) == 0) |
4255 | + { |
4256 | + int *fd = static_cast<int*>(configuration_item_value); |
4257 | + *fd = geis->fd; |
4258 | + return GEIS_STATUS_SUCCESS; |
4259 | + } |
4260 | + else |
4261 | + { |
4262 | + return GEIS_STATUS_NOT_SUPPORTED; |
4263 | + } |
4264 | +} |
4265 | + |
4266 | +GeisStatus geis_dispatch_events(Geis geis) |
4267 | +{ |
4268 | + DEBUG_PRINT |
4269 | + return GEIS_STATUS_SUCCESS; |
4270 | +} |
4271 | + |
4272 | +GeisStatus geis_next_event(Geis geis, GeisEvent *event) |
4273 | +{ |
4274 | + DEBUG_PRINT |
4275 | + |
4276 | + if (geis->pending_events.size() > 0) |
4277 | + { |
4278 | + *event = geis->pending_events.front(); |
4279 | + geis->pending_events.pop_front(); |
4280 | + |
4281 | + if (geis->pending_events.size() > 0) |
4282 | + return GEIS_STATUS_CONTINUE; |
4283 | + else |
4284 | + return GEIS_STATUS_SUCCESS; |
4285 | + } |
4286 | + else |
4287 | + { |
4288 | + *event = nullptr; |
4289 | + return GEIS_STATUS_EMPTY; |
4290 | + } |
4291 | +} |
4292 | + |
4293 | +GeisStatus geis_gesture_accept(Geis geis, |
4294 | + GeisGroup group, |
4295 | + GeisGestureId gesture_id) |
4296 | +{ |
4297 | + DEBUG_PRINT |
4298 | + return GEIS_STATUS_SUCCESS; |
4299 | +} |
4300 | + |
4301 | +GeisStatus geis_gesture_reject(Geis geis, |
4302 | + GeisGroup group, |
4303 | + GeisGestureId gesture_id) |
4304 | +{ |
4305 | + DEBUG_PRINT |
4306 | + return GEIS_STATUS_SUCCESS; |
4307 | +} |
4308 | + |
4309 | +/******* Attr *******/ |
4310 | + |
4311 | +GeisString geis_attr_name(GeisAttr attr) |
4312 | +{ |
4313 | + DEBUG_PRINT |
4314 | + return attr->name.c_str(); |
4315 | +} |
4316 | + |
4317 | +GeisAttrType geis_attr_type(GeisAttr attr) |
4318 | +{ |
4319 | + DEBUG_PRINT |
4320 | + return attr->type; |
4321 | +} |
4322 | + |
4323 | +GeisBoolean geis_attr_value_to_boolean(GeisAttr attr) |
4324 | +{ |
4325 | + DEBUG_PRINT |
4326 | + return attr->b; |
4327 | +} |
4328 | + |
4329 | +GeisFloat geis_attr_value_to_float(GeisAttr attr) |
4330 | +{ |
4331 | + DEBUG_PRINT |
4332 | + return attr->f; |
4333 | +} |
4334 | + |
4335 | +GeisInteger geis_attr_value_to_integer(GeisAttr attr) |
4336 | +{ |
4337 | + DEBUG_PRINT |
4338 | + return attr->i; |
4339 | +} |
4340 | + |
4341 | +GeisPointer geis_attr_value_to_pointer(GeisAttr attr) |
4342 | +{ |
4343 | + DEBUG_PRINT |
4344 | + return attr->p; |
4345 | +} |
4346 | + |
4347 | +GeisString geis_attr_value_to_string(GeisAttr attr) |
4348 | +{ |
4349 | + DEBUG_PRINT |
4350 | + return attr->s.c_str(); |
4351 | +} |
4352 | + |
4353 | +/*********** Event **************/ |
4354 | + |
4355 | +void geis_event_delete(GeisEvent event) |
4356 | +{ |
4357 | + DEBUG_PRINT |
4358 | + delete event; |
4359 | +} |
4360 | + |
4361 | +GeisEventType geis_event_type(GeisEvent event) |
4362 | +{ |
4363 | + DEBUG_PRINT |
4364 | + return event->type; |
4365 | +} |
4366 | + |
4367 | +GeisSize geis_event_attr_count(GeisEvent event) |
4368 | +{ |
4369 | + DEBUG_PRINT |
4370 | + return event->attributes.Count(); |
4371 | +} |
4372 | + |
4373 | +GeisAttr geis_event_attr(GeisEvent event, GeisSize index) |
4374 | +{ |
4375 | + DEBUG_PRINT |
4376 | + return event->attributes.GetByIndex(index); |
4377 | +} |
4378 | + |
4379 | +GeisAttr geis_event_attr_by_name(GeisEvent event, GeisString attr_name) |
4380 | +{ |
4381 | + DEBUG_PRINT |
4382 | + return event->attributes.GetByName(attr_name); |
4383 | +} |
4384 | + |
4385 | +/******** Gesture Class ***********/ |
4386 | + |
4387 | +void geis_gesture_class_ref(GeisGestureClass gesture_class) |
4388 | +{ |
4389 | + DEBUG_PRINT |
4390 | +} |
4391 | + |
4392 | +void geis_gesture_class_unref(GeisGestureClass gesture_class) |
4393 | +{ |
4394 | + DEBUG_PRINT |
4395 | +} |
4396 | + |
4397 | +GeisString geis_gesture_class_name(GeisGestureClass gesture_class) |
4398 | +{ |
4399 | + DEBUG_PRINT |
4400 | + return const_cast<char *>(gesture_class->name.c_str()); |
4401 | +} |
4402 | + |
4403 | +GeisInteger geis_gesture_class_id(GeisGestureClass gesture_class) |
4404 | +{ |
4405 | + DEBUG_PRINT |
4406 | + return gesture_class->id; |
4407 | +} |
4408 | + |
4409 | +GeisSize geis_gesture_class_attr_count(GeisGestureClass gesture_class) |
4410 | +{ |
4411 | + DEBUG_PRINT |
4412 | + return gesture_class->attributes.Count(); |
4413 | +} |
4414 | + |
4415 | +GeisAttr geis_gesture_class_attr(GeisGestureClass gesture_class, int index) |
4416 | +{ |
4417 | + DEBUG_PRINT |
4418 | + return gesture_class->attributes.GetByIndex(index); |
4419 | +} |
4420 | + |
4421 | +/********* Device ***********/ |
4422 | + |
4423 | +GeisInteger geis_device_id(GeisDevice device) |
4424 | +{ |
4425 | + DEBUG_PRINT |
4426 | + return device->id; |
4427 | +} |
4428 | + |
4429 | +GeisAttr geis_device_attr_by_name(GeisDevice device, GeisString attr_name) |
4430 | +{ |
4431 | + DEBUG_PRINT |
4432 | + return device->attributes.GetByName(attr_name); |
4433 | +} |
4434 | + |
4435 | +/********** Group Set **********/ |
4436 | + |
4437 | +GeisSize geis_groupset_group_count(GeisGroupSet groupset) |
4438 | +{ |
4439 | + DEBUG_PRINT |
4440 | + return groupset->vector.size(); |
4441 | +} |
4442 | + |
4443 | +GeisGroup geis_groupset_group(GeisGroupSet groupset, GeisSize index) |
4444 | +{ |
4445 | + DEBUG_PRINT |
4446 | + return groupset->vector[index]; |
4447 | +} |
4448 | + |
4449 | +/****** Group ******/ |
4450 | + |
4451 | +GeisSize geis_group_frame_count(GeisGroup group) |
4452 | +{ |
4453 | + DEBUG_PRINT |
4454 | + return group->vector.size(); |
4455 | +} |
4456 | + |
4457 | +GeisFrame geis_group_frame(GeisGroup group, GeisSize index) |
4458 | +{ |
4459 | + DEBUG_PRINT |
4460 | + return group->vector[index]; |
4461 | +} |
4462 | + |
4463 | +/***** Touch Set *****/ |
4464 | + |
4465 | +GeisSize geis_touchset_touch_count(GeisTouchSet touchset) |
4466 | +{ |
4467 | + DEBUG_PRINT |
4468 | + return touchset->vector.size(); |
4469 | +} |
4470 | + |
4471 | +GeisTouch geis_touchset_touch(GeisTouchSet touchset, GeisSize index) |
4472 | +{ |
4473 | + DEBUG_PRINT |
4474 | + return touchset->vector[index]; |
4475 | +} |
4476 | + |
4477 | +/***** Touch ******/ |
4478 | + |
4479 | +GeisTouchId geis_touch_id(GeisTouch touch) |
4480 | +{ |
4481 | + DEBUG_PRINT |
4482 | + return touch->id; |
4483 | +} |
4484 | + |
4485 | +GeisSize geis_touch_attr_count(GeisTouch touch) |
4486 | +{ |
4487 | + DEBUG_PRINT |
4488 | + return touch->attributes.Count(); |
4489 | +} |
4490 | + |
4491 | +GeisAttr geis_touch_attr(GeisTouch touch, GeisSize index) |
4492 | +{ |
4493 | + DEBUG_PRINT |
4494 | + return touch->attributes.GetByIndex(index); |
4495 | +} |
4496 | + |
4497 | +GeisAttr geis_touch_attr_by_name(GeisTouch touch, GeisString name) |
4498 | +{ |
4499 | + DEBUG_PRINT |
4500 | + return touch->attributes.GetByName(name); |
4501 | +} |
4502 | + |
4503 | +/***** Frame *****/ |
4504 | + |
4505 | +GeisGestureId geis_frame_id(GeisFrame frame) |
4506 | +{ |
4507 | + DEBUG_PRINT |
4508 | + return frame->id; |
4509 | +} |
4510 | + |
4511 | +GeisBoolean geis_frame_is_class(GeisFrame frame, |
4512 | + GeisGestureClass gesture_class) |
4513 | +{ |
4514 | + DEBUG_PRINT |
4515 | + std::set<int>::iterator it = frame->class_ids.find(gesture_class->id); |
4516 | + if (it == frame->class_ids.end()) |
4517 | + return GEIS_FALSE; |
4518 | + else |
4519 | + return GEIS_TRUE; |
4520 | +} |
4521 | + |
4522 | +GeisSize geis_frame_attr_count(GeisFrame frame) |
4523 | +{ |
4524 | + DEBUG_PRINT |
4525 | + return frame->attributes.Count(); |
4526 | +} |
4527 | + |
4528 | +GeisAttr geis_frame_attr(GeisFrame frame, GeisSize index) |
4529 | +{ |
4530 | + DEBUG_PRINT |
4531 | + return frame->attributes.GetByIndex(index); |
4532 | +} |
4533 | + |
4534 | +GeisAttr geis_frame_attr_by_name(GeisFrame frame, GeisString name) |
4535 | +{ |
4536 | + DEBUG_PRINT |
4537 | + return frame->attributes.GetByName(name); |
4538 | +} |
4539 | + |
4540 | +/***** Subscription *****/ |
4541 | + |
4542 | +GeisSubscription geis_subscription_new(Geis geis, |
4543 | + GeisString name, |
4544 | + GeisSubscriptionFlags flags) |
4545 | +{ |
4546 | + DEBUG_PRINT |
4547 | + return new struct _GeisSubscription; |
4548 | +} |
4549 | + |
4550 | +GeisStatus geis_subscription_delete(GeisSubscription subscription) |
4551 | +{ |
4552 | + DEBUG_PRINT |
4553 | + for (auto filter : subscription->filters) |
4554 | + { |
4555 | + delete filter; |
4556 | + } |
4557 | + delete subscription; |
4558 | + return GEIS_STATUS_SUCCESS; |
4559 | +} |
4560 | + |
4561 | +GeisStatus geis_subscription_activate(GeisSubscription subscription) |
4562 | +{ |
4563 | + DEBUG_PRINT |
4564 | + return GEIS_STATUS_SUCCESS; |
4565 | +} |
4566 | + |
4567 | +GeisStatus geis_subscription_deactivate(GeisSubscription subscription) |
4568 | +{ |
4569 | + DEBUG_PRINT |
4570 | + return GEIS_STATUS_SUCCESS; |
4571 | +} |
4572 | + |
4573 | +GeisString geis_subscription_name(GeisSubscription subscription) |
4574 | +{ |
4575 | + DEBUG_PRINT |
4576 | + return const_cast<char *>(subscription->name.c_str()); |
4577 | +} |
4578 | + |
4579 | +GeisInteger geis_subscription_id(GeisSubscription subscription) |
4580 | +{ |
4581 | + DEBUG_PRINT |
4582 | + return subscription->id; |
4583 | +} |
4584 | + |
4585 | +GeisStatus geis_subscription_add_filter(GeisSubscription subscription, |
4586 | + GeisFilter filter) |
4587 | +{ |
4588 | + DEBUG_PRINT |
4589 | + subscription->filters.push_back(filter); |
4590 | + return GEIS_STATUS_SUCCESS; |
4591 | +} |
4592 | + |
4593 | +GeisFilter geis_subscription_filter_by_name(GeisSubscription sub, |
4594 | + GeisString name) |
4595 | +{ |
4596 | + DEBUG_PRINT |
4597 | + return nullptr; |
4598 | +} |
4599 | + |
4600 | +GeisStatus geis_subscription_remove_filter(GeisSubscription subscription, |
4601 | + GeisFilter filter) |
4602 | +{ |
4603 | + DEBUG_PRINT |
4604 | + std::vector<GeisFilter>::iterator it = subscription->filters.begin(); |
4605 | + while (it != subscription->filters.end()) |
4606 | + { |
4607 | + if (filter == *it) |
4608 | + { |
4609 | + subscription->filters.erase(it); |
4610 | + return GEIS_STATUS_SUCCESS; |
4611 | + } |
4612 | + ++it; |
4613 | + } |
4614 | + return GEIS_STATUS_BAD_ARGUMENT; |
4615 | +} |
4616 | |
4617 | === added file 'tests/geis_mock.h' |
4618 | --- tests/geis_mock.h 1970-01-01 00:00:00 +0000 |
4619 | +++ tests/geis_mock.h 2012-08-01 18:42:21 +0000 |
4620 | @@ -0,0 +1,148 @@ |
4621 | +/* |
4622 | + * Copyright 2012 Canonical Ltd. |
4623 | + * |
4624 | + * This program is free software: you can redistribute it and/or modify it |
4625 | + * under the terms of the GNU Lesser General Public License, as |
4626 | + * published by the Free Software Foundation; either version 2.1 or 3.0 |
4627 | + * of the License. |
4628 | + * |
4629 | + * This program is distributed in the hope that it will be useful, but |
4630 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
4631 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
4632 | + * PURPOSE. See the applicable version of the GNU Lesser General Public |
4633 | + * License for more details. |
4634 | + * |
4635 | + * You should have received a copy of both the GNU Lesser General Public |
4636 | + * License along with this program. If not, see <http://www.gnu.org/licenses/> |
4637 | + * |
4638 | + * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com> |
4639 | + * |
4640 | + */ |
4641 | + |
4642 | +#ifndef GEIS_MOCK_H |
4643 | +#define GEIS_MOCK_H |
4644 | + |
4645 | +#include <memory> |
4646 | +#include <set> |
4647 | +#include <string> |
4648 | +#include <vector> |
4649 | +#include <list> |
4650 | + |
4651 | +/* helper class */ |
4652 | +class GeisAttributes |
4653 | +{ |
4654 | + public: |
4655 | + GeisAttr GetByName(GeisString name); |
4656 | + GeisAttr GetByIndex(GeisSize index); |
4657 | + GeisSize Count(); |
4658 | + void AddBoolean(const char *name, GeisBoolean value); |
4659 | + void AddFloat(const char *name, GeisFloat value); |
4660 | + void AddInteger(const char *name, GeisInteger value); |
4661 | + void AddPointer(const char *name, void *value); |
4662 | + std::vector< std::unique_ptr<struct _GeisAttr> > vector; |
4663 | +}; |
4664 | + |
4665 | +struct _GeisAttr |
4666 | +{ |
4667 | + GeisAttrType type; |
4668 | + std::string name; |
4669 | + |
4670 | + GeisBoolean b; |
4671 | + GeisFloat f; |
4672 | + GeisInteger i; |
4673 | + std::string s; |
4674 | + void* p; |
4675 | +}; |
4676 | + |
4677 | +struct GeisStructWithAttributes |
4678 | +{ |
4679 | + GeisAttributes attributes; |
4680 | + |
4681 | + bool GetBoolean(const char *name) |
4682 | + { |
4683 | + return attributes.GetByName(name)->b; |
4684 | + } |
4685 | + |
4686 | + float GetFloat(const char *name) |
4687 | + { |
4688 | + return attributes.GetByName(name)->f; |
4689 | + } |
4690 | + |
4691 | + int GetInteger(const char *name) |
4692 | + { |
4693 | + return attributes.GetByName(name)->i; |
4694 | + } |
4695 | + |
4696 | + std::string GetString(const char *name) |
4697 | + { |
4698 | + return attributes.GetByName(name)->s; |
4699 | + } |
4700 | + |
4701 | + void* GetPointer(const char *name) |
4702 | + { |
4703 | + return attributes.GetByName(name)->p; |
4704 | + } |
4705 | +}; |
4706 | + |
4707 | +struct _Geis |
4708 | +{ |
4709 | + int fd; |
4710 | + std::list<GeisEvent> pending_events; |
4711 | +}; |
4712 | + |
4713 | +struct _GeisGestureClass : GeisStructWithAttributes |
4714 | +{ |
4715 | + int id; |
4716 | + std::string name; |
4717 | +}; |
4718 | + |
4719 | +struct _GeisEvent : GeisStructWithAttributes |
4720 | +{ |
4721 | + GeisEventType type; |
4722 | +}; |
4723 | + |
4724 | +struct _GeisDevice : GeisStructWithAttributes |
4725 | +{ |
4726 | + int id; |
4727 | +}; |
4728 | + |
4729 | +struct _GeisGroup |
4730 | +{ |
4731 | + int id; |
4732 | + std::vector<GeisFrame> vector; |
4733 | +}; |
4734 | + |
4735 | +struct _GeisGroupSet |
4736 | +{ |
4737 | + std::vector<GeisGroup> vector; |
4738 | +}; |
4739 | + |
4740 | +struct _GeisTouch : GeisStructWithAttributes |
4741 | +{ |
4742 | + int id; |
4743 | +}; |
4744 | + |
4745 | +struct _GeisTouchSet |
4746 | +{ |
4747 | + std::vector<GeisTouch> vector; |
4748 | +}; |
4749 | + |
4750 | +struct _GeisFrame : GeisStructWithAttributes |
4751 | +{ |
4752 | + int id; |
4753 | + std::set<int> class_ids; |
4754 | +}; |
4755 | + |
4756 | +struct _GeisSubscription |
4757 | +{ |
4758 | + int id; |
4759 | + std::string name; |
4760 | + std::vector<GeisFilter> filters; |
4761 | +}; |
4762 | + |
4763 | +struct _GeisFilter |
4764 | +{ |
4765 | + int foo; |
4766 | +}; |
4767 | + |
4768 | +#endif |
4769 | |
4770 | === added file 'tests/gtest-nux-geisadapter.cpp' |
4771 | --- tests/gtest-nux-geisadapter.cpp 1970-01-01 00:00:00 +0000 |
4772 | +++ tests/gtest-nux-geisadapter.cpp 2012-08-01 18:42:21 +0000 |
4773 | @@ -0,0 +1,230 @@ |
4774 | +#include <gtest/gtest.h> |
4775 | + |
4776 | +#include <Nux/GeisAdapter.h> |
4777 | +#include "geis_mock.h" |
4778 | +#include <list> |
4779 | + |
4780 | +class GeisAdapterTest : public ::testing::Test |
4781 | +{ |
4782 | + public: |
4783 | + |
4784 | + GeisAdapterTest() |
4785 | + { |
4786 | + adapter_emitted_init_complete = false; |
4787 | + } |
4788 | + |
4789 | + void AddGestureClass(Geis geis, GeisGestureClass gesture_class) |
4790 | + { |
4791 | + GeisEvent event = new struct _GeisEvent; |
4792 | + event->type = GEIS_EVENT_CLASS_AVAILABLE; |
4793 | + event->attributes.AddPointer(GEIS_EVENT_ATTRIBUTE_CLASS, gesture_class); |
4794 | + geis->pending_events.push_back(event); |
4795 | + } |
4796 | + |
4797 | + void AddDevice(Geis geis, GeisDevice device) |
4798 | + { |
4799 | + GeisEvent event = new struct _GeisEvent; |
4800 | + event->type = GEIS_EVENT_DEVICE_AVAILABLE; |
4801 | + event->attributes.AddPointer(GEIS_EVENT_ATTRIBUTE_DEVICE, device); |
4802 | + geis->pending_events.push_back(event); |
4803 | + } |
4804 | + |
4805 | + void FinishInitialization(Geis geis) |
4806 | + { |
4807 | + GeisEvent event = new struct _GeisEvent; |
4808 | + event->type = GEIS_EVENT_INIT_COMPLETE; |
4809 | + geis->pending_events.push_back(event); |
4810 | + } |
4811 | + |
4812 | + void StoreNuxGestureEvent(nux::GestureEvent &gesture_event) |
4813 | + { |
4814 | + gesture_events.push_back(gesture_event); |
4815 | + } |
4816 | + |
4817 | + void MarkInitCompleteAsEmitted() |
4818 | + { |
4819 | + adapter_emitted_init_complete = true; |
4820 | + } |
4821 | + |
4822 | + std::list<nux::GestureEvent> gesture_events; |
4823 | + bool adapter_emitted_init_complete; |
4824 | +}; |
4825 | + |
4826 | +/* |
4827 | + Send a GEIS_EVENT_INIT_COMPLETE and check if GeisAdapter emits init_complete |
4828 | + and IsInitComplete() returns a correct value. |
4829 | + */ |
4830 | +TEST_F(GeisAdapterTest, SignalsInitComplete) |
4831 | +{ |
4832 | + nux::GeisAdapter geis_adapter; |
4833 | + Geis geis = geis_adapter.GetGeisInstance(); |
4834 | + |
4835 | + ASSERT_FALSE(geis_adapter.IsInitComplete()); |
4836 | + |
4837 | + geis_adapter.init_complete.connect(sigc::mem_fun(this, |
4838 | + &GeisAdapterTest::MarkInitCompleteAsEmitted)); |
4839 | + |
4840 | + FinishInitialization(geis); |
4841 | + |
4842 | + geis_adapter.ProcessGeisEvents(); |
4843 | + |
4844 | + ASSERT_TRUE(adapter_emitted_init_complete); |
4845 | + ASSERT_TRUE(geis_adapter.IsInitComplete()); |
4846 | +} |
4847 | + |
4848 | +/* |
4849 | + First send some necessary initialization events. One to define a multi-touch device, |
4850 | + another defining a gesture class and one that tells that the initialization |
4851 | + has completed. |
4852 | + |
4853 | + After that send a well formed GEIS_EVENT_GESTURE_BEGIN and check if GeisAdapter |
4854 | + emits a corresponding nux::GestureEvent. |
4855 | + */ |
4856 | +TEST_F(GeisAdapterTest, EmitsWellFormedNuxGestureEvent) |
4857 | +{ |
4858 | + nux::GeisAdapter geis_adapter; |
4859 | + Geis geis = geis_adapter.GetGeisInstance(); |
4860 | + |
4861 | + struct _GeisGestureClass drag_class; |
4862 | + drag_class.id = 12; |
4863 | + drag_class.name = GEIS_GESTURE_DRAG; |
4864 | + AddGestureClass(geis, &drag_class); |
4865 | + |
4866 | + struct _GeisDevice device; |
4867 | + device.id = 7; |
4868 | + device.attributes.AddBoolean(GEIS_DEVICE_ATTRIBUTE_DIRECT_TOUCH, true); |
4869 | + AddDevice(geis, &device); |
4870 | + |
4871 | + FinishInitialization(geis); |
4872 | + |
4873 | + struct _GeisFrame frame; |
4874 | + frame.id = 321; |
4875 | + frame.class_ids.insert(drag_class.id); |
4876 | + frame.attributes.AddInteger(GEIS_GESTURE_ATTRIBUTE_DEVICE_ID, device.id); |
4877 | + frame.attributes.AddFloat(GEIS_GESTURE_ATTRIBUTE_FOCUS_X, 87.3f); |
4878 | + frame.attributes.AddFloat(GEIS_GESTURE_ATTRIBUTE_FOCUS_Y, 123.4f); |
4879 | + |
4880 | + struct _GeisGroup group; |
4881 | + group.vector.push_back(&frame); |
4882 | + |
4883 | + struct _GeisGroupSet group_set; |
4884 | + group_set.vector.push_back(&group); |
4885 | + |
4886 | + struct _GeisTouch touch; |
4887 | + touch.id = 0; |
4888 | + touch.attributes.AddFloat(GEIS_TOUCH_ATTRIBUTE_ID, 123); |
4889 | + touch.attributes.AddFloat(GEIS_TOUCH_ATTRIBUTE_X, 777.7f); |
4890 | + touch.attributes.AddFloat(GEIS_TOUCH_ATTRIBUTE_Y, 888.8f); |
4891 | + |
4892 | + struct _GeisTouchSet touch_set; |
4893 | + touch_set.vector.push_back(&touch); |
4894 | + |
4895 | + GeisEvent event = new struct _GeisEvent; |
4896 | + event->type = GEIS_EVENT_GESTURE_BEGIN; |
4897 | + event->attributes.AddPointer(GEIS_EVENT_ATTRIBUTE_GROUPSET, &group_set); |
4898 | + event->attributes.AddPointer(GEIS_EVENT_ATTRIBUTE_TOUCHSET, &touch_set); |
4899 | + event->attributes.AddBoolean(GEIS_EVENT_ATTRIBUTE_CONSTRUCTION_FINISHED, true); |
4900 | + geis->pending_events.push_back(event); |
4901 | + |
4902 | + geis_adapter.event_ready.connect(sigc::mem_fun(this, |
4903 | + &GeisAdapterTest::StoreNuxGestureEvent)); |
4904 | + |
4905 | + geis_adapter.ProcessGeisEvents(); |
4906 | + |
4907 | + ASSERT_EQ(1, gesture_events.size()); |
4908 | + |
4909 | + const nux::GestureEvent &gesture_event = gesture_events.front(); |
4910 | + |
4911 | + ASSERT_EQ(nux::EVENT_GESTURE_BEGIN, gesture_event.type); |
4912 | + |
4913 | + ASSERT_EQ(frame.id, gesture_event.GetGestureId()); |
4914 | + |
4915 | + ASSERT_EQ(device.GetBoolean(GEIS_DEVICE_ATTRIBUTE_DIRECT_TOUCH), |
4916 | + gesture_event.IsDirectTouch()); |
4917 | + |
4918 | + ASSERT_FLOAT_EQ(frame.GetFloat(GEIS_GESTURE_ATTRIBUTE_FOCUS_X), |
4919 | + gesture_event.GetFocus().x); |
4920 | + |
4921 | + ASSERT_FLOAT_EQ(frame.GetFloat(GEIS_GESTURE_ATTRIBUTE_FOCUS_Y), |
4922 | + gesture_event.GetFocus().y); |
4923 | + |
4924 | + ASSERT_EQ(touch_set.vector.size(), gesture_event.GetTouches().size()); |
4925 | + for (size_t i = 0; i < gesture_event.GetTouches().size(); ++i) |
4926 | + { |
4927 | + ASSERT_FLOAT_EQ(touch_set.vector[i]->GetInteger(GEIS_TOUCH_ATTRIBUTE_ID), |
4928 | + gesture_event.GetTouches()[i].id); |
4929 | + |
4930 | + ASSERT_FLOAT_EQ(touch_set.vector[i]->GetFloat(GEIS_TOUCH_ATTRIBUTE_X), |
4931 | + gesture_event.GetTouches()[i].x); |
4932 | + |
4933 | + ASSERT_FLOAT_EQ(touch_set.vector[i]->GetFloat(GEIS_TOUCH_ATTRIBUTE_Y), |
4934 | + gesture_event.GetTouches()[i].y); |
4935 | + } |
4936 | + |
4937 | +} |
4938 | + |
4939 | +/* |
4940 | + Checks that when a Tap Update GeisEvent is received, GeisAdpater generates |
4941 | + two events from it: A Tap Begin and a Tap End. |
4942 | + */ |
4943 | +TEST_F(GeisAdapterTest, SplitTapUpdateIntoTapBeginAndTapEnd) |
4944 | +{ |
4945 | + nux::GeisAdapter geis_adapter; |
4946 | + Geis geis = geis_adapter.GetGeisInstance(); |
4947 | + |
4948 | + struct _GeisGestureClass tap_class; |
4949 | + tap_class.id = 12; |
4950 | + tap_class.name = GEIS_GESTURE_TAP; |
4951 | + AddGestureClass(geis, &tap_class); |
4952 | + |
4953 | + struct _GeisDevice device; |
4954 | + device.id = 7; |
4955 | + device.attributes.AddBoolean(GEIS_DEVICE_ATTRIBUTE_DIRECT_TOUCH, true); |
4956 | + AddDevice(geis, &device); |
4957 | + |
4958 | + FinishInitialization(geis); |
4959 | + |
4960 | + struct _GeisFrame frame; |
4961 | + frame.id = 321; |
4962 | + frame.class_ids.insert(tap_class.id); |
4963 | + frame.attributes.AddInteger(GEIS_GESTURE_ATTRIBUTE_DEVICE_ID, device.id); |
4964 | + frame.attributes.AddFloat(GEIS_GESTURE_ATTRIBUTE_FOCUS_X, 87.3f); |
4965 | + frame.attributes.AddFloat(GEIS_GESTURE_ATTRIBUTE_FOCUS_Y, 123.4f); |
4966 | + |
4967 | + struct _GeisGroup group; |
4968 | + group.vector.push_back(&frame); |
4969 | + |
4970 | + struct _GeisGroupSet group_set; |
4971 | + group_set.vector.push_back(&group); |
4972 | + |
4973 | + struct _GeisTouch touch; |
4974 | + touch.id = 0; |
4975 | + touch.attributes.AddFloat(GEIS_TOUCH_ATTRIBUTE_ID, 123); |
4976 | + touch.attributes.AddFloat(GEIS_TOUCH_ATTRIBUTE_X, 777.7f); |
4977 | + touch.attributes.AddFloat(GEIS_TOUCH_ATTRIBUTE_Y, 888.8f); |
4978 | + |
4979 | + struct _GeisTouchSet touch_set; |
4980 | + touch_set.vector.push_back(&touch); |
4981 | + |
4982 | + GeisEvent event = new struct _GeisEvent; |
4983 | + event->type = GEIS_EVENT_GESTURE_UPDATE; |
4984 | + event->attributes.AddPointer(GEIS_EVENT_ATTRIBUTE_GROUPSET, &group_set); |
4985 | + event->attributes.AddPointer(GEIS_EVENT_ATTRIBUTE_TOUCHSET, &touch_set); |
4986 | + event->attributes.AddBoolean(GEIS_EVENT_ATTRIBUTE_CONSTRUCTION_FINISHED, false); |
4987 | + geis->pending_events.push_back(event); |
4988 | + |
4989 | + geis_adapter.event_ready.connect(sigc::mem_fun(this, |
4990 | + &GeisAdapterTest::StoreNuxGestureEvent)); |
4991 | + |
4992 | + geis_adapter.ProcessGeisEvents(); |
4993 | + |
4994 | + ASSERT_EQ(2, gesture_events.size()); |
4995 | + |
4996 | + ASSERT_EQ(nux::EVENT_GESTURE_BEGIN, gesture_events.front().type); |
4997 | + ASSERT_EQ(nux::TAP_GESTURE, gesture_events.front().GetGestureClasses()); |
4998 | + ASSERT_TRUE(gesture_events.front().IsConstructionFinished()); |
4999 | + |
5000 | + ASSERT_EQ(nux::EVENT_GESTURE_END, gesture_events.back().type); |
It's worth noting that the Unity compiz plugin will have to be changed in order to work with a nux version that has gestures support. Currently it uses utouch-geis directly. With that nux patch in, it will have to use gestures via the nux API instead.