HID: lenovo: Hide middle-button press until release
authorJamie Lentin <jm@lentin.co.uk>
Tue, 11 Aug 2015 21:40:52 +0000 (22:40 +0100)
committerJiri Kosina <jkosina@suse.cz>
Wed, 12 Aug 2015 12:32:31 +0000 (14:32 +0200)
Don't relay a middle button press to userspace until release, and then
only if there was no scroll events inbetween. This is closer to what
Xorg's wheel emulation does, and avoids spurious middle-click pastes.

Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-lenovo.c

index d40221d781883fff8c98a6400ecff1540125a875..e4bc6cb6d7fa5d5481f650c87fde01a7348fdb6a 100644 (file)
@@ -37,6 +37,7 @@ struct lenovo_drvdata_tpkbd {
 };
 
 struct lenovo_drvdata_cptkbd {
+       u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
        bool fn_lock;
        int sensitivity;
 };
@@ -316,6 +317,53 @@ static int lenovo_raw_event(struct hid_device *hdev,
        return 0;
 }
 
+static int lenovo_event_cptkbd(struct hid_device *hdev,
+               struct hid_field *field, struct hid_usage *usage, __s32 value)
+{
+       struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+
+       /* "wheel" scroll events */
+       if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
+                       usage->code == REL_HWHEEL)) {
+               /* Scroll events disable middle-click event */
+               cptkbd_data->middlebutton_state = 2;
+               return 0;
+       }
+
+       /* Middle click events */
+       if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
+               if (value == 1) {
+                       cptkbd_data->middlebutton_state = 1;
+               } else if (value == 0) {
+                       if (cptkbd_data->middlebutton_state == 1) {
+                               /* No scrolling inbetween, send middle-click */
+                               input_event(field->hidinput->input,
+                                       EV_KEY, BTN_MIDDLE, 1);
+                               input_sync(field->hidinput->input);
+                               input_event(field->hidinput->input,
+                                       EV_KEY, BTN_MIDDLE, 0);
+                               input_sync(field->hidinput->input);
+                       }
+                       cptkbd_data->middlebutton_state = 0;
+               }
+               return 1;
+       }
+
+       return 0;
+}
+
+static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       switch (hdev->product) {
+       case USB_DEVICE_ID_LENOVO_CUSBKBD:
+       case USB_DEVICE_ID_LENOVO_CBTKBD:
+               return lenovo_event_cptkbd(hdev, field, usage, value);
+       default:
+               return 0;
+       }
+}
+
 static int lenovo_features_set_tpkbd(struct hid_device *hdev)
 {
        struct hid_report *report;
@@ -708,6 +756,7 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
                hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
 
        /* Set keyboard settings to known state */
+       cptkbd_data->middlebutton_state = 0;
        cptkbd_data->fn_lock = true;
        cptkbd_data->sensitivity = 0x05;
        lenovo_features_set_cptkbd(hdev);
@@ -835,6 +884,7 @@ static struct hid_driver lenovo_driver = {
        .probe = lenovo_probe,
        .remove = lenovo_remove,
        .raw_event = lenovo_raw_event,
+       .event = lenovo_event,
        .report_fixup = lenovo_report_fixup,
 };
 module_hid_driver(lenovo_driver);