Merge lp://staging/~chasedouglas/frame/v2 into lp://staging/frame

Proposed by Chase Douglas
Status: Merged
Merged at revision: 45
Proposed branch: lp://staging/~chasedouglas/frame/v2
Merge into: lp://staging/frame
Diff against target: 6387 lines (+6005/-12)
46 files modified
.bzrignore (+4/-0)
Makefile.am (+4/-1)
configure.ac (+7/-1)
doc/Doxyfile (+1749/-0)
doc/Makefile.am (+42/-0)
include/utouch/frame-mtdev.h (+14/-0)
include/utouch/frame-xi2.h (+14/-0)
include/utouch/frame.h (+624/-3)
include/utouch/frame_x11.h (+136/-0)
src/Makefile.am (+40/-6)
src/libutouch-frame.ver (+47/-0)
src/v2/axis.cpp (+54/-0)
src/v2/axis.h (+55/-0)
src/v2/device.cpp (+87/-0)
src/v2/device.h (+56/-0)
src/v2/event.cpp (+106/-0)
src/v2/event.h (+55/-0)
src/v2/frame.cpp (+258/-0)
src/v2/frame.h (+68/-0)
src/v2/handle.cpp (+87/-0)
src/v2/handle.h (+59/-0)
src/v2/property.h (+78/-0)
src/v2/touch.cpp (+171/-0)
src/v2/touch.h (+63/-0)
src/v2/typedefs.h (+41/-0)
src/v2/value.cpp (+201/-0)
src/v2/value.h (+99/-0)
src/v2/window.cpp (+47/-0)
src/v2/window.h (+53/-0)
src/v2/x11/device_x11.cpp (+222/-0)
src/v2/x11/device_x11.h (+66/-0)
src/v2/x11/frame_x11.cpp (+74/-0)
src/v2/x11/handle_x11.cpp (+182/-0)
src/v2/x11/handle_x11.h (+63/-0)
src/v2/x11/window_x11.cpp (+198/-0)
src/v2/x11/window_x11.h (+66/-0)
tools/Makefile.am (+17/-0)
tools/common-defs.h (+1/-1)
tools/common/device.c (+188/-0)
tools/common/device.h (+28/-0)
tools/common/frame.c (+80/-0)
tools/common/frame.h (+24/-0)
tools/common/touch.c (+213/-0)
tools/common/touch.h (+25/-0)
tools/utouch-frame-test-x11.c (+221/-0)
tools/utouch-frame-test-x11.txt (+18/-0)
To merge this branch: bzr merge lp://staging/~chasedouglas/frame/v2
Reviewer Review Type Date Requested Status
Stephen M. Webb (community) Approve
Chase Douglas (community) Needs Resubmitting
Jussi Pakkanen (community) Approve
Review via email: mp+81794@code.staging.launchpad.net

This proposal supersedes a proposal from 2011-08-27.

Description of the change

This branch includes all the agreed upon changes.

Original description
====================
This adds a v2.x layer of the uTouch-Frame library. It co-exists with the v1.x layer for now, but the goal should be to remove the v1.x layer once we have moved to the client-side architecture. Differences and reasons for an upgrade:

* v1 has a push and pull api, there was a queue, but no way to signal that data was available.
- v2 has a queue and provides an eventfd signaling interface.

* v1 uses a ring buffer of public structures as its api. This creates ABI breakage every time the structure is extended.
- v2 uses opaque objects retrieved from a queue in an opaque object.

* v1 works on a per-device level. This means a new context must be created for every new device, adding extra overhead and management.
- v2 uses one context and provides events when devices are added/removed

