Skip to content

Commit

Permalink
Easier to use right click interaction for touchscreens (#68)
Browse files Browse the repository at this point in the history
* Nicer right click action: can tap/hold/release and the menu persists, or tap/hold/drag/release to select an item

* Remove debug logs

* Make isCloseTo static
  • Loading branch information
lazd authored Mar 30, 2023
1 parent f67a7e5 commit 858c6f2
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 62 deletions.
111 changes: 56 additions & 55 deletions VoodooI2CHID/VoodooI2CTouchscreenHIDEventDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@

#define super VoodooI2CMultitouchHIDEventDriver

/* Check if the first set of coordinates is within fat finger distance of the second set
*/
static bool isCloseTo(IOFixed x, IOFixed y, IOFixed other_x, IOFixed other_y) {
IOFixed diff_x = x - other_x;
IOFixed diff_y = y - other_y;
return (diff_x * diff_x) +
(diff_y * diff_y) <
FAT_FINGER_ZONE;
}

OSDefineMetaClassAndStructors(VoodooI2CTouchscreenHIDEventDriver, VoodooI2CMultitouchHIDEventDriver);

// Override of VoodooI2CMultitouchHIDEventDriver
Expand Down Expand Up @@ -39,60 +49,60 @@ bool VoodooI2CTouchscreenHIDEventDriver::checkFingerTouch(AbsoluteTime timestamp
IOFixed y = ((transducer->coordinates.y.value() * 1.0f) / transducer->logical_max_y) * 65535;

checkRotation(&x, &y);
// Begin long press right click routine. Increasing compare_input_counter check will lengthen the time until execution.

UInt16 temp_x = x;
UInt16 temp_y = y;

if (!right_click && digitiser.contact_count->getValue() == 1 && transducer->type == kDigitiserTransducerFinger && transducer->tip_switch) {
if (temp_x == compare_input_x && temp_y == compare_input_y) {
compare_input_counter = compare_input_counter + 1;
compare_input_x = temp_x;
compare_input_y = temp_y;

if (compare_input_counter >= 120 && !right_click) {
compare_input_x = 0;
compare_input_y = 0;
compare_input_counter = 0;
right_click = true;
}
} else {
compare_input_x = temp_x;
compare_input_y = temp_y;
compare_input_counter = 0;
}
}
// End long press right click routine.


// Get time in a usable format
uint64_t nanoseconds;
absolutetime_to_nanoseconds(timestamp, &nanoseconds);

// We need the first couple of single touch events to be in hover mode. In modes such as Mission Control, this allows us
// to select and drag windows vs just select and exit. We are mimicking a cursor being moved into position prior to
// executing a drag movement. There is little noticeable affect in other circumstances. This also assists in transitioning
// executing a drag movement. There is little noticeable effect in other circumstances. This also assists in transitioning
// between single / multitouch.

if (click_tick < HOVER_TICKS) {
buttons = HOVER;
} else {
touch_start_time = nanoseconds;
touch_start_x = x;
touch_start_y = y;
} else if (
!right_click &&
digitiser.contact_count->getValue() == 1 &&
isCloseTo(x, y, touch_start_x, touch_start_y) &&
(nanoseconds - touch_start_time) >= RIGHT_CLICK_TIME
) {
right_click = true;
dispatchDigitizerEventWithTiltOrientation(timestamp, transducer->secondary_id, transducer->type, 0x1, RIGHT_CLICK, x, y);
dispatchDigitizerEventWithTiltOrientation(timestamp, transducer->secondary_id, transducer->type, 0x1, HOVER, x, y);
}
else {
buttons = transducer->tip_switch.value();
}
if (right_click)
buttons = RIGHT_CLICK;


click_tick++;

// Get time in a usable format
uint64_t nanoseconds;
absolutetime_to_nanoseconds(timestamp, &nanoseconds);

// If we're clicking again where we just clicked, precisely position the pointer where it was before
if (
isCloseToLastClick(x, y) &&
if (right_click) {
// After a right click has been executed, all interactions are now hovers
buttons = HOVER;

if (isCloseTo(x, y, touch_start_x, touch_start_y)) {
// Adopt the location of the touch start so we don't stray and close the right click menu
x = touch_start_x;
y = touch_start_y;
}
else {
// Track if we moved after the right click so we know if we need to click again on release
moved_during_right_click = true;
}
} else if (
isCloseTo(x, y, last_click_x, last_click_y) &&
(nanoseconds - last_click_time) <= DOUBLE_CLICK_TIME
) {
// If we're clicking again where we just clicked, precisely position the pointer where it was before
x = last_click_x;
y = last_click_y;
}

// Only dispatch a single click event after we've done our hover ticks
if ((click_tick <= HOVER_TICKS + 1) || (x != last_x || y != last_y)) {
dispatchDigitizerEventWithTiltOrientation(timestamp, transducer->secondary_id, transducer->type, 0x1, buttons, x, y);
Expand Down Expand Up @@ -177,21 +187,20 @@ void VoodooI2CTouchscreenHIDEventDriver::fingerLift() {
// checkFingerTouch function, however, this has the side effect of not releasing the pointer. This watchdog
// timer is needed regardless so the pointer release is best done here.


click_tick = 0;
start_scroll = true;
uint64_t now_abs;
clock_get_uptime(&now_abs);

dispatchDigitizerEventWithTiltOrientation(now_abs, last_id, kDigitiserTransducerFinger, 0x1, HOVER, last_x, last_y);

// If a right click has been executed, we reset our counter and ensure that pointer is not stuck in right
// click button down situation.

if (right_click) {
right_click = false;
// This seems strange, but we go to hover mode after right clicking, and we need to execute a click if we've moved
if (right_click && moved_during_right_click) {
dispatchDigitizerEventWithTiltOrientation(now_abs, last_id, kDigitiserTransducerFinger, 0x1, LEFT_CLICK, last_x, last_y);
}

dispatchDigitizerEventWithTiltOrientation(now_abs, last_id, kDigitiserTransducerFinger, 0x1, HOVER, last_x, last_y);

click_tick = 0;
start_scroll = true;
right_click = false;
moved_during_right_click = false;
last_click_time = now_abs;
last_click_x = last_x;
last_click_y = last_y;
Expand Down Expand Up @@ -335,14 +344,6 @@ void VoodooI2CTouchscreenHIDEventDriver::scrollPosition(AbsoluteTime timestamp,
scheduleLift();
}

bool VoodooI2CTouchscreenHIDEventDriver::isCloseToLastClick(IOFixed x, IOFixed y) {
IOFixed diff_x = x - last_click_x;
IOFixed diff_y = y - last_click_y;
return (diff_x * diff_x) +
(diff_y * diff_y) <
FAT_FINGER_ZONE;
}

void VoodooI2CTouchscreenHIDEventDriver::scheduleLift() {
timer_source->setTimeoutMS(FINGER_LIFT_DELAY);
}
12 changes: 5 additions & 7 deletions VoodooI2CHID/VoodooI2CTouchscreenHIDEventDriver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#define FAT_FINGER_ZONE 1000000 // 1000^2
#define DOUBLE_CLICK_TIME 450 * 1000000
#define RIGHT_CLICK_TIME 500 * 1000000
#define FINGER_LIFT_DELAY 50
#define HOVER_TICKS 3

Expand Down Expand Up @@ -72,10 +73,6 @@ class EXPORT VoodooI2CTouchscreenHIDEventDriver : public VoodooI2CMultitouchHIDE
*/
IOReturn parseElements(UInt32) override;

/* Check if this interaction is within fat finger distance
*/
bool isCloseToLastClick(IOFixed x, IOFixed y);

/* Schedule a finger lift event
*/
void scheduleLift();
Expand All @@ -96,6 +93,8 @@ class EXPORT VoodooI2CTouchscreenHIDEventDriver : public VoodooI2CMultitouchHIDE
IOFixed last_y = 0;
IOFixed last_click_x = 0;
IOFixed last_click_y = 0;
IOFixed touch_start_x = 0;
IOFixed touch_start_y = 0;
UInt32 barrel_switch_offset = 0;
UInt32 eraser_switch_offset = 0;
SInt32 last_id = 0;
Expand All @@ -105,10 +104,9 @@ class EXPORT VoodooI2CTouchscreenHIDEventDriver : public VoodooI2CMultitouchHIDE

UInt32 click_tick = 0;
bool right_click = false;
bool moved_during_right_click = false;
bool start_scroll = true;
UInt16 compare_input_x = 0;
UInt16 compare_input_y = 0;
int compare_input_counter = 0;
UInt64 touch_start_time = 0;
UInt64 last_click_time = 0;

/* The transducer is checked for singletouch finger based operation and the pointer event dispatched. This function
Expand Down

0 comments on commit 858c6f2

Please sign in to comment.