The reachable range is 2100x14800, highest pressure is 1023. Unlike the slate
the paper block still affords to address almost the whole range even though
the paper is slightly narrower than the range - but it can slide left/right by
about 5mm.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Because it's annoying when there's an issue with the timestamp in the file and
you have to grep for the file name.
And while we're there, change the human readable date to be actually readable.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
It's the same message with a different argument, let's try not to do
gymnastics in the Msg code just so we can return a tuple.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
We can hook into the ProtocolVersion here, so let's de-duplicate this.
Should be backwards-compatible for config files but new config files will have
the uppercase name written now.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This is still a bit awkward because the communication of the spark especially
is so strange. It sends a (rejected) uuid, then a single command to register
that UUID. The Slate and the IntuosPro are more sensible, they seem to have a
special message to register a UUID - that one either fails or succeeds.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
The default timeout is too short for registering a button, let's control this
from the message.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This is an in-situ replacement of the old functionality vs the new one,
resulting in a bit of duplication. Now that the Protocol takes care of the
device-specifics most of our functions look identical. This will be fixed in a
follow-up commit.
Missing from the conversion here is the initial register handling (too
convoluted right now) and the parsing of the pen data.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This is only the framework for a new protocol handler that should simplify
adding, extending and debugging the actual protocol messages.
The summary is:
- each protocol interaction is a subclass of Msg() that handles the data sent
and received from the device.
- the list of messages for each device is stored in a dictionary the caller
initializes by providing the protocol version
- those messages are addressable via the Interactions enum through the
Protocol class
- the messages can be run via execute(), taking and setting the obvious
properties on the object
- actual communication is done via a single callback that takes/returns a
NordicData object for the request/reply.
So after the basic setup, the caller can do this:
p = Protocol(ProtocolVersion.INTUOS_PRO)
name = p.execute(Interactions.GET_NAME).name
p.execute(Interactions.SET_TIME, time.time())
This completely separates the protocol from the implementation where we want
the result of that exchange, ideally paving the way for better testing and
easier debugging of what's happening on the wire. Well, ether.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This function doesn't process the data beyond the logging required. It simply
sends the request and returns the reply, whatever that is.
Currently unused, this is prep work for later.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
For testing it's a lot easier to just provide a /tmp/ directory than having to
clear out the normal one that may contain useful drawings for debugging.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Regression introduced in e1a3675439
Where a device has no config entry, the e1a367 change would return early. This
is fine for devices previously seen and still lingering in the config but for
completely new devices, we'll never get past that KeyError.
Return to the old behaviour: where we don't have a stored UUID we continue
with the None UUID.
Fixes#148
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
If we have more data than our packet [1], let's not return the whole data
array. Slice it to size instead.
[1] this never happens with the current code, we don't work asynchronously
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
we don't need the same-ish check twice, we can just pop our non-list into a
list an go from there.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This requires adjusting the svg conversion in kete as well, afaict the 1000
range there was chosen because it's (almost) the midpoint of the Bamboo series
with 2048 pressure grades. So let's use half of 0x10000 instead, which is
approximately 0x8000 as the crow flies.
Fixes#142
This requires adjusting the svg conversion in kete as well, afaict the 1000
range there was chosen because it's (almost) the midpoint of the Bamboo series
with 2048 pressure grades. So let's use half of 0x10000 instead, which is
approximately 0x8000 as the crow flies.
Fixes#142
If we want to integrate this with TuhiGui, we can't have multiple mainloops.
And there's nothing with Tuhi that needs it anyway, it's all about how it is
being called. Which means we can just move this to the main method.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This broke device initialization in some cases (not sure why it worked until
today though). Where a device is in register mode but the ManufacturerData has
not yet updated to the 4-byte string (i.e. a device previously known
normally), the device would now always call listen(). The firmware - in
register mode - would start initializing the connection though and get
rejected, leaving us with a device that cannot be registered.
This reverts commit a061240b11.
No functional change, it just uses a dict now to pick the protocol class which
makes it *slightly* easier to read.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Instead of just doing a single log line, let's log this to a yaml file so we
have a permanent record of the interactions with the device for bug reports.
This was getting stacked - once on init and once whenever we actually made a
connecting, resulting in the signal never getting disconnected properly and
some races because we tried to sync twice.
The whole "listen()" vs "listening" vs "connected" etc. is a bit of a mess and
should be reworked for a proper fix to this. But for now this will do.
This may fix#124
Propagated whenever we start talking to the device (and then again when we
stop). The purpose of this signal is merely that a UI can show e.g. a progress
bar while we're talking to the device to ensure the user something is
happening.
Fixes#138
Virtually all the commands we have that don't have a specific opcode expect
0xb3. Since that's the general ACK command, we can default to that and make
everything else the exception.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This is what the dbusserver module does as well, so we end up with a weird
mismatch: to the base module the device is still known and unregistered on the
next search start. So when we see the bluez properties float past, we don't
add a new devices.
To the dbusserver the device is gone though and since we don't add it again in
the base module, we never send a signal for it.
Since there's a reasonable assumption that if we don't register the device, we
don't want it anyway, let's delete it from the base device list too.
This doesn't fix the issue of a device timing out while waiting for a button
press. That device will count as known until the next search stop. But at
least now we don't have to restart tuhi.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
tuhi/drawing.py:46: PyGIDeprecationWarning: GObject.property is deprecated;
use GObject.Property instead
@GObject.property
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
No need to get triggered by BlueZDeviceManager when it finds any GATT
characteristic. The BlueZDevice just needs its 'ServicesResolved' and
can resolve the GATT characteristics itself.
This solves a FIXME.
Signed-off-by: Daniel Martin <consume.noise@gmail.com>
The object list returned by object managers get_objects() function is
not sorted. Though, we rely on objects being sorted by their object
path.
Replace the object managers get_objects() function with a variant
fixing that. Additionally, our variant makes it possible to filter
the returned object list by object path and interface.
Examples:
- get_objects():
/
/org/bluez
/org/bluez/hci0
/org/bluez/hci0/dev_00_00_00_00_00_00
/org/bluez/hci0/dev_00_00_00_00_00_00/service0000
/org/bluez/hci0/dev_00_00_00_00_00_00/service0000/char0000
/org/bluez/hci0/dev_00_00_00_00_00_00/service1111
/org/bluez/hci0/dev_00_00_00_00_00_00/service1111/char1111
/org/bluez/hci0/dev_FF_FF_FF_FF_FF_FF
/org/bluez/hci0/dev_FF_FF_FF_FF_FF_FF/serviceffff
/org/bluez/hci0/dev_FF_FF_FF_FF_FF_FF/serviceffff/charffff
- get_objects(interface='org.bluez.Adapter1'):
/org/bluez/hci0
- get_objects(interface='org.bluez.GattCharacteristic1'
base_path='/org/bluez/hci0/dev_00_00_00_00_00_00'):
/org/bluez/hci0/dev_00_00_00_00_00_00/service0000/char0000
/org/bluez/hci0/dev_00_00_00_00_00_00/service1111/char1111
Signed-off-by: Daniel Martin <consume.noise@gmail.com>
b3 01 00 seems to be a generic "I'm happy" and the few others we've seen
are errors. So let's encode that and reconsider if it turns out to be false.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
we already are keeping a record of the x,y and p. So we are trying
to duplicate the effort by using new_rel.
The problem is that some reports have relative coordinates but absolute
pressure data. Which means we are missing the pressure change as it has
a relative value of 0.
By trusting the wacom internal state, we can have the json data that
matches it completely.
We need python 3.6 but old Ubuntus only have 3.5. 3.6 can be installed (and
thus we get past the 3.6 sys.version check), but the gi.repository backports
are in various states of disarray (see #103 and #104).
And Python (and in particular gi) makes it hard for users to distinguish
between "the dependencies are a mess" and "this is a bug in tuhi".
So let's catch any error during gi.repository import and print a warning to
the user, in the hope of getting less bugs caused by Ubuntu frankenpythons.
Still won't fix issues like #104, but I'm not sure how to test for those
errors.
Related to #103
Export the supported versions in a Manager property and require the client to
request a specific version in GetJSONData. Without that, the server could
never update the format and clients would have to support every single
historical version to make sure they can run against any version server.
Fixes#98
Because we're totally going to ship tuhi in embedded devices that are going to
be in use until after 2038 when time resets to zero, the world goes big bang
and the only survivers will be 64-bit species. Or something like that. Either
way, the API documentation already said so anyway and the DrawingsAvailable
property is already 64 bit too.
Semi-confusingly: the dbus 64 bit unsigned type is 't', it doesn't stand for
'timestamp'.
It can easily take more than 5 seconds when reading the data. The current
timeout just cuts the connection while it is still reading.
We *could* just remove the timeout here as the device will gracefully
send us a 0xc8 opcode with a blank CRC if we shut the device down while
retrieving the data.
However, better being on the safe side and keep a timeout, but restart it
if we are still getting pen data during the timeout.
Fixes#91
When using the live mode on the Slate, the bloc note actually hampers
the pen and prevents it to address all of the pixels on the screen.
Add some artificial boundaries that roughly match the primary design
of the device.
The Intuos Pro has a clip where you put a regular sheet of paper, so there
is no point restraining the active surface.
Fixes#88
For data packets other than basic motion/pressure strokes, add a PacketHandler
that can deal with that particular packet. Those handlers are part of the
class declaration, when we instantiate a protocol we pull them all into a
packet_handlers list and go through them. The first one to return True is
considered the right handler and we expect something has been done with the
packet.
This patch includes the handlers for the end-of-stroke and end-of-sequence
packets, the rest to be added later.
Instad of carrying around a lot of parameters, move this into a class with
properties. Eventually we can subclass that one for the special opcodes (or
something...)
Makes end-of-stroke handling easier, we can call seal() on the current stroke
and it'll make drawing.current_stroke() None, which is something we can deal
with.
Now that we got the basics running and reliable, this debug message is mostly
just noise, especially when in an office with lots of bluetooth devices
around. Comment it out, to be enabled when necessary.
On the Intuos Pro at least we sometimes get events that are all relative at
first and it can take several events for us to have all three values as
absolute. This is likely a parsing error on our side, but meanwhile don't
write the data until we have at least one known value for all three axes.
And because the SVG writer isn't happy with empty strokes, add a seal()
function to the drawing to purge empty strokes.
Fixed#92 or at least works around it
The prefix (4 bytes on the spark/slate) is some data that we don't know yet.
It *could* be the size of a tablet/drawing in LE byte order: 14434x29794
although that doesn't quite match the Intuos Pro data where the prefix is
different.
Either way, having this be an opcode of 0x3800 was probably just coincidence.
Let's skip over the header normally and assume that once we have a prefix, we
have a drawing. This opcode never occurs a second time within the same
drawing.
The Intuos Pro has a different prefix, we'll need to override this. Dropping
the comment about the 8bt, on the intuos pro the prefix is nonsensical so
let's not leave a false lead there.
On the Spark, the 0xc9 CRC comes almost immediately after the 0xc8
end-of-sequence. This causes a race condition where sometimes we process the
0xc8 before the 0xc9 arrives, other times the 0xc9 overwrites the current
nordic_answer value before we get to it - triggering an unexpected opcode
exception.
Fix this by appending the nordic answer and only ever pulling off enough to
satisfy the current request.
Fixes#90
A device isn't registered or not, it's merely in a *state* of being
registering or not. Pass the current mode down to the TuhiDevice instead of a
boolean for 'registered'. The 'registered' gobject property is left in-place
for now, the dbus server needs it.
An 'event' boolean is less than obvious ('is-event' may be better). This
signalled the difference between a device-added during Manager startup and the
device-added at runtime (i.e. device goes online).
We don't need that differentiation. The manager adds all existing devices
immediately after connnect_to_bluez(). All we have to do is run through the
device list, add them locally and then subscribe to the signal.
This keeps the is-event confusion within one file only instead of spreading it
across two and an internal API. And we can re-name it to hotplugged.
Calling listen on an unregistered device currently triggers a register on the
device. This can happen when the device has been registered but the
ManufacturerData has not yet been updated.
Disentangle this by only calling the right function based on what we're trying
to get. This also requires that the connected/disconnected signals are only
handled when we're actually trying to do something with them.
Fixes#79 (multiple patches required)
Previously, we set self._mode and used that to determine what connection to
attempt. This can sometimes lead to a device attempting to register in
response to a listen() request (and vice versa).
Make this dependent on the caller arguments only. So when listen() is
requested do exactly that and generate the right exception if the device is in
the wrong mode.
Fixes#79 (multiple patches required)
We only ever used it in conjunction with vendor_id, so we can just make this a
normal property and send GObject notifies when it changes. This allows us to
listen for the MD to change when it finished registration.
[BT: do not add the second indirection by calling vendor_id]
Fold register_connection into register_device and (with comments) handle the
spark and slate here. This makes it easier to handle the Intuos Pro paper
later.
During the first attempt to register, we might not know before hand
the protocol of the device. Have a special class for it, and then
instantiate the correct protocol before continuing.
This makes the 'Protocol' entry in the config file mandatory
The Slate is more recent than the Spark. We better have the Spark as a
base class that we will never touch again and make the Slate depend on it.
When adding the Intuos Paper, we should make it a subclass of the Slate,
as the 2 protocols are similar.
The Intuos Pro Paper is close enough to the Slate but with some
subtleties. Instead of having a bunch of ifs, let's have nice subclasses
for the differences in the protocol.
We are already handling 2 protocols, one for the Spark, and one for
the Slate. That's one too many, and given that there are some subtleties
in the Intuos Pro Paper (see #56), we better start splitting the protocol
from the interface, so we can have different protocols for different
devices instead of having a bunch of 'ifs'
The Intuos Paper uses a different one, because of course it does.
The 'company ID' from the Paper will be added while adding support for it.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This isn't something we need in the daemon, it depends too much on what the
client expects so let the client save this data and handle the rotation
accordingly.
Note that the sensor in Sparks and Slates is in landscape format, i.e. every
picture is 90 deg rotated from what you'd expect.
Fixes#33 (wontfix)
Instead of failing with a syntax error on format strings, actually bail out
instead. Likewise for setup.py, require 3.6 and add this to the classifiers
too while we're there.
Related to #71