int
tp_init_buttons(struct tp_dispatch *tp,
                struct evdev_device *device)
{
    struct libinput *libinput = tp_libinput_context(tp);
    struct tp_touch *t;
    const struct input_absinfo *absinfo_x, *absinfo_y;

    tp->buttons.is_clickpad = libevdev_has_property(device->evdev,
                              INPUT_PROP_BUTTONPAD);
    tp->buttons.has_topbuttons = libevdev_has_property(device->evdev,
                                 INPUT_PROP_TOPBUTTONPAD);

    if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE) ||
            libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT)) {
        if (tp->buttons.is_clickpad)
            log_bug_kernel(libinput,
                           "%s: clickpad advertising right button\n",
                           device->devname);
    } else if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_LEFT) &&
               !tp->buttons.is_clickpad &&
               libevdev_get_id_vendor(device->evdev) != VENDOR_ID_APPLE) {
        log_bug_kernel(libinput,
                       "%s: non clickpad without right button?\n",
                       device->devname);
    }

    absinfo_x = device->abs.absinfo_x;
    absinfo_y = device->abs.absinfo_y;

    /* pinned-finger motion threshold, see tp_unpin_finger. */
    tp->buttons.motion_dist.x_scale_coeff = 1.0/absinfo_x->resolution;
    tp->buttons.motion_dist.y_scale_coeff = 1.0/absinfo_y->resolution;

    tp->buttons.config_method.get_methods = tp_button_config_click_get_methods;
    tp->buttons.config_method.set_method = tp_button_config_click_set_method;
    tp->buttons.config_method.get_method = tp_button_config_click_get_method;
    tp->buttons.config_method.get_default_method = tp_button_config_click_get_default_method;
    tp->device->base.config.click_method = &tp->buttons.config_method;

    tp->buttons.click_method = tp_click_get_default_method(tp);
    tp_switch_click_method(tp);

    tp_init_top_softbuttons(tp, device, 1.0);

    tp_init_middlebutton_emulation(tp, device);

    tp_for_each_touch(tp, t) {
        t->button.state = BUTTON_STATE_NONE;
        libinput_timer_init(&t->button.timer,
                            tp_libinput_context(tp),
                            tp_button_handle_timeout, t);
    }
int
tp_process_button(struct tp_dispatch *tp,
		  const struct input_event *e,
		  uint64_t time)
{
	struct libinput *libinput = tp_libinput_context(tp);
	uint32_t mask = 1 << (e->code - BTN_LEFT);

	/* Ignore other buttons on clickpads */
	if (tp->buttons.is_clickpad && e->code != BTN_LEFT) {
		log_bug_kernel(libinput,
			       "received %s button event on a clickpad\n",
			       libevdev_event_code_get_name(EV_KEY, e->code));
		return 0;
	}

	if (e->value) {
		tp->buttons.state |= mask;
		tp->queued |= TOUCHPAD_EVENT_BUTTON_PRESS;
	} else {
		tp->buttons.state &= ~mask;
		tp->queued |= TOUCHPAD_EVENT_BUTTON_RELEASE;
	}

	return 0;
}
static void
tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp,
			       struct tp_touch *t,
			       enum scroll_event event)
{
	struct libinput *libinput = tp_libinput_context(tp);

	switch (event) {
	case SCROLL_EVENT_TOUCH:
		log_bug_libinput(libinput,
				 "unexpected scroll event %d in edge new state\n",
				 event);
		break;
	case SCROLL_EVENT_MOTION:
		t->scroll.edge &= tp_touch_get_edge(tp, t);
		if (!t->scroll.edge)
			tp_edge_scroll_set_state(tp, t,
					EDGE_SCROLL_TOUCH_STATE_AREA);
		break;
	case SCROLL_EVENT_RELEASE:
		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
		break;
	case SCROLL_EVENT_TIMEOUT:
	case SCROLL_EVENT_POSTED:
		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_EDGE);
		break;
	}
}
static void
tp_edge_scroll_handle_event(struct tp_dispatch *tp,
			    struct tp_touch *t,
			    enum scroll_event event)
{
	struct libinput *libinput = tp_libinput_context(tp);
	enum tp_edge_scroll_touch_state current = t->scroll.edge_state;

	switch (current) {
	case EDGE_SCROLL_TOUCH_STATE_NONE:
		tp_edge_scroll_handle_none(tp, t, event);
		break;
	case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
		tp_edge_scroll_handle_edge_new(tp, t, event);
		break;
	case EDGE_SCROLL_TOUCH_STATE_EDGE:
		tp_edge_scroll_handle_edge(tp, t, event);
		break;
	case EDGE_SCROLL_TOUCH_STATE_AREA:
		tp_edge_scroll_handle_area(tp, t, event);
		break;
	}

