Blob Blame History Raw
/*
 * Copyright © 2014-2015 Red Hat, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "config.h"

#include <stdint.h>

#include "evdev.h"

#define MIDDLEBUTTON_TIMEOUT ms2us(50)

/*****************************************
 * BEFORE YOU EDIT THIS FILE, look at the state diagram in
 * doc/middle-button-emulation-state-machine.svg, or online at
 * https://drive.google.com/file/d/0B1NwWmji69nodUJncXRMc1FvY1k/view?usp=sharing
 * (it's a http://draw.io diagram)
 *
 * Any changes in this file must be represented in the diagram.
 *
 * Note in regards to the state machine: it only handles left, right and
 * emulated middle button clicks, all other button events are passed
 * through. When in the PASSTHROUGH state, all events are passed through
 * as-is.
 */

static inline const char*
middlebutton_state_to_str(enum evdev_middlebutton_state state)
{
	switch (state) {
	CASE_RETURN_STRING(MIDDLEBUTTON_IDLE);
	CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_DOWN);
	CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_DOWN);
	CASE_RETURN_STRING(MIDDLEBUTTON_MIDDLE);
	CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_UP_PENDING);
	CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_UP_PENDING);
	CASE_RETURN_STRING(MIDDLEBUTTON_PASSTHROUGH);
	CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_LR);
	CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_L);
	CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_R);
	}

	return NULL;
}

static inline const char*
middlebutton_event_to_str(enum evdev_middlebutton_event event)
{
	switch (event) {
	CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_DOWN);
	CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_DOWN);
	CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_OTHER);
	CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_UP);
	CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_UP);
	CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_TIMEOUT);
	CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_ALL_UP);
	}

	return NULL;
}

static void
middlebutton_state_error(struct evdev_device *device,
			 enum evdev_middlebutton_event event)
{
	evdev_log_bug_libinput(device,
			       "Invalid event %s in middle btn state %s\n",
			       middlebutton_event_to_str(event),
			       middlebutton_state_to_str(device->middlebutton.state));
}

static void
middlebutton_timer_set(struct evdev_device *device, uint64_t now)
{
	libinput_timer_set(&device->middlebutton.timer,
			   now + MIDDLEBUTTON_TIMEOUT);
}

static void
middlebutton_timer_cancel(struct evdev_device *device)
{
	libinput_timer_cancel(&device->middlebutton.timer);
}

static inline void
middlebutton_set_state(struct evdev_device *device,
		       enum evdev_middlebutton_state state,
		       uint64_t now)
{
	switch (state) {
	case MIDDLEBUTTON_LEFT_DOWN:
	case MIDDLEBUTTON_RIGHT_DOWN:
		middlebutton_timer_set(device, now);
		device->middlebutton.first_event_time = now;
		break;
	case MIDDLEBUTTON_IDLE:
	case MIDDLEBUTTON_MIDDLE:
	case MIDDLEBUTTON_LEFT_UP_PENDING:
	case MIDDLEBUTTON_RIGHT_UP_PENDING:
	case MIDDLEBUTTON_PASSTHROUGH:
	case MIDDLEBUTTON_IGNORE_LR:
	case MIDDLEBUTTON_IGNORE_L:
	case MIDDLEBUTTON_IGNORE_R:
		middlebutton_timer_cancel(device);
		break;
	}

	device->middlebutton.state = state;
}

static void
middlebutton_post_event(struct evdev_device *device,
			uint64_t now,
			int button,
			enum libinput_button_state state)
{
	evdev_pointer_notify_button(device,
				    now,
				    button,
				    state);
}

static int
evdev_middlebutton_idle_handle_event(struct evdev_device *device,
				     uint64_t time,
				     enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
		middlebutton_set_state(device, MIDDLEBUTTON_LEFT_DOWN, time);
		break;
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_set_state(device, MIDDLEBUTTON_RIGHT_DOWN, time);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
	case MIDDLEBUTTON_EVENT_L_UP:
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		break;
	}

	return 1;
}

