Nux

Merge lp:~dandrader/nux/geis into lp:nux

Proposed by Jay Taoko
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
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

Description of the change

Adding gestures support to Nux.

To post a comment you must log in.
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

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.

Revision history for this message
Chase Douglas (chasedouglas) wrote :

* Please use one of the c++ or NUX-specific casts. For example:

GeisAdapterEventSource *event_source = (GeisAdapterEventSource*) source;

should be:

GeisAdapterEventSource *event_source = reinterpret_cast<GeisAdapterEventSource*>(source);

* Why is GeisAdapter::nux_event_ a member instead of a local variable in ProcessGeisEvents?

* 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.

review: Needs Fixing
Revision history for this message
Jay Taoko (jaytaoko) wrote :

I get a build error:

make[2]: Entering directory `/home/jay/inalogic/build/nux.geis/Nux'
  CXX libnux_2.0_la-GeisAdapter.lo
./GeisAdapter.cpp: In member function 'void nux::GeisAdapter::FillNuxEvent(nux::GestureEvent&, GeisEvent, nux::EventType)':
./GeisAdapter.cpp:344:41: error: 'GEIS_EVENT_ATTRIBUTE_CONSTRUCTION_FINISHED' 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.

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> I get a build error:
>
> make[2]: Entering directory `/home/jay/inalogic/build/nux.geis/Nux'
> CXX libnux_2.0_la-GeisAdapter.lo
> ./GeisAdapter.cpp: In member function 'void
> nux::GeisAdapter::FillNuxEvent(nux::GestureEvent&, GeisEvent,
> nux::EventType)':
> ./GeisAdapter.cpp:344:41: error: 'GEIS_EVENT_ATTRIBUTE_CONSTRUCTION_FINISHED'
> 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.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Back to "work in progress" status as GesturesSubscription API will need to be a bit more powerful to cater for Unity Shell compiz plugin use case.

Revision history for this message
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.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> * Why is GeisAdapter::nux_event_ a member instead of a local variable in
> ProcessGeisEvents?

It's a small optimization to avoid unnecessary memory allocations.

lp:~dandrader/nux/geis updated
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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
Chase Douglas (chasedouglas) wrote :

> > * Why is GeisAdapter::nux_event_ a member instead of a local variable in
> > 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.

review: Needs Fixing
Revision history for this message
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.

review: Needs Information
Revision history for this message
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 :).

Revision history for this message
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?

review: Needs Fixing
Revision history for this message
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.

Revision history for this message
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?

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> > > * Why is GeisAdapter::nux_event_ a member instead of a local variable in
> > > 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.

Revision history for this message
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::Accept() and
GestureEvent::Reject() const methods too. There's nothing in those
methods that modify the GestureEvent object.

Revision history for this message
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::Accept() and
> GestureEvent::Reject() const methods too. There's nothing in those
> 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.

Revision history for this message
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::Accept() and
>> GestureEvent::Reject() const methods too. There's nothing in those
>> 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 :).

Revision history for this message
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::Accept() and
> >> GestureEvent::Reject() const methods too. There's nothing in those
> >> 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 :)

Revision history for this message
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::Accept() and
> > >> GestureEvent::Reject() const methods too. There's nothing in those
> > >> 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!

review: Approve
lp:~dandrader/nux/geis updated
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::OnPreeditUpdate.. 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)

TextEntryComposeSeqs: updated with new sequencies computed using compose key list generator script

List based on:
http://cgit.freedesktop.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 GesturesSubscriptions 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 GesturesSubscription

656. By Daniel d'Andrada

GeisAdapter: make nux_event_ a local variable

As it's only used iniside ProcessGeisEvents().
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

Revision history for this message
Jay Taoko (jaytaoko) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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);
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches