diff --git a/meson.build b/meson.build index 1366c09e..ceb3b98e 100644 --- a/meson.build +++ b/meson.build @@ -891,6 +891,7 @@ if get_option('tests') 'test/litest-device-keyboard-razer-blackwidow.c', 'test/litest-device-keyboard-razer-blade-stealth.c', 'test/litest-device-keyboard-razer-blade-stealth-videoswitch.c', + 'test/litest-device-keypad-slide-switch.c', 'test/litest-device-lenovo-scrollpoint.c', 'test/litest-device-lid-switch.c', 'test/litest-device-lid-switch-surface3.c', diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index 54a4c479..f25f581e 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -82,14 +82,17 @@ fallback_interface_get_switch_state(struct evdev_dispatch *evdev_dispatch, switch (sw) { case LIBINPUT_SWITCH_TABLET_MODE: + return dispatch->tablet_mode.sw.state ? LIBINPUT_SWITCH_STATE_ON + : LIBINPUT_SWITCH_STATE_OFF; + break; + case LIBINPUT_SWITCH_KEYPAD_SLIDE: + return dispatch->keypad_slide.sw.state ? LIBINPUT_SWITCH_STATE_ON + : LIBINPUT_SWITCH_STATE_OFF; break; default: /* Internal function only, so we can abort here */ abort(); } - - return dispatch->tablet_mode.sw.state ? LIBINPUT_SWITCH_STATE_ON - : LIBINPUT_SWITCH_STATE_OFF; } static inline bool @@ -833,6 +836,20 @@ fallback_process_switch(struct fallback_dispatch *dispatch, LIBINPUT_SWITCH_TABLET_MODE, state); break; + case EVDEV_SW_KEYPAD_SLIDE: + if (dispatch->keypad_slide.sw.state == e->value) + return; + + dispatch->keypad_slide.sw.state = e->value; + if (e->value) + state = LIBINPUT_SWITCH_STATE_ON; + else + state = LIBINPUT_SWITCH_STATE_OFF; + switch_notify_toggle(&device->base, + time, + LIBINPUT_SWITCH_KEYPAD_SLIDE, + state); + break; default: break; } @@ -1303,6 +1320,13 @@ fallback_interface_sync_initial_state(struct evdev_device *device, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON); } + + if (dispatch->keypad_slide.sw.state) { + switch_notify_toggle(&device->base, + time, + LIBINPUT_SWITCH_KEYPAD_SLIDE, + LIBINPUT_SWITCH_STATE_ON); + } } static void @@ -1733,6 +1757,11 @@ fallback_dispatch_init_switch(struct fallback_dispatch *dispatch, dispatch->tablet_mode.sw.state = val; } + if (device->tags & EVDEV_TAG_KEYPAD_SLIDE_SWITCH) { + val = libevdev_get_event_value(device->evdev, EV_SW, SW_KEYPAD_SLIDE); + dispatch->keypad_slide.sw.state = val; + } + libinput_device_init_event_listener(&dispatch->tablet_mode.other.listener); } diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h index 9bfcf9ad..c89932b7 100644 --- a/src/evdev-fallback.h +++ b/src/evdev-fallback.h @@ -116,6 +116,13 @@ struct fallback_dispatch { } other; } tablet_mode; + struct { + /* Switch */ + struct { + int state; + } sw; + } keypad_slide; + /* Bitmask of pressed keys used to ignore initial release events from * the kernel. */ unsigned long hw_key_mask[NLONGS(KEY_CNT)]; diff --git a/src/evdev-frame.h b/src/evdev-frame.h index ccf6f385..3dfe7eea 100644 --- a/src/evdev-frame.h +++ b/src/evdev-frame.h @@ -139,6 +139,7 @@ enum evdev_usage { EVDEV_SW_LID = _evbit(EV_SW, SW_LID), EVDEV_SW_TABLET_MODE = _evbit(EV_SW, SW_TABLET_MODE), + EVDEV_SW_KEYPAD_SLIDE = _evbit(EV_SW, SW_KEYPAD_SLIDE), EVDEV_SW_MAX = _evbit(EV_SW, SW_MAX), EVDEV_MSC_SCAN = _evbit(EV_MSC, MSC_SCAN), @@ -248,6 +249,7 @@ evdev_usage_name(evdev_usage_t usage) CASE_RETURN_STRING(EVDEV_SW_LID); CASE_RETURN_STRING(EVDEV_SW_TABLET_MODE); + CASE_RETURN_STRING(EVDEV_SW_KEYPAD_SLIDE); CASE_RETURN_STRING(EVDEV_SW_MAX); CASE_RETURN_STRING(EVDEV_MSC_SCAN); diff --git a/src/evdev.c b/src/evdev.c index 1457a254..c37361c6 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1999,6 +1999,11 @@ evdev_configure_device(struct evdev_device *device, } } + if (libevdev_has_event_code(evdev, EV_SW, SW_KEYPAD_SLIDE)) { + device->seat_caps |= EVDEV_DEVICE_SWITCH; + device->tags |= EVDEV_TAG_KEYPAD_SLIDE_SWITCH; + } + if (device->seat_caps & EVDEV_DEVICE_SWITCH) evdev_log_info(device, "device is a switch device\n"); } @@ -2670,6 +2675,9 @@ evdev_device_has_switch(struct evdev_device *device, enum libinput_switch sw) case LIBINPUT_SWITCH_TABLET_MODE: code = SW_TABLET_MODE; break; + case LIBINPUT_SWITCH_KEYPAD_SLIDE: + code = SW_KEYPAD_SLIDE; + break; default: return -1; } diff --git a/src/evdev.h b/src/evdev.h index 9263fb6a..e2a6049f 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -80,6 +80,7 @@ enum evdev_device_tags { EVDEV_TAG_TABLET_MODE_SWITCH = bit(8), EVDEV_TAG_TABLET_TOUCHPAD = bit(9), EVDEV_TAG_VIRTUAL = bit(10), + EVDEV_TAG_KEYPAD_SLIDE_SWITCH = bit(11), }; enum evdev_middlebutton_state { diff --git a/src/libinput.h b/src/libinput.h index f9560cad..441f6750 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -736,6 +736,20 @@ enum libinput_switch { * in tablet mode. */ LIBINPUT_SWITCH_TABLET_MODE, + + /** + * This switch indicates if the device keypad is exposed or not. + * + * If the switch is in state @ref LIBINPUT_SWITCH_STATE_OFF, the + * keypad is hidden. If the state is @ref LIBINPUT_SWITCH_STATE_ON, + * the keypad is exposed. + * + * All devices will remain accessible regardless of the state of this + * switch. + * + * @since 1.31 + */ + LIBINPUT_SWITCH_KEYPAD_SLIDE, }; /** diff --git a/src/util-libinput.c b/src/util-libinput.c index 23a38683..0f0a9460 100644 --- a/src/util-libinput.c +++ b/src/util-libinput.c @@ -976,6 +976,9 @@ print_switch_event(struct libinput_event *ev, const struct libinput_print_option case LIBINPUT_SWITCH_TABLET_MODE: which = "tablet-mode"; break; + case LIBINPUT_SWITCH_KEYPAD_SLIDE: + which = "keypad-slide"; + break; default: abort(); } diff --git a/test/litest-device-keypad-slide-switch.c b/test/litest-device-keypad-slide-switch.c new file mode 100644 index 00000000..1b48965f --- /dev/null +++ b/test/litest-device-keypad-slide-switch.c @@ -0,0 +1,56 @@ +/* + * Copyright © 2025 Sicelo A. Mhlongo + * + * 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 "litest-int.h" +#include "litest.h" + +static struct input_id input_id = { + .bustype = 0x19, + .vendor = 0x0, + .product = 0x5, +}; + +/* clang-format off */ +static int events[] = { + EV_SW, SW_KEYPAD_SLIDE, + EV_SW, SW_CAMERA_LENS_COVER, + EV_KEY, KEY_CAMERA_FOCUS, + -1, -1, +}; +/* clang-format on */ + +TEST_DEVICE(LITEST_KEYPAD_SLIDE_SWITCH, + .features = LITEST_SWITCH, + .interface = NULL, + + .name = "Keypad Slide Switch", + .id = &input_id, + .events = events, + .absinfo = NULL, + + .udev_properties = { + { "ID_INPUT_SWITCH", "1" }, + { NULL }, + }, ) diff --git a/test/litest.c b/test/litest.c index 42acaa40..b2d5eaac 100644 --- a/test/litest.c +++ b/test/litest.c @@ -3323,6 +3323,9 @@ litest_switch_action(struct litest_device *dev, case LIBINPUT_SWITCH_TABLET_MODE: code = SW_TABLET_MODE; break; + case LIBINPUT_SWITCH_KEYPAD_SLIDE: + code = SW_KEYPAD_SLIDE; + break; default: litest_abort_msg("Invalid switch %d", sw); break; diff --git a/test/litest.h b/test/litest.h index 9aa76f3c..2f2ebfb0 100644 --- a/test/litest.h +++ b/test/litest.h @@ -499,6 +499,7 @@ enum litest_device_type { /* Switches */ LITEST_LID_SWITCH, LITEST_LID_SWITCH_SURFACE3, + LITEST_KEYPAD_SLIDE_SWITCH, LITEST_TABLET_MODE_UNRELIABLE, /* Special devices */ diff --git a/test/test-misc.c b/test/test-misc.c index c1303e2a..8a9f5450 100644 --- a/test/test-misc.c +++ b/test/test-misc.c @@ -528,6 +528,12 @@ START_TEST(event_conversion_switch) litest_switch_action(dev, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON); litest_switch_action(dev, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_OFF); + litest_switch_action(dev, + LIBINPUT_SWITCH_KEYPAD_SLIDE, + LIBINPUT_SWITCH_STATE_ON); + litest_switch_action(dev, + LIBINPUT_SWITCH_KEYPAD_SLIDE, + LIBINPUT_SWITCH_STATE_OFF); litest_dispatch(li); while ((event = libinput_get_event(li))) { @@ -899,6 +905,7 @@ TEST_COLLECTION(misc) litest_add_for_device(event_conversion_tablet, LITEST_WACOM_CINTIQ_12WX_PEN); litest_add_for_device(event_conversion_tablet_pad, LITEST_WACOM_INTUOS5_PAD); litest_add_for_device(event_conversion_switch, LITEST_LID_SWITCH); + litest_add_for_device(event_conversion_switch, LITEST_KEYPAD_SLIDE_SWITCH); litest_add_deviceless(context_ref_counting); litest_add_deviceless(config_status_string); diff --git a/test/test-switch.c b/test/test-switch.c index d25405ef..f5b666e4 100644 --- a/test/test-switch.c +++ b/test/test-switch.c @@ -105,6 +105,20 @@ START_TEST(switch_has_tablet_mode_switch) } END_TEST +START_TEST(switch_has_keypad_slide_switch) +{ + struct litest_device *dev = litest_current_device(); + + if (!libevdev_has_event_code(dev->evdev, EV_SW, SW_KEYPAD_SLIDE)) + return LITEST_NOT_APPLICABLE; + + litest_assert_int_eq( + libinput_device_switch_has_switch(dev->libinput_device, + LIBINPUT_SWITCH_KEYPAD_SLIDE), + 1); +} +END_TEST + START_TEST(switch_toggle) { struct litest_device *dev = litest_current_device(); @@ -638,6 +652,9 @@ START_TEST(switch_suspend_with_keyboard) case LIBINPUT_SWITCH_TABLET_MODE: sw = litest_add_device(li, LITEST_THINKPAD_EXTRABUTTONS); break; + case LIBINPUT_SWITCH_KEYPAD_SLIDE: + sw = litest_add_device(li, LITEST_KEYPAD_SLIDE_SWITCH); + break; default: abort(); } @@ -1352,13 +1369,20 @@ TEST_COLLECTION(switch) litest_add(switch_has_cap, LITEST_SWITCH, LITEST_ANY); litest_add(switch_has_lid_switch, LITEST_SWITCH, LITEST_ANY); litest_add(switch_has_tablet_mode_switch, LITEST_SWITCH, LITEST_ANY); + litest_add(switch_has_keypad_slide_switch, LITEST_SWITCH, LITEST_ANY); litest_add(switch_not_down_on_init, LITEST_SWITCH, LITEST_ANY); - litest_with_parameters(params, "switch", 'I', 2, litest_named_i32(LIBINPUT_SWITCH_LID, "lid"), - litest_named_i32(LIBINPUT_SWITCH_TABLET_MODE, "tablet_mode")) { + litest_with_parameters(params, "switch", 'I', 3, litest_named_i32(LIBINPUT_SWITCH_LID, "lid"), + litest_named_i32(LIBINPUT_SWITCH_TABLET_MODE, "tablet_mode"), + litest_named_i32(LIBINPUT_SWITCH_KEYPAD_SLIDE, "keypad_slide")) { litest_add_parametrized(switch_toggle, LITEST_SWITCH, LITEST_ANY, params); litest_add_parametrized(switch_toggle_double, LITEST_SWITCH, LITEST_ANY, params); litest_add_parametrized(switch_down_on_init, LITEST_SWITCH, LITEST_ANY, params); + litest_add_parametrized(switch_not_down_on_init, LITEST_SWITCH, LITEST_ANY, params); + } + + litest_with_parameters(params, "switch", 'I', 2, litest_named_i32(LIBINPUT_SWITCH_LID, "lid"), + litest_named_i32(LIBINPUT_SWITCH_TABLET_MODE, "tablet_mode")) { litest_add_parametrized(switch_disable_touchpad, LITEST_SWITCH, LITEST_ANY, params); litest_add_parametrized(switch_disable_touchpad_during_touch, LITEST_SWITCH, LITEST_ANY, params); litest_add_parametrized(switch_disable_touchpad_edge_scroll, LITEST_SWITCH, LITEST_ANY, params);