static int
evdev_middlebutton_ldown_handle_event(struct evdev_device *device,
				      uint64_t time,
				      enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_post_event(device, time,
					BTN_MIDDLE,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		middlebutton_post_event(device, time,
					BTN_LEFT,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device,
				       MIDDLEBUTTON_PASSTHROUGH,
				       time);
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_L_UP:
		middlebutton_post_event(device,
					device->middlebutton.first_event_time,
					BTN_LEFT,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_post_event(device, time,
					BTN_LEFT,
					LIBINPUT_BUTTON_STATE_RELEASED);
		middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_post_event(device,
					device->middlebutton.first_event_time,
					BTN_LEFT,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device,
				       MIDDLEBUTTON_PASSTHROUGH,
				       time);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_state_error(device, event);
		break;
	}

	return 1;
}

static int
evdev_middlebutton_rdown_handle_event(struct evdev_device *device,
				      uint64_t time,
				      enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
		middlebutton_post_event(device, time,
					BTN_MIDDLE,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		middlebutton_post_event(device,
					device->middlebutton.first_event_time,
					BTN_RIGHT,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device,
				       MIDDLEBUTTON_PASSTHROUGH,
				       time);
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
		middlebutton_post_event(device,
					device->middlebutton.first_event_time,
					BTN_RIGHT,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_post_event(device, time,
					BTN_RIGHT,
					LIBINPUT_BUTTON_STATE_RELEASED);
		middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_L_UP:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_post_event(device,
					device->middlebutton.first_event_time,
					BTN_RIGHT,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device,
				       MIDDLEBUTTON_PASSTHROUGH,
				       time);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_state_error(device, event);
		break;
	}

	return 1;
}

static int
evdev_middlebutton_middle_handle_event(struct evdev_device *device,
				       uint64_t time,
				       enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		middlebutton_post_event(device, time,
					BTN_MIDDLE,
					LIBINPUT_BUTTON_STATE_RELEASED);
		middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_LR, time);
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
		middlebutton_post_event(device, time,
					BTN_MIDDLE,
					LIBINPUT_BUTTON_STATE_RELEASED);
		middlebutton_set_state(device,
				       MIDDLEBUTTON_LEFT_UP_PENDING,
				       time);
		break;
	case MIDDLEBUTTON_EVENT_L_UP:
		middlebutton_post_event(device, time,
					BTN_MIDDLE,
					LIBINPUT_BUTTON_STATE_RELEASED);
		middlebutton_set_state(device,
				       MIDDLEBUTTON_RIGHT_UP_PENDING,
				       time);
		break;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_state_error(device, event);
		break;
	}

	return 1;
}

static int
evdev_middlebutton_lup_pending_handle_event(struct evdev_device *device,
					    uint64_t time,
					    enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_post_event(device, time,
					BTN_MIDDLE,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_L_UP:
		middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_state_error(device, event);
		break;
	}

	return 1;
}

static int
evdev_middlebutton_rup_pending_handle_event(struct evdev_device *device,
					    uint64_t time,
					    enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
		middlebutton_post_event(device, time,
					BTN_MIDDLE,
					LIBINPUT_BUTTON_STATE_PRESSED);
		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
		middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
		break;
	case MIDDLEBUTTON_EVENT_L_UP:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_state_error(device, event);
		break;
	}

	return 1;
}

static int
evdev_middlebutton_passthrough_handle_event(struct evdev_device *device,
					    uint64_t time,
					    enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
	case MIDDLEBUTTON_EVENT_R_DOWN:
	case MIDDLEBUTTON_EVENT_OTHER:
	case MIDDLEBUTTON_EVENT_R_UP:
	case MIDDLEBUTTON_EVENT_L_UP:
		return 0;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
		break;
	}

	return 1;
}

static int
evdev_middlebutton_ignore_lr_handle_event(struct evdev_device *device,
					  uint64_t time,
					  enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
		middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
		break;
	case MIDDLEBUTTON_EVENT_L_UP:
		middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
		break;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_state_error(device, event);
		break;
	}

	return 1;
}