* In order to be fast, v1 allocated all memory up front. The user is required to specify at context creation time how much buffering to use. If you specified too much you wasted memory; if you specified too little you might overrun your buffer. The amount is hard to determine, and the best choice is probably machine and load specific.
- v2 uses C++ under the covers, and objects that are frequently created and destroyed are allocated from pools of memory, either through the standard library in the case of std::vector and std::queue or through __gnu_cxx::bitmap_allocator for other std templates and objects. __gnu_cxx::bitmap_allocator allocates memory pools of exponentially increasing size, so malloc and free should rarely be called after context creation. Further, multiple objects from different devices/frames/etc use the same allocator pool, so having three touch devices does not necessarily mean three times the memory usage.

* v1 copies the all the touches from one frame to the next when a new frame is created.
- v2 uses std::shared_ptr and only copies the shared_ptr context from frame to frame when a touch is unchanged. Thus, a five touch frame where only one touch has changed copies and consumes the memory of only one touch instead of five.

* v1 calculates the velocity of each touch
- v2 drops this. I feel it should be done elsewhere

* v1 provides pointers to other objects in the ring buffer as though it were a linked list to allow for the user to see the state of a touch in a previous frame. However, there was no guarantee that the pointers were valid beyond the previous touch to the current frame.
- In v2 all objects have a defined lifetime. The lifetime of device-related objects (the device itself and its axis objects) is until a device removed event is put back to the library. The lifetime of a frame and its touches is until the event for the frame is put back to the library. In theory any number of events may be retrieved from the library at a time, but they must be put back in the order of retrieval or undefined results will occur.

* v1 has support for reading from mtdev.
- v2 drops this. It will not have any use once we move to the client side architecture.

* v1 is written in Henrik's coding style (which is fine :).
- v2 conforms to the Google C++ code style (which is what the DX team has agreed to).

If you want to test it, check it out, build it as usual, and run tools/utouch-frame-test-x11. It will open a window and when you use a touch device inside the window data will be output to the terminal.

To post a comment you must log in.
Revision history for this message
Jussi Pakkanen (jpakkane) wrote : Posted in a previous version of this proposal

Every single comment I have made about accessor functions vs generic property getter/setter applies here too. In fact it applies even stronger. As an example, the Axis class has no use for a generic setter. None. Zilch. Zero. There are no new properties that can magically appear at runtime. They are all specified by the kernel interface, the type enum and they are all required arguments of the constructor. All it does is complexify the code and circumvent the type system making the entire thing more fragile.

And if opaque getters and setters are to be used, they better be used everywhere and always. For example, UFTouchClass has getters and setters for id and state but not others. Which is it gonna be?

UFHandleClass requires an Initialize call before it is usable. This is wrong. Either this needs to be done in the constructor or a static factory. It might be sensible not to use exceptions in this case since Frame is being used as a C library. I'm not quite sure what happens if an exception escapes the boundary.

There are multiple duplicate declarations of SharedUFDeviceClass, SharedUFDeviceClass etc. There can be only one.

The PutEvent/GetEvent interface is a bit cryptic. Why is it called PutEvent if it just deletes the event. Should it not be ReleaseEvent? "Put" in a container object implies adding new stuff for further use. Actually, the entire interface seems a bit fishy to me. Could you give an example on how you envision it will be used and how it differs from the current API.