	log_debug(libinput,
		  "edge state: %s → %s → %s\n",
		  edge_state_to_str(current),
		  edge_event_to_str(event),
		  edge_state_to_str(t->scroll.edge_state));
}
static void
tp_edge_scroll_handle_edge(struct tp_dispatch *tp,
			   struct tp_touch *t,
			   enum scroll_event event)
{
	struct libinput *libinput = tp_libinput_context(tp);

	switch (event) {
	case SCROLL_EVENT_TOUCH:
	case SCROLL_EVENT_TIMEOUT:
		log_bug_libinput(libinput,
				 "unexpected scroll event %d in edge state\n",
				 event);
		break;
	case SCROLL_EVENT_MOTION:
		/* If started at the bottom right, decide in which dir to scroll */
		if (t->scroll.edge == (EDGE_RIGHT | EDGE_BOTTOM)) {
			t->scroll.edge &= tp_touch_get_edge(tp, t);
			if (!t->scroll.edge)
				tp_edge_scroll_set_state(tp, t,
						EDGE_SCROLL_TOUCH_STATE_AREA);
		}
		break;
	case SCROLL_EVENT_RELEASE:
		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
		break;
	case SCROLL_EVENT_POSTED:
		break;
	}
}
static void
tp_edge_scroll_handle_none(struct tp_dispatch *tp,
			   struct tp_touch *t,
			   enum scroll_event event)
{
	struct libinput *libinput = tp_libinput_context(tp);

	switch (event) {
	case SCROLL_EVENT_TOUCH:
		if (tp_touch_get_edge(tp, t)) {
			tp_edge_scroll_set_state(tp, t,
					EDGE_SCROLL_TOUCH_STATE_EDGE_NEW);
		} else {
			tp_edge_scroll_set_state(tp, t,
					EDGE_SCROLL_TOUCH_STATE_AREA);
		}
		break;
	case SCROLL_EVENT_MOTION:
	case SCROLL_EVENT_RELEASE:
	case SCROLL_EVENT_TIMEOUT:
	case SCROLL_EVENT_POSTED:
		log_bug_libinput(libinput,
				 "unexpected scroll event %d in none state\n",
				 event);
		break;
	}
}
static void
tp_tap_multitap_handle_event(struct tp_dispatch *tp,
			      struct tp_touch *t,
			      enum tap_event event, uint64_t time)
{
	struct libinput *libinput = tp_libinput_context(tp);

	switch (event) {
	case TAP_EVENT_RELEASE:
		log_bug_libinput(libinput,
				 "invalid tap event, no fingers are down\n");
		break;
	case TAP_EVENT_TOUCH:
		tp->tap.state = TAP_STATE_MULTITAP_DOWN;
		tp->tap.multitap_last_time = time;
		tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
		tp_tap_set_timer(tp, time);
		break;
	case TAP_EVENT_MOTION:
		log_bug_libinput(libinput,
				 "invalid tap event, no fingers are down\n");
		break;
	case TAP_EVENT_TIMEOUT:
		tp->tap.state = TAP_STATE_IDLE;
		tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
		tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
		break;
	case TAP_EVENT_BUTTON:
		tp->tap.state = TAP_STATE_IDLE;
		tp_tap_clear_timer(tp);
		break;
	case TAP_EVENT_THUMB:
		break;
	}
}
static void
tp_tap_tapped_handle_event(struct tp_dispatch *tp,
			   struct tp_touch *t,
			   enum tap_event event, uint64_t time)
{
	struct libinput *libinput = tp_libinput_context(tp);

	switch (event) {
	case TAP_EVENT_MOTION:
	case TAP_EVENT_RELEASE:
		log_bug_libinput(libinput,
				 "invalid tap event when fingers are up\n");
		break;
	case TAP_EVENT_TOUCH:
		tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP;
		tp_tap_set_timer(tp, time);
		break;
	case TAP_EVENT_TIMEOUT:
		tp->tap.state = TAP_STATE_IDLE;
		tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
		break;
	case TAP_EVENT_BUTTON:
		tp->tap.state = TAP_STATE_DEAD;
		tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
		break;
	case TAP_EVENT_THUMB:
		break;
	}
}
static void
tp_tap_idle_handle_event(struct tp_dispatch *tp,
			 struct tp_touch *t,
			 enum tap_event event, uint64_t time)
{
	struct libinput *libinput = tp_libinput_context(tp);