static int
evdev_middlebutton_ignore_l_handle_event(struct evdev_device *device,
					 uint64_t time,
					 enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_R_DOWN:
		return 0;
	case MIDDLEBUTTON_EVENT_OTHER:
	case MIDDLEBUTTON_EVENT_R_UP:
		return 0;
	case MIDDLEBUTTON_EVENT_L_UP:
		middlebutton_set_state(device,
				       MIDDLEBUTTON_PASSTHROUGH,
				       time);
		break;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
	case MIDDLEBUTTON_EVENT_ALL_UP:
		middlebutton_state_error(device, event);
		break;
	}

	return 1;
}
static int
evdev_middlebutton_ignore_r_handle_event(struct evdev_device *device,
					 uint64_t time,
					 enum evdev_middlebutton_event event)
{
	switch (event) {
	case MIDDLEBUTTON_EVENT_L_DOWN:
		return 0;
	case MIDDLEBUTTON_EVENT_R_DOWN:
		middlebutton_state_error(device, event);
		break;
	case MIDDLEBUTTON_EVENT_OTHER:
		return 0;
	case MIDDLEBUTTON_EVENT_R_UP:
		middlebutton_set_state(device,
				       MIDDLEBUTTON_PASSTHROUGH,
				       time);
		break;
	case MIDDLEBUTTON_EVENT_L_UP:
		return 0;
	case MIDDLEBUTTON_EVENT_TIMEOUT:
	case MIDDLEBUTTON_EVENT_ALL_UP:
		break;
	}

	return 1;
}

static int
evdev_middlebutton_handle_event(struct evdev_device *device,
				uint64_t time,
				enum evdev_middlebutton_event event)
{
	int rc = 0;
	enum evdev_middlebutton_state current;

	current = device->middlebutton.state;

	switch (current) {
	case MIDDLEBUTTON_IDLE:
		rc = evdev_middlebutton_idle_handle_event(device, time, event);
		break;
	case MIDDLEBUTTON_LEFT_DOWN:
		rc = evdev_middlebutton_ldown_handle_event(device, time, event);
		break;
	case MIDDLEBUTTON_RIGHT_DOWN:
		rc = evdev_middlebutton_rdown_handle_event(device, time, event);
		break;
	case MIDDLEBUTTON_MIDDLE:
		rc = evdev_middlebutton_middle_handle_event(device, time, event);
		break;
	case MIDDLEBUTTON_LEFT_UP_PENDING:
		rc = evdev_middlebutton_lup_pending_handle_event(device,
								 time,
								 event);
		break;
	case MIDDLEBUTTON_RIGHT_UP_PENDING:
		rc = evdev_middlebutton_rup_pending_handle_event(device,
								 time,
								 event);
		break;
	case MIDDLEBUTTON_PASSTHROUGH:
		rc = evdev_middlebutton_passthrough_handle_event(device,
								 time,
								 event);
		break;
	case MIDDLEBUTTON_IGNORE_LR:
		rc = evdev_middlebutton_ignore_lr_handle_event(device,
							       time,
							       event);
		break;
	case MIDDLEBUTTON_IGNORE_L:
		rc = evdev_middlebutton_ignore_l_handle_event(device,
							      time,
							      event);
		break;
	case MIDDLEBUTTON_IGNORE_R:
		rc = evdev_middlebutton_ignore_r_handle_event(device,
							      time,
							      event);
		break;
	default:
		evdev_log_bug_libinput(device,
				       "Invalid middle button state %d\n",
				       current);
		break;
	}

	evdev_log_debug(device,
			"middlebuttonstate: %s → %s → %s, rc %d\n",
			middlebutton_state_to_str(current),
			middlebutton_event_to_str(event),
			middlebutton_state_to_str(device->middlebutton.state),
			rc);

	return rc;
}

static inline void
evdev_middlebutton_apply_config(struct evdev_device *device)
{
	if (device->middlebutton.want_enabled ==
	    device->middlebutton.enabled)
		return;

	if (device->middlebutton.button_mask != 0)
		return;

	device->middlebutton.enabled = device->middlebutton.want_enabled;
}

bool
evdev_middlebutton_filter_button(struct evdev_device *device,
				 uint64_t time,
				 int button,
				 enum libinput_button_state state)
{
	enum evdev_middlebutton_event event;
	bool is_press = state == LIBINPUT_BUTTON_STATE_PRESSED;
	int rc;
	unsigned int bit = (button - BTN_LEFT);
	uint32_t old_mask = 0;