If this is going to be C++0x, then these sorts of structures:

  for (unsigned int i = 0; i < prev_->touches_array_.size(); ++i) {
    const SharedUFTouchClass& prev_touch = prev_->touches_array_[i];

should be replaced with auto iterators.

And so on and so on. I'm going to mark this as rejected simply to signal that there is a lot of work to be done.

review: Disapprove
Revision history for this message
Chase Douglas (chasedouglas) wrote : Posted in a previous version of this proposal
Download full text (4.0 KiB)

> Every single comment I have made about accessor functions vs generic property
> getter/setter applies here too. In fact it applies even stronger. As an
> example, the Axis class has no use for a generic setter. None. Zilch. Zero.
> There are no new properties that can magically appear at runtime. They are all
> specified by the kernel interface, the type enum and they are all required
> arguments of the constructor. All it does is complexify the code and
> circumvent the type system making the entire thing more fragile.

Note that there are no setters in the api.

One can reason that the axis object is not going to be extended, so I have converted frame_axis_get_property() into four separate static accessor function.

The rest of the objects may be extended in the future, and they could be extended at runtime. Leaving them as generic opaque getters allows for better forward compatibility.

> And if opaque getters and setters are to be used, they better be used
> everywhere and always. For example, UFTouchClass has getters and setters for
> id and state but not others. Which is it gonna be?

Touches are retrieved through an indexed lookup because there's no other way to retrieve them from the frame object.

Axes can be retrieved through an indexed lookup for the same reason until you know what axis types are available for the device. Once you have looped through the available axes you can get an axis directly by its type.

These are special cases because the objects are part of arrays.

> UFHandleClass requires an Initialize call before it is usable. This is wrong.
> Either this needs to be done in the constructor or a static factory. It might
> be sensible not to use exceptions in this case since Frame is being used as a
> C library. I'm not quite sure what happens if an exception escapes the
> boundary.

It has been converted to a factor function. Additionally, frame_x11_new() now catches any exceptions and returns UFStatusErrorResources.

> There are multiple duplicate declarations of SharedUFDeviceClass,
> SharedUFDeviceClass etc. There can be only one.

These are essentially forward declarations of the typedefs. If you don't forward declare them, you will need to include the appropriate header files. If you attempt to do that (which I tried to do), you get into a header recursion and end up with undefined type errors.

This leaves us with two possibilities: forgo the typedefs and use the exact definitions, or leave the forward declarations of typedefs. Note that the typedefs cannot get out of sync or else you will get compiler errors stating there is a conflicting declaration. I have decided to leave them as it makes the code easier to read.

> The PutEvent/GetEvent interface is a bit cryptic. Why is it called PutEvent if
> it just deletes the event. Should it not be ReleaseEvent? "Put" in a container
> object implies adding new stuff for further use. Actually, the entire
> interface seems a bit fishy to me. Could you give an example on how you
> envision it will be used and how it differs from the current API.

In linux, and I believe elsewhere too, some reference counting APIs use "get" and "put" to increment and decrement the referen...

Read more...

Revision history for this message
Chase Douglas (chasedouglas) wrote : Posted in a previous version of this proposal

I want to note that this api is nowhere near final. What I propose is instead of merging this into lp:utouch-frame, we merge it into lp:utouch-frame/v2. We continually merge any changes from trunk (which should be few if any), and we switch the daily ppa over to build from lp:utouch-frame/v2. We can then have a final merge review before merging lp:utouch-frame/v2 into lp:utouch-frame.

During this time, we'll be able to kick the tires on the api. If we find certain things to be cumbersome, we can change them. From this point of view, this merge proposal should be about finding and fixing implementation issues and the direction of the api. For example, if the api structure is reasonable, but there's a question as to what should have static accessors and what should have generic property getters, we can address those later on before merging back to trunk.

Revision history for this message
Jussi Pakkanen (jpakkane) wrote : Posted in a previous version of this proposal

Forward declarations have one of the following two forms:

class Foo;
struct Foo;

What we have in the code is a type declaration. They must _not_ be duplicated. Ever. In this particular case an easy fix is to have a header such as basic_definitions.h which does not include anything and has the following contents:

class UFTouch;
// other forward declared classes here

typedef std::shared_ptr<const UFTouch_> SharedUFTouch_;
// other type declaration thingies here.

It can be included anywhere, is short, conflict-free and does not cause compilation slowdown.

Revision history for this message
Jussi Pakkanen (jpakkane) wrote : Posted in a previous version of this proposal

The X event object is created in utouch-frame-test-x11 but it is released in UFHandleX11_::InjectEvent. This is an ownership error. The one who created the event should have the responsibility of destroying it as well.

The main function in x11 test app is too long. It should be split into smaller, logical groups (setup, processing, teardown or something similar).

PRINT_PROPERTY is quite long, it should really be a function.

Revision history for this message
Jussi Pakkanen (jpakkane) wrote : Posted in a previous version of this proposal

The header file of x11-test lists Henrik as the author.

Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

Looking good. Just some things I found.

InjectEvent still destroys the X event given to it. This is an ownership error as described above.

Classes live inside utouch::frame namespace but they still have the UF prefix in their names. Shouldn't they be just Touch, Device etc. since they are only for internal use?

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

InjectEvent takes the X event and gets the "extra" data for the event and frees it after it's done with the extra data. This is how one handles XI2 events because the X protocol is dumb. However, anyone with access to the event can get the extra data again. For example, in the utouch-frame-test-x11 code you could do more processing with the event after injecting it into utouch-frame. See XGetEventData for more details.

I am keeping the UF prefix to signify which classes back an opaque API object. For example, UFFrame is exposed through the API, while Window is not. Otherwise it would be hard to determine what is backing the API without looking closely at the header files.

lp://staging/~chasedouglas/frame/v2 updated
119. By Chase Douglas

Handle long unsigned int values, fixes build on i386

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

I take the InjectEvent comments back. It appears you cannot call XGetEventData twice on the same event. I will modify the API so that the caller has to call XGetEventData before injecting the event, and then XFreeEventData after. I'm also going to rename it to frame_x11_process_event to fit with the grail api scheme.

lp://staging/~chasedouglas/frame/v2 updated
120. By Chase Douglas

Make the client get and free event data

Event data can only be retrieved once, so the client needs to be in
control of when this occurs.

Also, rename frame_x11_inject_event() to frame_x11_process_event()

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

I have pushed a fix to the branch. Please review.

And thanks, Jussi, for remembering this!

review: Needs Resubmitting
Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

Ok.

review: Approve
Revision history for this message
Stephen M. Webb (bregma) wrote :

(0) I had to back out r119 to get the code to build on a 64-bit oneiric system. You might want to double-check that code.

(1) Downcasts should never use reinterpret_cast: use either dynamic_cast if you're unsure of the derived type (and the base type has at least 1 virtual function and you check for successful conversion) or static_cast if you're sure of the derived type or the base is a POD. I see a lot of this in X11 classes -- all of the reinterpret_casts should actually be static_casts. Same with value.cpp.

(2) None of the uses of dynamic_cast are necessary in value.cpp are necessary: you're using it to cast a contained pointer to the same type.

(3) There is much potential for leakage on a memory allocation failure. I'm assuming we don't care too much since a memory allocation failure is invariably fatal anyway, leaking a little is the last of our worries when it happens. Let me know if this is a legitimate concern and I can enumerate cases for fixing (I vote it's not a concern).

I'm marking this as needs fixing for the casting changes and the build failure. The API seems reasonable and while the implementation is a little more complex than I would expect from a simple adaptor filter library, appears solid. Valgrind reports no leaks other than those due to X11 itself and I can see no name leakage from the SO file.

review: Needs Fixing
lp://staging/~chasedouglas/frame/v2 updated
121. By Chase Douglas

Only define long unsigned value variant when it differs from uint64_t

Fixes build on 64-bit machines.

122. By Chase Douglas

Use static_cast when casting from a super- to a sub-class

123. By Chase Douglas

dynamic_cast is unnecessary where it's been used

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

Stephen,

Thanks for catching the build failure! I modified the way X window IDs were handled while on a plane with my x86 netbook, and I hadn't compiled it on behemoth since. It should be fixed now.

I fixed the casting issues. I agree that the memory leak on memory allocation failures are not worth fixing at this time. Feel free to open a bug if you really want :).

Please review the changes I have pushed.

Thanks!

review: Needs Resubmitting
Revision history for this message
Stephen M. Webb (bregma) wrote :

This is certainly Good Enough. Let's get this sucker in.

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

Great! As a note, I'm waiting on integration of a unit test framework and adding some basic test cases before merging into trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
The diff is not available at this time. You can reload the page or download it.

Subscribers

People subscribed via source and target branches