| /* |
| * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "gesture.h" |
| |
| #include <time.h> |
| |
| #include <gestures/gestures.h> |
| #include <xorg/xf86_OSproc.h> |
| |
| #include "cmt.h" |
| #include "properties.h" |
| |
| /* |
| * Gestures timer functions |
| */ |
| static GesturesTimer* Gesture_TimerCreate(void*); |
| static void Gesture_TimerSet(void*, |
| GesturesTimer*, |
| stime_t, |
| GesturesTimerCallback, |
| void*); |
| static void Gesture_TimerCancel(void*, GesturesTimer*); |
| static void Gesture_TimerFree(void*, GesturesTimer*); |
| |
| static CARD32 Gesture_TimerCallback(OsTimerPtr, CARD32, pointer); |
| |
| struct GesturesTimer { |
| OsTimerPtr timer; |
| GesturesTimerCallback callback; |
| void* callback_data; |
| int is_monotonic:1; |
| }; |
| |
| static GesturesTimerProvider Gesture_GesturesTimerProvider = { |
| .create_fn = Gesture_TimerCreate, |
| .set_fn = Gesture_TimerSet, |
| .cancel_fn = Gesture_TimerCancel, |
| .free_fn = Gesture_TimerFree |
| }; |
| |
| /* |
| * Callback for Gestures library. |
| */ |
| static void Gesture_Gesture_Ready(void* client_data, |
| const struct Gesture* gesture); |
| |
| static enum GestureInterpreterDeviceClass Gesture_Device_Class(EvdevClass cls); |
| |
| int |
| Gesture_Init(GesturePtr rec, size_t max_fingers) |
| { |
| rec->interpreter = NewGestureInterpreter(); |
| rec->slot_states = NULL; |
| |
| if (!rec->interpreter) |
| return !Success; |
| rec->fingers = malloc(max_fingers * sizeof(struct FingerState)); |
| if (!rec->fingers) |
| goto Error_Alloc_Fingers; |
| rec->mask = valuator_mask_new(MAX_VALUATORS); |
| if (!rec->mask) |
| goto Error_Alloc_Mask; |
| return Success; |
| |
| Error_Alloc_Mask: |
| free(rec->fingers); |
| rec->fingers = NULL; |
| Error_Alloc_Fingers: |
| DeleteGestureInterpreter(rec->interpreter); |
| rec->interpreter = NULL; |
| return BadAlloc; |
| } |
| |
| void |
| Gesture_Free(GesturePtr rec) |
| { |
| // free gesture interpreter first, this will cancel all timers. |
| DeleteGestureInterpreter(rec->interpreter); |
| rec->interpreter = NULL; |
| rec->dev = NULL; |
| |
| valuator_mask_free(&rec->mask); |
| |
| if (rec->fingers) { |
| free(rec->fingers); |
| rec->fingers = NULL; |
| } |
| if(rec->slot_states) { |
| free(rec->slot_states); |
| rec->slot_states = NULL; |
| } |
| } |
| |
| void |
| Gesture_Device_Init(GesturePtr rec, DeviceIntPtr dev) |
| { |
| InputInfoPtr info = dev->public.devicePrivate; |
| CmtDevicePtr cmt = info->private; |
| CmtPropertiesPtr props = &cmt->props; |
| EventStatePtr evstate = &cmt->evstate; |
| struct HardwareProperties hwprops; |
| EvdevPtr evdev = &cmt->evdev; |
| int i; |
| |
| /* Store the device for which to generate gestures */ |
| rec->dev = dev; |
| |
| /* TODO: support different models */ |
| hwprops.left = props->area_left; |
| hwprops.top = props->area_top; |
| hwprops.right = props->area_right; |
| hwprops.bottom = props->area_bottom; |
| hwprops.res_x = props->res_x; |
| hwprops.res_y = props->res_y; |
| hwprops.screen_x_dpi = 133; |
| hwprops.screen_y_dpi = 133; |
| hwprops.orientation_minimum = props->orientation_minimum; |
| hwprops.orientation_maximum = props->orientation_maximum; |
| hwprops.max_finger_cnt = evstate->slot_count; |
| hwprops.max_touch_cnt = Event_Get_Touch_Count_Max(evdev); |
| hwprops.supports_t5r2 = Event_Get_T5R2(evdev); |
| hwprops.support_semi_mt = Event_Get_Semi_MT(evdev); |
| /* buttonpad means a physical button under the touch surface */ |
| hwprops.is_button_pad = Event_Get_Button_Pad(evdev); |
| |
| GestureInterpreterSetPropProvider(rec->interpreter, &prop_provider, |
| rec->dev); |
| GestureInterpreterInitialize( |
| rec->interpreter, |
| Gesture_Device_Class(cmt->evdev.info.evdev_class)); |
| GestureInterpreterSetHardwareProperties(rec->interpreter, &hwprops); |
| |
| rec->slot_states = malloc(sizeof(int) * evstate->slot_count); |
| if (!rec->slot_states) { |
| ERR(info, "BadAlloc: rec->slot_states"); |
| return; |
| } |
| for (i = 0; i < evstate->slot_count; ++i) |
| rec->slot_states[i] = SLOT_STATUS_FREE; |
| } |
| |
| void |
| Gesture_Device_On(GesturePtr rec) |
| { |
| GestureInterpreterSetTimerProvider(rec->interpreter, |
| &Gesture_GesturesTimerProvider, |
| rec->dev); |
| GestureInterpreterSetCallback(rec->interpreter, &Gesture_Gesture_Ready, |
| rec); |
| } |
| |
| void |
| Gesture_Device_Off(GesturePtr rec) |
| { |
| GestureInterpreterSetCallback(rec->interpreter, NULL, NULL); |
| } |
| |
| void |
| Gesture_Device_Close(GesturePtr rec) |
| { |
| GestureInterpreterSetPropProvider(rec->interpreter, NULL, NULL); |
| GestureInterpreterSetTimerProvider(rec->interpreter, NULL, NULL); |
| } |
| |
| void |
| Gesture_Process_Slots(void* vrec, |
| EventStatePtr evstate, |
| struct timeval* tv) |
| { |
| GesturePtr rec = vrec; |
| DeviceIntPtr dev = rec->dev; |
| InputInfoPtr info = dev->public.devicePrivate; |
| CmtDevicePtr cmt = info->private; |
| EvdevPtr evdev = &cmt->evdev; |
| ValuatorMask* mask = rec->mask; |
| int i; |
| MtSlotPtr slot; |
| struct HardwareState hwstate = { 0 }; |
| int current_finger; |
| bool has_gesture_fingers = false; |
| |
| if (!rec->interpreter || ! rec->slot_states) |
| return; |
| |
| /* zero initialize all FingerStates to clear out previous state. */ |
| memset(rec->fingers, 0, |
| Event_Get_Slot_Count(evdev) * sizeof(struct FingerState)); |
| |
| /* clear out previous state from valuator */ |
| valuator_mask_zero(mask); |
| |
| if (cmt->props.raw_passthrough) { |
| for (i = 0; i < evstate->slot_count; i++) { |
| slot = &evstate->slots[i]; |
| |
| /* send TouchEnd for lifted fingers */ |
| if (slot->tracking_id == -1) { |
| if (rec->slot_states[i] == SLOT_STATUS_RAW) { |
| xf86PostTouchEvent(dev, i, XI_TouchEnd, 0, mask); |
| } |
| rec->slot_states[i] = SLOT_STATUS_FREE; |
| continue; |
| } |
| |
| /* |
| * valuators 0 (CMT_AXIS_X) and 1 (CMT_AXIS_Y) are hardcoded into |
| * X.org as finger position, so we need to set those too. |
| */ |
| valuator_mask_set_double(mask, CMT_AXIS_MT_POSITION_X, |
| slot->position_x); |
| valuator_mask_set_double(mask, CMT_AXIS_MT_POSITION_Y, |
| slot->position_y); |
| valuator_mask_set_double(mask, CMT_AXIS_MT_PRESSURE, |
| slot->pressure); |
| valuator_mask_set_double(mask, CMT_AXIS_MT_TOUCH_MAJOR, |
| slot->touch_major); |
| valuator_mask_set_double(mask, CMT_AXIS_TOUCH_TIMESTAMP, |
| StimeFromTimeval(tv)); |
| valuator_mask_set_double(mask, CMT_AXIS_X, slot->position_x); |
| valuator_mask_set_double(mask, CMT_AXIS_Y, slot->position_y); |
| |
| if (rec->slot_states[i] == SLOT_STATUS_RAW) { |
| xf86PostTouchEvent(dev, i, XI_TouchUpdate, 0, mask); |
| } else { |
| /* take over STATUS_GESTURE slots too */ |
| if (rec->slot_states[i] == SLOT_STATUS_GESTURE) |
| has_gesture_fingers = true; |
| xf86PostTouchEvent(dev, i, XI_TouchBegin, 0, mask); |
| |
| } |
| rec->slot_states[i] = SLOT_STATUS_RAW; |
| } |
| |
| if (has_gesture_fingers) { |
| /* push empty hardware state to clear interpreter state */ |
| hwstate.timestamp = StimeFromTimeval(tv); |
| GestureInterpreterPushHardwareState(rec->interpreter, &hwstate); |
| } |
| return; |
| } |
| |
| current_finger = 0; |
| for (i = 0; i < evstate->slot_count; i++) { |
| slot = &evstate->slots[i]; |
| if (slot->tracking_id == -1) { |
| rec->slot_states[i] = SLOT_STATUS_FREE; |
| continue; |
| } |
| |
| if (rec->slot_states[i] == SLOT_STATUS_FREE) { |
| rec->slot_states[i] = SLOT_STATUS_GESTURE; |
| } else if (rec->slot_states[i] == SLOT_STATUS_RAW) { |
| /* ignore any fingers that are still present from raw mode */ |
| continue; |
| } |
| |
| rec->fingers[current_finger].touch_major = (float)slot->touch_major; |
| rec->fingers[current_finger].touch_minor = (float)slot->touch_minor; |
| rec->fingers[current_finger].width_major = (float)slot->width_major; |
| rec->fingers[current_finger].width_minor = (float)slot->width_minor; |
| rec->fingers[current_finger].pressure = (float)slot->pressure; |
| rec->fingers[current_finger].orientation = (float)slot->orientation; |
| rec->fingers[current_finger].position_x = (float)slot->position_x; |
| rec->fingers[current_finger].position_y = (float)slot->position_y; |
| rec->fingers[current_finger].tracking_id = slot->tracking_id; |
| current_finger++; |
| } |
| hwstate.timestamp = StimeFromTimeval(tv); |
| if (Event_Get_Button_Left(evdev)) |
| hwstate.buttons_down |= GESTURES_BUTTON_LEFT; |
| if (Event_Get_Button_Middle(evdev)) |
| hwstate.buttons_down |= GESTURES_BUTTON_MIDDLE; |
| if (Event_Get_Button_Right(evdev)) |
| hwstate.buttons_down |= GESTURES_BUTTON_RIGHT; |
| hwstate.touch_cnt = Event_Get_Touch_Count(evdev); |
| hwstate.finger_cnt = current_finger; |
| hwstate.fingers = rec->fingers; |
| hwstate.rel_x = evstate->rel_x; |
| hwstate.rel_y = evstate->rel_y; |
| hwstate.rel_wheel = evstate->rel_wheel; |
| hwstate.rel_hwheel = evstate->rel_hwheel; |
| GestureInterpreterPushHardwareState(rec->interpreter, &hwstate); |
| } |
| |
| static void SetTimeValues(ValuatorMask* mask, |
| const struct Gesture* gesture, |
| DeviceIntPtr dev, |
| BOOL is_absolute) |
| { |
| double start_time = gesture->start_time; |
| double end_time = gesture->end_time; |
| |
| if (!is_absolute) { |
| /* |
| * We send the movement axes as relative values, which causes the |
| * times to be sent as relative values too. This code computes the |
| * right relative values. |
| */ |
| start_time -= dev->last.valuators[CMT_AXIS_DBL_START_TIME]; |
| end_time -= dev->last.valuators[CMT_AXIS_DBL_END_TIME]; |
| } |
| |
| valuator_mask_set_double(mask, CMT_AXIS_DBL_START_TIME, start_time); |
| valuator_mask_set_double(mask, CMT_AXIS_DBL_END_TIME, end_time); |
| } |
| |
| static void SetOrdinalValues(ValuatorMask* mask, |
| DeviceIntPtr dev, |
| float x, |
| float y, |
| BOOL is_absolute) { |
| if (!is_absolute) { |
| /* |
| * We send the movement axes as relative values, which causes the |
| * times to be sent as relative values too. This code computes the |
| * right relative values. |
| */ |
| x -= dev->last.valuators[CMT_AXIS_ORDINAL_X]; |
| y -= dev->last.valuators[CMT_AXIS_ORDINAL_Y]; |
| } |
| |
| valuator_mask_set_double(mask, CMT_AXIS_ORDINAL_X, x); |
| valuator_mask_set_double(mask, CMT_AXIS_ORDINAL_Y, y); |
| } |
| |
| static void Gesture_Gesture_Ready(void* client_data, |
| const struct Gesture* gesture) |
| { |
| GesturePtr rec = client_data; |
| DeviceIntPtr dev = rec->dev; |
| ValuatorMask* mask = rec->mask; |
| InputInfoPtr info = dev->public.devicePrivate; |
| CmtDevicePtr cmt = info->private; |
| |
| if (cmt->props.raw_passthrough) { |
| DBG(info, "Gesture Suppressed"); |
| return; |
| } |
| |
| DBG(info, "Gesture Start: %f End: %f \n", |
| gesture->start_time, gesture->end_time); |
| |
| valuator_mask_zero(mask); |
| switch (gesture->type) { |
| case kGestureTypeContactInitiated: |
| /* TODO(adlr): handle contact initiated */ |
| break; |
| case kGestureTypeMove: { |
| const GestureMove* move = &gesture->details.move; |
| DBG(info, "Gesture Move: (%f, %f) [%f, %f]\n", |
| move->dx, move->dy, move->ordinal_dx, move->ordinal_dy); |
| valuator_mask_set_double(mask, CMT_AXIS_X, move->dx); |
| valuator_mask_set_double(mask, CMT_AXIS_Y, move->dy); |
| SetTimeValues(mask, gesture, dev, FALSE); |
| SetOrdinalValues(mask, |
| dev, |
| move->ordinal_dx, |
| move->ordinal_dy, |
| FALSE); |
| xf86PostMotionEventM(dev, FALSE, mask); |
| break; |
| } |
| case kGestureTypeScroll: { |
| const GestureScroll* scroll = &gesture->details.scroll; |
| DBG(info, "Gesture Scroll: (%f, %f) [%f, %f]\n", |
| scroll->dx, scroll->dy, scroll->ordinal_dx, scroll->ordinal_dy); |
| valuator_mask_set_double(mask, CMT_AXIS_SCROLL_X, scroll->dx); |
| valuator_mask_set_double(mask, CMT_AXIS_SCROLL_Y, scroll->dy); |
| valuator_mask_set_double(mask, CMT_AXIS_FINGER_COUNT, 2.0); |
| SetTimeValues(mask, gesture, dev, TRUE); |
| SetOrdinalValues(mask, |
| dev, |
| scroll->ordinal_dx, |
| scroll->ordinal_dy, |
| TRUE); |
| xf86PostMotionEventM(dev, TRUE, mask); |
| break; |
| } |
| case kGestureTypeButtonsChange: { |
| const GestureButtonsChange* buttons = &gesture->details.buttons; |
| DBG(info, "Gesture Button Change: down=0x%02x up=0x%02x\n", |
| buttons->down, buttons->up); |
| SetTimeValues(mask, gesture, dev, TRUE); |
| if (buttons->down & GESTURES_BUTTON_LEFT) |
| xf86PostButtonEventM(dev, TRUE, CMT_BTN_LEFT, 1, mask); |
| if (buttons->down & GESTURES_BUTTON_MIDDLE) |
| xf86PostButtonEventM(dev, TRUE, CMT_BTN_MIDDLE, 1, mask); |
| if (buttons->down & GESTURES_BUTTON_RIGHT) |
| xf86PostButtonEventM(dev, TRUE, CMT_BTN_RIGHT, 1, mask); |
| if (buttons->up & GESTURES_BUTTON_LEFT) |
| xf86PostButtonEventM(dev, TRUE, CMT_BTN_LEFT, 0, mask); |
| if (buttons->up & GESTURES_BUTTON_MIDDLE) |
| xf86PostButtonEventM(dev, TRUE, CMT_BTN_MIDDLE, 0, mask); |
| if (buttons->up & GESTURES_BUTTON_RIGHT) |
| xf86PostButtonEventM(dev, TRUE, CMT_BTN_RIGHT, 0, mask); |
| break; |
| } |
| case kGestureTypeFling: { |
| const GestureFling* fling = &gesture->details.fling; |
| DBG(info, "Gesture Fling: (%f, %f) [%f, %f] fling_state=%d\n", |
| fling->vx, fling->vy, fling->ordinal_vx, fling->ordinal_vy, |
| fling->fling_state); |
| valuator_mask_set_double(mask, CMT_AXIS_DBL_FLING_VX, fling->vx); |
| valuator_mask_set_double(mask, CMT_AXIS_DBL_FLING_VY, fling->vy); |
| valuator_mask_set(mask, CMT_AXIS_FLING_STATE, fling->fling_state); |
| SetTimeValues(mask, gesture, dev, TRUE); |
| SetOrdinalValues(mask, |
| dev, |
| fling->ordinal_vx, |
| fling->ordinal_vy, |
| TRUE); |
| xf86PostMotionEventM(dev, TRUE, mask); |
| break; |
| } |
| case kGestureTypeSwipe: { |
| const GestureSwipe* swipe = &gesture->details.swipe; |
| DBG(info, "Gesture Swipe: (%f, %f) [%f, %f]\n", |
| swipe->dx, swipe->dy, swipe->ordinal_dx, swipe->ordinal_dy); |
| valuator_mask_set_double(mask, CMT_AXIS_SCROLL_X, swipe->dx); |
| valuator_mask_set_double(mask, CMT_AXIS_SCROLL_Y, swipe->dy); |
| valuator_mask_set_double(mask, CMT_AXIS_FINGER_COUNT, 3.0); |
| SetTimeValues(mask, gesture, dev, TRUE); |
| SetOrdinalValues(mask, |
| dev, |
| swipe->ordinal_dx, |
| swipe->ordinal_dy, |
| TRUE); |
| xf86PostMotionEventM(dev, TRUE, mask); |
| break; |
| } |
| case kGestureTypeSwipeLift: |
| DBG(info, "Gesture Swipe Lift\n"); |
| // Turn a swipe lift into a fling start. |
| SetTimeValues(mask, gesture, dev, TRUE); |
| valuator_mask_set_double(mask, CMT_AXIS_DBL_FLING_VX, 0); |
| valuator_mask_set_double(mask, CMT_AXIS_DBL_FLING_VY, 0); |
| valuator_mask_set(mask, CMT_AXIS_FLING_STATE, 0); |
| xf86PostMotionEventM(dev, TRUE, mask); |
| break; |
| case kGestureTypePinch: { |
| const GesturePinch* pinch = &gesture->details.pinch; |
| DBG(info, "Gesture Pinch: dz=%f [%f]\n", |
| pinch->dz, pinch->ordinal_dz); |
| break; |
| } |
| case kGestureTypeMetrics: { |
| const GestureMetrics* metrics = &gesture->details.metrics; |
| DBG(info, "Gesture Metrics: [%f, %f] type=%d\n", |
| metrics->data[0], metrics->data[1], metrics->type); |
| valuator_mask_set_double(mask, CMT_AXIS_METRICS_DATA1, |
| metrics->data[0]); |
| valuator_mask_set_double(mask, CMT_AXIS_METRICS_DATA2, |
| metrics->data[1]); |
| valuator_mask_set(mask, CMT_AXIS_METRICS_TYPE, metrics->type); |
| SetTimeValues(mask, gesture, dev, TRUE); |
| xf86PostMotionEventM(dev, TRUE, mask); |
| break; |
| } |
| default: |
| ERR(info, "Unrecognized gesture type (%u)\n", gesture->type); |
| break; |
| } |
| } |
| |
| static GesturesTimer* |
| Gesture_TimerCreate(void* provider_data) |
| { |
| DeviceIntPtr dev = provider_data; |
| InputInfoPtr info = dev->public.devicePrivate; |
| CmtDevicePtr cmt = info->private; |
| GesturesTimer* timer = (GesturesTimer*)calloc(1, sizeof(GesturesTimer)); |
| if (!timer) |
| return NULL; |
| timer->timer = TimerSet(NULL, 0, 0, NULL, 0); |
| if (!timer->timer) { |
| free(timer); |
| return NULL; |
| } |
| timer->is_monotonic = cmt->evdev.info.is_monotonic; |
| return timer; |
| } |
| |
| static void |
| Gesture_TimerSet(void* provider_data, |
| GesturesTimer* timer, |
| stime_t delay, |
| GesturesTimerCallback callback, |
| void* callback_data) |
| { |
| CARD32 ms = delay * 1000.0; |
| |
| if (!timer) |
| return; |
| timer->callback = callback; |
| timer->callback_data = callback_data; |
| if (ms == 0) |
| ms = 1; |
| TimerSet(timer->timer, 0, ms, Gesture_TimerCallback, timer); |
| } |
| |
| static void |
| Gesture_TimerCancel(void* provider_data, GesturesTimer* timer) |
| { |
| TimerCancel(timer->timer); |
| } |
| |
| static void |
| Gesture_TimerFree(void* provider_data, GesturesTimer* timer) |
| { |
| TimerFree(timer->timer); |
| timer->timer = NULL; |
| free(timer); |
| } |
| |
| static CARD32 |
| Gesture_TimerCallback(OsTimerPtr timer, |
| CARD32 millis, |
| pointer callback_data) |
| { |
| GesturesTimer* tm = callback_data; |
| stime_t now; |
| stime_t rc; |
| CARD32 next_timeout = 0; |
| |
| if (tm->is_monotonic) { |
| struct timespec ts; |
| clock_gettime(CLOCK_MONOTONIC, &ts); |
| now = StimeFromTimespec(&ts); |
| } else { |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| now = StimeFromTimeval(&tv); |
| } |
| |
| rc = tm->callback(now, tm->callback_data); |
| if (rc >= 0.0) { |
| next_timeout = rc * 1000.0; |
| if (next_timeout == 0) |
| next_timeout = 1; |
| } |
| |
| return next_timeout; |
| } |
| |
| static enum GestureInterpreterDeviceClass |
| Gesture_Device_Class(EvdevClass cls) { |
| switch (cls) { |
| case EvdevClassMouse: |
| return GESTURES_DEVCLASS_MOUSE; |
| case EvdevClassMultitouchMouse: |
| return GESTURES_DEVCLASS_MULTITOUCH_MOUSE; |
| case EvdevClassTouchpad: |
| return GESTURES_DEVCLASS_TOUCHPAD; |
| case EvdevClassTouchscreen: |
| return GESTURES_DEVCLASS_TOUCHSCREEN; |
| default: |
| return GESTURES_DEVCLASS_UNKNOWN; |
| } |
| } |
| |
| _X_EXPORT void gestures_log(int verb, const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| if (verb > 0) |
| xf86VDrvMsgVerb(-1, X_INFO, 7, fmt, args); |
| else |
| xf86VDrvMsgVerb(-1, X_ERROR, 0, fmt, args); |
| va_end(args); |
| } |