	if (!device->middlebutton.enabled)
		return false;

	switch (button) {
	case BTN_LEFT:
		if (is_press)
			event = MIDDLEBUTTON_EVENT_L_DOWN;
		else
			event = MIDDLEBUTTON_EVENT_L_UP;
		break;
	case BTN_RIGHT:
		if (is_press)
			event = MIDDLEBUTTON_EVENT_R_DOWN;
		else
			event = MIDDLEBUTTON_EVENT_R_UP;
		break;

	/* BTN_MIDDLE counts as "other" and resets middle button
	 * emulation */
	case BTN_MIDDLE:
	default:
		event = MIDDLEBUTTON_EVENT_OTHER;
		break;
	}

	if (button < BTN_LEFT ||
	    bit >= sizeof(device->middlebutton.button_mask) * 8) {
		evdev_log_bug_libinput(device,
				       "Button mask too small for %s\n",
				       libevdev_event_code_get_name(EV_KEY,
								    button));
		return true;
	}

	rc = evdev_middlebutton_handle_event(device, time, event);

	old_mask = device->middlebutton.button_mask;
	if (is_press)
		device->middlebutton.button_mask |= 1 << bit;
	else
		device->middlebutton.button_mask &= ~(1 << bit);

	if (old_mask != device->middlebutton.button_mask &&
	    device->middlebutton.button_mask == 0) {
		evdev_middlebutton_handle_event(device,
						time,
						MIDDLEBUTTON_EVENT_ALL_UP);
		evdev_middlebutton_apply_config(device);
	}

	return rc;
}

static void
evdev_middlebutton_handle_timeout(uint64_t now, void *data)
{
	struct evdev_device *device = evdev_device(data);

	evdev_middlebutton_handle_event(device, now, MIDDLEBUTTON_EVENT_TIMEOUT);
}

int
evdev_middlebutton_is_available(struct libinput_device *device)
{
	return 1;
}

static enum libinput_config_status
evdev_middlebutton_set(struct libinput_device *device,
		       enum libinput_config_middle_emulation_state enable)
{
	struct evdev_device *evdev = evdev_device(device);

	switch (enable) {
	case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:
		evdev->middlebutton.want_enabled = true;
		break;
	case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
		evdev->middlebutton.want_enabled = false;
		break;
	default:
		return LIBINPUT_CONFIG_STATUS_INVALID;
	}

	evdev_middlebutton_apply_config(evdev);

	return LIBINPUT_CONFIG_STATUS_SUCCESS;
}

enum libinput_config_middle_emulation_state
evdev_middlebutton_get(struct libinput_device *device)
{
	struct evdev_device *evdev = evdev_device(device);

	return evdev->middlebutton.want_enabled ?
			LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
			LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
}

enum libinput_config_middle_emulation_state
evdev_middlebutton_get_default(struct libinput_device *device)
{
	struct evdev_device *evdev = evdev_device(device);

	return evdev->middlebutton.enabled_default ?
			LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
			LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
}

void
evdev_init_middlebutton(struct evdev_device *device,
			bool enable,
			bool want_config)
{
	char timer_name[64];

	snprintf(timer_name,
		 sizeof(timer_name),
		 "%s middlebutton",
		 evdev_device_get_sysname(device));
	libinput_timer_init(&device->middlebutton.timer,
			    evdev_libinput_context(device),
			    timer_name,
			    evdev_middlebutton_handle_timeout,
			    device);
	device->middlebutton.enabled_default = enable;
	device->middlebutton.want_enabled = enable;
	device->middlebutton.enabled = enable;

	if (!want_config)
		return;

	device->middlebutton.config.available = evdev_middlebutton_is_available;
	device->middlebutton.config.set = evdev_middlebutton_set;
	device->middlebutton.config.get = evdev_middlebutton_get;
	device->middlebutton.config.get_default = evdev_middlebutton_get_default;
	device->base.config.middle_emulation = &device->middlebutton.config;
}