evdev_button_scroll_button(struct evdev_device *device,
			   uint64_t time, int is_press)
{
	/* Where the button lock is enabled, we wrap the buttons into
	   their own little state machine and filter out the events.
	 */
	switch (device->scroll.lock_state) {
	case BUTTONSCROLL_LOCK_DISABLED:
		break;
	case BUTTONSCROLL_LOCK_IDLE:
		assert(is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_FIRSTDOWN;
		evdev_log_debug(device, "scroll lock: first down\n");
		break; /* handle event */
	case BUTTONSCROLL_LOCK_FIRSTDOWN:
		assert(!is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_FIRSTUP;
		evdev_log_debug(device, "scroll lock: first up\n");
		return; /* filter release event */
	case BUTTONSCROLL_LOCK_FIRSTUP:
		assert(is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_SECONDDOWN;
		evdev_log_debug(device, "scroll lock: second down\n");
		return; /* filter press event */
	case BUTTONSCROLL_LOCK_SECONDDOWN:
		assert(!is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_IDLE;
		evdev_log_debug(device, "scroll lock: idle\n");
		break; /* handle event */
	}

	if (is_press) {
		enum timer_flags flags = TIMER_FLAG_NONE;

		device->scroll.button_scroll_state = BUTTONSCROLL_BUTTON_DOWN;

		/* Special case: if middle button emulation is enabled and
		 * our scroll button is the left or right button, we only
		 * get here *after* the middle button timeout has expired
		 * for that button press. The time passed is the button-down
		 * time though (which is in the past), so we have to allow
		 * for a negative timer to be set.
		 */
		if (device->middlebutton.enabled &&
		    (device->scroll.button == BTN_LEFT ||
		     device->scroll.button == BTN_RIGHT)) {
			flags = TIMER_FLAG_ALLOW_NEGATIVE;
		}

		libinput_timer_set_flags(&device->scroll.timer,
					 time + DEFAULT_BUTTON_SCROLL_TIMEOUT,
					 flags);
		device->scroll.button_down_time = time;
		evdev_log_debug(device, "btnscroll: down\n");
	} else {
		libinput_timer_cancel(&device->scroll.timer);
		switch(device->scroll.button_scroll_state) {
		case BUTTONSCROLL_IDLE:
			evdev_log_bug_libinput(device,
				       "invalid state IDLE for button up\n");
			break;
		case BUTTONSCROLL_BUTTON_DOWN:
		case BUTTONSCROLL_READY:
			evdev_log_debug(device, "btnscroll: cancel\n");

			/* If the button is released quickly enough or
			 * without scroll events, emit the
			 * button press/release events. */
			evdev_pointer_post_button(device,
					device->scroll.button_down_time,
					device->scroll.button,
					LIBINPUT_BUTTON_STATE_PRESSED);
			evdev_pointer_post_button(device, time,
					device->scroll.button,
					LIBINPUT_BUTTON_STATE_RELEASED);
			break;
		case BUTTONSCROLL_SCROLLING:
			evdev_log_debug(device, "btnscroll: up\n");
			evdev_stop_scroll(device, time,
					  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
			break;
		}

		device->scroll.button_scroll_state = BUTTONSCROLL_IDLE;
	}
}
evdev_button_scroll_button(struct evdev_device *device,
			   uint64_t time, int is_press)
{
	 
	switch (device->scroll.lock_state) {
	case BUTTONSCROLL_LOCK_DISABLED:
		break;
	case BUTTONSCROLL_LOCK_IDLE:
		assert(is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_FIRSTDOWN;
		evdev_log_debug(device, "scroll lock: first down\n");
		break;  
	case BUTTONSCROLL_LOCK_FIRSTDOWN:
		assert(!is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_FIRSTUP;
		evdev_log_debug(device, "scroll lock: first up\n");
		return;  
	case BUTTONSCROLL_LOCK_FIRSTUP:
		assert(is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_SECONDDOWN;
		evdev_log_debug(device, "scroll lock: second down\n");
		return;  
	case BUTTONSCROLL_LOCK_SECONDDOWN:
		assert(!is_press);
		device->scroll.lock_state = BUTTONSCROLL_LOCK_IDLE;
		evdev_log_debug(device, "scroll lock: idle\n");
		break;  
	}

	if (is_press) {
		enum timer_flags flags = TIMER_FLAG_NONE;

		device->scroll.button_scroll_state = BUTTONSCROLL_BUTTON_DOWN;

		 
		if (device->middlebutton.enabled &&
		    (device->scroll.button == BTN_LEFT ||
		     device->scroll.button == BTN_RIGHT)) {
			flags = TIMER_FLAG_ALLOW_NEGATIVE;
		}

		libinput_timer_set_flags(&device->scroll.timer,
					 time + DEFAULT_BUTTON_SCROLL_TIMEOUT,
					 flags);
		device->scroll.button_down_time = time;
		evdev_log_debug(device, "btnscroll: down\n");
	} else {
		libinput_timer_cancel(&device->scroll.timer);
		switch(device->scroll.button_scroll_state) {
		case BUTTONSCROLL_IDLE:
			evdev_log_bug_libinput(device,
				       "invalid state IDLE for button up\n");
			break;
		case BUTTONSCROLL_BUTTON_DOWN:
		case BUTTONSCROLL_READY:
			evdev_log_debug(device, "btnscroll: cancel\n");

			 
			evdev_pointer_post_button(device,
					device->scroll.button_down_time,
					device->scroll.button,
					LIBINPUT_BUTTON_STATE_PRESSED);
			evdev_pointer_post_button(device, time,
					device->scroll.button,
					LIBINPUT_BUTTON_STATE_RELEASED);
			break;
		case BUTTONSCROLL_SCROLLING:
			evdev_log_debug(device, "btnscroll: up\n");
			evdev_stop_scroll(device, time,
					  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
			break;
		}

		device->scroll.button_scroll_state = BUTTONSCROLL_IDLE;
	}
}