	switch (event) {
	case TAP_EVENT_TOUCH:
		tp->tap.state = TAP_STATE_TOUCH;
		tp_tap_set_timer(tp, time);
		break;
	case TAP_EVENT_RELEASE:
		break;
	case TAP_EVENT_MOTION:
		log_bug_libinput(libinput,
				 "invalid tap event, no fingers are down\n");
		break;
	case TAP_EVENT_TIMEOUT:
		break;
	case TAP_EVENT_BUTTON:
		tp->tap.state = TAP_STATE_DEAD;
		break;
	case TAP_EVENT_THUMB:
		log_bug_libinput(libinput,
				 "invalid tap event, no fingers down, no thumb\n");
		break;
	}
}
int
tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device)
{
	struct tp_touch *t;
	int edge_width, edge_height;

	/* 7mm edge size */
	edge_width = device->abs.absinfo_x->resolution * 7;
	edge_height = device->abs.absinfo_y->resolution * 7;

	tp->scroll.right_edge = device->abs.absinfo_x->maximum - edge_width;
	tp->scroll.bottom_edge = device->abs.absinfo_y->maximum - edge_height;

	tp_for_each_touch(tp, t) {
		t->scroll.direction = -1;
		libinput_timer_init(&t->scroll.timer,
				    tp_libinput_context(tp),
				    tp_edge_scroll_handle_timeout, t);
	}
static void
tp_button_handle_event(struct tp_dispatch *tp,
		       struct tp_touch *t,
		       enum button_event event,
		       uint64_t time)
{
	struct libinput *libinput = tp_libinput_context(tp);
	enum button_state current = t->button.state;

	switch(t->button.state) {
	case BUTTON_STATE_NONE:
		tp_button_none_handle_event(tp, t, event);
		break;
	case BUTTON_STATE_AREA:
		tp_button_area_handle_event(tp, t, event);
		break;
	case BUTTON_STATE_BOTTOM:
		tp_button_bottom_handle_event(tp, t, event);
		break;
	case BUTTON_STATE_TOP:
		tp_button_top_handle_event(tp, t, event);
		break;
	case BUTTON_STATE_TOP_NEW:
		tp_button_top_new_handle_event(tp, t, event);
		break;
	case BUTTON_STATE_TOP_TO_IGNORE:
		tp_button_top_to_ignore_handle_event(tp, t, event);
		break;
	case BUTTON_STATE_IGNORE:
		tp_button_ignore_handle_event(tp, t, event);
		break;
	}

	if (current != t->button.state)
		log_debug(libinput,
			  "button state: from %s, event %s to %s\n",
			  button_state_to_str(current),
			  button_event_to_str(event),
			  button_state_to_str(t->button.state));
}
static void
tp_edge_scroll_handle_area(struct tp_dispatch *tp,
			   struct tp_touch *t,
			   enum scroll_event event)
{
	struct libinput *libinput = tp_libinput_context(tp);

	switch (event) {
	case SCROLL_EVENT_TOUCH:
	case SCROLL_EVENT_TIMEOUT:
	case SCROLL_EVENT_POSTED:
		log_bug_libinput(libinput,
				 "unexpected scroll event %d in area state\n",
				 event);
		break;
	case SCROLL_EVENT_MOTION:
		break;
	case SCROLL_EVENT_RELEASE:
		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
		break;
	}
}
Example #13
0
static void
tp_tap_handle_event(struct tp_dispatch *tp,
		    struct tp_touch *t,
		    enum tap_event event,
		    uint64_t time)
{
	struct libinput *libinput = tp_libinput_context(tp);
	enum tp_tap_state current;

	current = tp->tap.state;

	switch(tp->tap.state) {
	case TAP_STATE_IDLE:
		tp_tap_idle_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_TOUCH:
		tp_tap_touch_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_HOLD:
		tp_tap_hold_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_TAPPED:
		tp_tap_tapped_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_TOUCH_2:
		tp_tap_touch2_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_TOUCH_2_HOLD:
		tp_tap_touch2_hold_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_TOUCH_3:
		tp_tap_touch3_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_TOUCH_3_HOLD:
		tp_tap_touch3_hold_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_DRAGGING_OR_DOUBLETAP:
		tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_DRAGGING:
		tp_tap_dragging_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_DRAGGING_WAIT:
		tp_tap_dragging_wait_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_DRAGGING_OR_TAP:
		tp_tap_dragging_tap_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_DRAGGING_2:
		tp_tap_dragging2_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_MULTITAP:
		tp_tap_multitap_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_MULTITAP_DOWN:
		tp_tap_multitap_down_handle_event(tp, t, event, time);
		break;
	case TAP_STATE_DEAD:
		tp_tap_dead_handle_event(tp, t, event, time);
		break;
	}

	if (tp->tap.state == TAP_STATE_IDLE || tp->tap.state == TAP_STATE_DEAD)
		tp_tap_clear_timer(tp);

	log_debug(libinput,
		  "tap state: %s → %s → %s\n",
		  tap_state_to_str(current),
		  tap_event_to_str(event),
		  tap_state_to_str(tp->tap.state));
}