diff --git a/app-android/app/src/main/java/at/bitfire/gfxtablet/CanvasView.java b/app-android/app/src/main/java/at/bitfire/gfxtablet/CanvasView.java index 388e58b..8cc27bb 100644 --- a/app-android/app/src/main/java/at/bitfire/gfxtablet/CanvasView.java +++ b/app-android/app/src/main/java/at/bitfire/gfxtablet/CanvasView.java @@ -17,10 +17,17 @@ import at.bitfire.gfxtablet.NetEvent.Type; public class CanvasView extends View implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = "GfxTablet.CanvasView"; + private enum InRangeStatus { + OutOfRange, + InRange, + FakeInRange + } + final SharedPreferences settings; NetworkClient netClient; boolean acceptStylusOnly; int maxX, maxY; + InRangeStatus inRangeStatus; // setup @@ -35,6 +42,7 @@ public class CanvasView extends View implements SharedPreferences.OnSharedPrefer settings.registerOnSharedPreferenceChangeListener(this); setBackground(); setInputMethods(); + inRangeStatus = InRangeStatus.OutOfRange; } public void setNetworkClient(NetworkClient networkClient) { @@ -83,13 +91,23 @@ public class CanvasView extends View implements SharedPreferences.OnSharedPrefer if (isEnabled()) { for (int ptr = 0; ptr < event.getPointerCount(); ptr++) if (!acceptStylusOnly || (event.getToolType(ptr) == MotionEvent.TOOL_TYPE_STYLUS)) { + short nx = normalizeX(event.getX(ptr)), + ny = normalizeY(event.getY(ptr)), + npressure = normalizePressure(event.getPressure(ptr)); Log.v(TAG, String.format("Generic motion event logged: %f|%f, pressure %f", event.getX(ptr), event.getY(ptr), event.getPressure(ptr))); - if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) - netClient.getQueue().add(new NetEvent(Type.TYPE_MOTION, - normalizeX(event.getX(ptr)), - normalizeY(event.getY(ptr)), - normalizePressure(event.getPressure(ptr)) - )); + switch (event.getActionMasked()) { + case MotionEvent.ACTION_HOVER_MOVE: + netClient.getQueue().add(new NetEvent(Type.TYPE_MOTION, nx, ny, npressure)); + break; + case MotionEvent.ACTION_HOVER_ENTER: + inRangeStatus = InRangeStatus.InRange; + netClient.getQueue().add(new NetEvent(Type.TYPE_BUTTON, nx, ny, npressure, -1, true)); + break; + case MotionEvent.ACTION_HOVER_EXIT: + inRangeStatus = InRangeStatus.OutOfRange; + netClient.getQueue().add(new NetEvent(Type.TYPE_BUTTON, nx, ny, npressure, -1, false)); + break; + } } return true; } @@ -110,11 +128,19 @@ public class CanvasView extends View implements SharedPreferences.OnSharedPrefer netClient.getQueue().add(new NetEvent(Type.TYPE_MOTION, nx, ny, npressure)); break; case MotionEvent.ACTION_DOWN: + if (inRangeStatus == inRangeStatus.OutOfRange) { + inRangeStatus = inRangeStatus.FakeInRange; + netClient.getQueue().add(new NetEvent(Type.TYPE_BUTTON, nx, ny, (short)0, -1, true)); + } netClient.getQueue().add(new NetEvent(Type.TYPE_BUTTON, nx, ny, npressure, 0, true)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: netClient.getQueue().add(new NetEvent(Type.TYPE_BUTTON, nx, ny, npressure, 0, false)); + if (inRangeStatus == inRangeStatus.FakeInRange) { + inRangeStatus = inRangeStatus.OutOfRange; + netClient.getQueue().add(new NetEvent(Type.TYPE_BUTTON, nx, ny, (short)0, -1, false)); + } break; } diff --git a/doc/protocol.txt b/doc/protocol.txt index 4ef9aa6..dee74ae 100644 --- a/doc/protocol.txt +++ b/doc/protocol.txt @@ -20,8 +20,21 @@ Packet structure, uses network byte order (big endian): 1 unsigned int16 pressure (accepting full range 0..65535, but will clip to 32768 == pressure 1.0f on Android device) when type == button event: - 1 signed int8 number of button, starting with 0 + 1 signed int8 button id: + -1: stylus in range pseudo-button + 0: left click / stylus in contact / button 0 + 1: extra button 1 + 2: extra button 2 1 byte button status: 0 button is released ("up") 1 button is pressed ("down") +XInput will ignore BTN_TOUCH events if they are not preceeded by +a BTN_TOOL_PEN event -- this would never happen for a real stylus, +because it would imply the stylus is touching the pad, yet too far +away from the pad to be detected. + +A GfxTablet client must therefore be careful to send a "Button -1 +down" event before a "Button 0 down" event, to emulate this +behaviour. If they are faking these events, they'll probably want +to likewise send a "Button -1 up" event after a "Button 0 up" event. diff --git a/driver-uinput/networktablet.c b/driver-uinput/networktablet.c index 4f9c9ce..93cab80 100644 --- a/driver-uinput/networktablet.c +++ b/driver-uinput/networktablet.c @@ -35,6 +35,12 @@ void init_device(int fd) die("error: ioctl UI_SET_EVBIT EV_KEY"); if (ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH) < 0) die("error: ioctl UI_SET_KEYBIT"); + if (ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN) < 0) + die("error: ioctl UI_SET_KEYBIT"); + if (ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS) < 0) + die("error: ioctl UI_SET_KEYBIT"); + if (ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2) < 0) + die("error: ioctl UI_SET_KEYBIT"); // enable 2 main axes + pressure (absolute positioning) if (ioctl(fd, UI_SET_EVBIT, EV_ABS) < 0) @@ -144,8 +150,19 @@ int main(void) send_event(device, EV_SYN, SYN_REPORT, 1); break; case EVENT_TYPE_BUTTON: + // stylus hovering + if (ev_pkt.button == -1) + send_event(device, EV_KEY, BTN_TOOL_PEN, ev_pkt.down); + // stylus touching if (ev_pkt.button == 0) send_event(device, EV_KEY, BTN_TOUCH, ev_pkt.down); + // button 1 + if (ev_pkt.button == 1) + send_event(device, EV_KEY, BTN_STYLUS, ev_pkt.down); + // button 2 + if (ev_pkt.button == 2) + send_event(device, EV_KEY, BTN_STYLUS2, ev_pkt.down); + printf("sent button: %hhi, %hhu\n", ev_pkt.button, ev_pkt.down); send_event(device, EV_SYN, SYN_REPORT, 1); break; diff --git a/driver-uinput/protocol.h b/driver-uinput/protocol.h index 654a878..17f362f 100644 --- a/driver-uinput/protocol.h +++ b/driver-uinput/protocol.h @@ -20,7 +20,11 @@ struct event_packet }; struct { /* only required for EVENT_TYPE_BUTTON */ - int8_t button; /* number of button, beginning with 1 */ + int8_t button; /* button id: + -1 = stylus in range, + 0 = tap/left click/button 0, + 1 = button 1, + 2 = button 2 */ int8_t down; /* 1 = button down, 0 = button up */ }; };