diff --git a/completion/zsh/_libinput b/completion/zsh/_libinput index 58428b36..df14ab59 100644 --- a/completion/zsh/_libinput +++ b/completion/zsh/_libinput @@ -49,6 +49,7 @@ __all_seats() '--udev=[Listen for notifications on the given seat]:seat:__all_seats' \ '--apply-to=[Apply configuration options where the device name matches the pattern]:pattern' \ '--disable-sendevents=[Disable send-events option for the devices matching the pattern]:pattern' \ + '--set-area=[Set the desired area as "x1/y1 x2/y2" (within [0.0, 1.0]) ]' \ '--set-click-method=[Set the desired click method]:click-method:(none clickfinger buttonareas)' \ '--set-clickfinger-map=[Set button mapping for clickfinger]:tap-map:(( \ lrm\:2-fingers\ right-click\ /\ 3-fingers\ middle-click \ diff --git a/doc/user/configuration.rst b/doc/user/configuration.rst index e9260a3b..3b00767b 100644 --- a/doc/user/configuration.rst +++ b/doc/user/configuration.rst @@ -191,3 +191,16 @@ the given threshold is met, and will reach the maximum logical pressure before the maximum hardware-supported pressure is reached. See :ref:`tablet-pressure-range` for more info. + +------------------------------------------------------------------------------ +Area configuration +------------------------------------------------------------------------------ + +Area configuration is available for some indirect input devices such as +graphics tablets. This configuration allows reducing the active area of +such a device to a subset of the physically possible area. + +An example use-case for this is to match the aspect ratio of the device to that +of the screen. + +See :ref:`tablet-area` for more info. diff --git a/doc/user/meson.build b/doc/user/meson.build index 4609eb65..49ef2d50 100644 --- a/doc/user/meson.build +++ b/doc/user/meson.build @@ -120,6 +120,7 @@ src_rst = files( 'svg/software-buttons-thumbpress.svg', 'svg/software-buttons-visualized.svg', 'svg/swipe-gestures.svg', + 'svg/tablet-area.svg', 'svg/tablet-axes.svg', 'svg/tablet-cintiq24hd-modes.svg', 'svg/tablet-interfaces.svg', diff --git a/doc/user/svg/tablet-area.svg b/doc/user/svg/tablet-area.svg new file mode 100644 index 00000000..b83890f6 --- /dev/null +++ b/doc/user/svg/tablet-area.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + 0/0 + + + + + + + + + + + diff --git a/doc/user/tablet-support.rst b/doc/user/tablet-support.rst index fededac0..5182a496 100644 --- a/doc/user/tablet-support.rst +++ b/doc/user/tablet-support.rst @@ -467,3 +467,31 @@ devices arbitration has to be done in userspace. libinput uses the **libinput_device_group** to decide on touch arbitration and automatically discards touch events whenever a tool is in proximity. The exact behavior is device-dependent. + +.. _tablet-area: + +------------------------------------------------------------------------------ +Tablet area +------------------------------------------------------------------------------ + +External tablet devices such as e.g. the Wacom Intuos series can be configured +to reduce the available logical input area. Typically the logical input area +is equivalent to the physical input area but it can be reduced with the +**libinput_device_config_area_set_rectangle()** call. Once reduced, input +events outside the logical input area are ignored and the logical input area +acts as if it represented the extents of the physical tablet. + +.. figure:: tablet-area.svg + :align: center + + Tablet area configuration example + +In the image above, the area is set to the rectangle 0.25/0.25 to 0.5/0.75. +Even though the tool is roughly at the physical position ``0.5 * width`` and +``0.75 * height``, the return values of +**libinput_event_tablet_tool_get_x_transformed()** and +**libinput_event_tablet_tool_get_y_transformed()** would be close to the +maximum provided in this call. + +The size of the tablet reported by **libinput_device_get_size()** always reflects +the physical area, not the logical area. diff --git a/src/libinput-private.h b/src/libinput-private.h index dd526cb0..6a0267f9 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -228,6 +228,14 @@ struct libinput_device_config_calibration { float matrix[6]); }; +struct libinput_device_config_area { + int (*has_rectangle)(struct libinput_device *device); + enum libinput_config_status (*set_rectangle)(struct libinput_device *device, + const struct libinput_config_area_rectangle *rectangle); + struct libinput_config_area_rectangle (*get_rectangle)(struct libinput_device *device); + struct libinput_config_area_rectangle (*get_default_rectangle)(struct libinput_device *device); +}; + struct libinput_device_config_send_events { uint32_t (*get_modes)(struct libinput_device *device); enum libinput_config_status (*set_mode)(struct libinput_device *device, @@ -391,6 +399,7 @@ struct libinput_device_config_gesture { struct libinput_device_config { struct libinput_device_config_tap *tap; struct libinput_device_config_calibration *calibration; + struct libinput_device_config_area *area; struct libinput_device_config_send_events *sendevents; struct libinput_device_config_accel *accel; struct libinput_device_config_natural_scroll *natural_scroll; diff --git a/src/libinput.c b/src/libinput.c index 9f686345..7bf26c7d 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -4134,6 +4134,45 @@ libinput_device_config_calibration_get_default_matrix(struct libinput_device *de return device->config.calibration->get_default_matrix(device, matrix); } +LIBINPUT_EXPORT int +libinput_device_config_area_has_rectangle(struct libinput_device *device) +{ + return device->config.area ? + device->config.area->has_rectangle(device) : 0; +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_area_set_rectangle(struct libinput_device *device, + const struct libinput_config_area_rectangle *rectangle) +{ + if (!libinput_device_config_area_has_rectangle(device)) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + + return device->config.area->set_rectangle(device, rectangle); +} + +LIBINPUT_EXPORT struct libinput_config_area_rectangle +libinput_device_config_area_get_rectangle(struct libinput_device *device) +{ + struct libinput_config_area_rectangle rect = { 0.0, 0.0, 1.0, 1.0 }; + + if (!libinput_device_config_area_has_rectangle(device)) + return rect; + + return device->config.area->get_rectangle(device); +} + +LIBINPUT_EXPORT struct libinput_config_area_rectangle +libinput_device_config_area_get_default_rectangle(struct libinput_device *device) +{ + struct libinput_config_area_rectangle rect = { 0.0, 0.0, 1.0, 1.0 }; + + if (!libinput_device_config_area_has_rectangle(device)) + return rect; + + return device->config.area->get_default_rectangle(device); +} + LIBINPUT_EXPORT uint32_t libinput_device_config_send_events_get_modes(struct libinput_device *device) { diff --git a/src/libinput.h b/src/libinput.h index aa0f2be3..2e4cfb85 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -2401,6 +2401,10 @@ libinput_event_tablet_tool_wheel_has_changed( * libinput_event_tablet_tool_get_x_transformed() for transforming the axis * value into a different coordinate space. * + * If an area is defined for this device, the coordinate is in mm from + * the top left corner of the area. See + * libinput_device_config_area_set_rectangle() for details. + * * @note On some devices, returned value may be negative or larger than the * width of the device. See the libinput documentation for more details. * @@ -2420,6 +2424,10 @@ libinput_event_tablet_tool_get_x(struct libinput_event_tablet_tool *event); * libinput_event_tablet_tool_get_y_transformed() for transforming the axis * value into a different coordinate space. * + * If an area is defined for this device, the coordinate is in mm from + * the top left corner of the area. See + * libinput_device_config_area_set_rectangle() for details. + * * @note On some devices, returned value may be negative or larger than the * width of the device. See the libinput documentation for more details. * @@ -5128,6 +5136,143 @@ int libinput_device_config_calibration_get_default_matrix(struct libinput_device *device, float matrix[6]); +/** + * @ingroup config + * + * Describes a rectangle to configure a device's area, see + * libinput_device_config_area_set_rectangle(). + * + * This struct describes a rectangle via the upper left points (x1, y1) + * and the lower right point (x2, y2). + * + * All arguments are normalized to the range [0.0, 1.0] to represent the + * corresponding proportion of the device's width and height, respectively. + * A rectangle covering the whole device thus comprises of the points + * (0.0, 0.0) and (1.0, 1.0). + * + * The conditions x1 < x2 and y1 < y2 must be true. + */ +struct libinput_config_area_rectangle { + double x1; + double y1; + double x2; + double y2; +}; + +/** + * @ingroup config + * + * Check if the device can change its logical input area via a rectangle. + * + * @param device The device to check + * @return Non-zero if the device can be calibrated, zero otherwise. + * + * @see libinput_device_config_area_set_rectangle + * @see libinput_device_config_area_get_rectangle + * @see libinput_device_config_area_get_default_rectangle + */ +int +libinput_device_config_area_has_rectangle(struct libinput_device *device); + +/** + * @ingroup config + * + * Set the given rectangle as the logical input area of this device. + * Future interactions by a tablet tool on this devices are scaled + * to only consider events within this logical input area - as if the + * logical input area were the available physical area. + * + * The coordinates of the rectangle represent the proportion of the + * available maximum physical area, normalized to the range [0.0, 1.0]. + * For example, a rectangle with the two points 0.25, 0.5, 0.75, 1.0 + * adds a 25% dead zone to the left and right and a 50% dead zone on + * the top: + * + * @code + * +----------------------------------+ + * | | + * | 50% | + * | | + * | +-----------------+ | + * | | | | + * | 25% | | 25% | + * | | | | + * +--------+-----------------+-------+ + * @endcode + * + * The area applies in the tablet's current logical rotation, i.e. the above + * example is always at the bottom of the tablet. + * + * Once applied, the logical area's top-left coordinate (in the current logical + * rotation) becomes the new offset (0/0) and the return values of + * libinput_event_tablet_tool_get_x() and libinput_event_tablet_tool_get_y() + * are in relation to this new offset. + * + * Likewise, libinput_event_tablet_tool_get_x_transformed() and + * libinput_event_tablet_tool_get_y_transformed() represent the value scaled + * into the configured logical area. + * + * The return value of libinput_device_get_size() is not affected by the + * configured area. + * + * Changing the area may not take effect immediately, the device may wait until + * it is in a neutral state before applying any changes. + * + * @param device The device to check + * @param rect The intended rectangle + * @return A config status code. Setting the area on a device that does not + * support area rectangles always fails with @ref LIBINPUT_CONFIG_STATUS_UNSUPPORTED. + * + * @see libinput_device_config_area_has_rectangle + * @see libinput_device_config_area_get_rectangle + * @see libinput_device_config_area_get_default_rectangle + */ +enum libinput_config_status +libinput_device_config_area_set_rectangle(struct libinput_device *device, + const struct libinput_config_area_rectangle *rect); + +/** + * @ingroup config + * + * Return the current area rectangle for this device. + * + * The return value for a device that does not support area rectangles is a + * rectangle with the points 0/0 and 1/1. + * + * @note It is an application bug to call this function for devices where + * libinput_device_config_area_has_rectangle() returns 0. + * + * @param device The device to check + * @return The current area rectangle + * + * @see libinput_device_config_area_has_rectangle + * @see libinput_device_config_area_set_rectangle + * @see libinput_device_config_area_get_default_rectangle + */ +struct libinput_config_area_rectangle +libinput_device_config_area_get_rectangle(struct libinput_device *device); + +/** + * @ingroup config + * + * Return the default area rectangle for this device. + * + * The return value for a device that does not support area rectangles is a + * rectangle with the points 0/0 and 1/1. + * + * @note It is an application bug to call this function for devices where + * libinput_device_config_area_has_rectangle() returns 0. + * + * @param device The device to check + * @return The default area rectangle + * + * @see libinput_device_config_area_has_rectangle + * @see libinput_device_config_area_set_rectangle + * @see libinput_device_config_area_get_rectangle + */ +struct libinput_config_area_rectangle +libinput_device_config_area_get_default_rectangle(struct libinput_device *device); + /** * @ingroup config * diff --git a/src/libinput.sym b/src/libinput.sym index e8275d50..2f3a7eee 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -350,3 +350,10 @@ LIBINPUT_1.26 { libinput_tablet_tool_config_pressure_range_get_default_minimum; libinput_tablet_tool_config_pressure_range_get_default_maximum; } LIBINPUT_1.23; + +LIBINPUT_1.27 { + libinput_device_config_area_has_rectangle; + libinput_device_config_area_set_rectangle; + libinput_device_config_area_get_rectangle; + libinput_device_config_area_get_default_rectangle; +} LIBINPUT_1.26; diff --git a/tools/libinput-debug-events.man b/tools/libinput-debug-events.man index 15437694..100e39a5 100644 --- a/tools/libinput-debug-events.man +++ b/tools/libinput-debug-events.man @@ -87,6 +87,10 @@ Enable or disable the scroll button lock .B \-\-enable\-tap|\-\-disable\-tap Enable or disable tap-to-click .TP 8 +.B \-\-set\-area="x1/y1 x2/y2" +Set the tablet area to the rectangle described by the two points x1/y1 and x2/y2. All +coordinates must be in the range [0.0, 1.0]. +.TP 8 .B \-\-set\-calibration="1.0 0.0 0.0 0.0 1.0 0.0" Set the first 6 elements of a 3x3 matrix to use for calibration. The third row of the matrix is always "0.0 0.0 0.1". diff --git a/tools/shared.c b/tools/shared.c index 9df277d6..327005ed 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -133,6 +133,10 @@ tools_init_options(struct tools_options *options) options->pressure_range[1] = 1.0; options->calibration[0] = 1.0; options->calibration[4] = 1.0; + options->area.x1 = 0.0; + options->area.y1 = 0.0; + options->area.x2 = 1.0; + options->area.y2 = 1.0; } int @@ -390,6 +394,22 @@ tools_parse_option(int option, options->calibration[i] = matrix[i]; free(matrix); break; + } + case OPT_AREA: { + if (!optarg) + return 1; + + double x1, x2, y1, y2; + + if (sscanf(optarg, "%lf/%lf %lf/%lf", &x1, &y1, &x2, &y2) != 4) { + fprintf(stderr, "Invalid --set-area values\n"); + return 1; + } + options->area.x1 = x1; + options->area.y1 = y1; + options->area.x2 = x2; + options->area.y2 = y2; + break; } } return 0; @@ -606,6 +626,9 @@ tools_device_apply_config(struct libinput_device *device, if (libinput_device_config_calibration_has_matrix(device)) libinput_device_config_calibration_set_matrix(device, options->calibration); + + if (libinput_device_config_area_has_rectangle(device)) + libinput_device_config_area_set_rectangle(device, &options->area); } void diff --git a/tools/shared.h b/tools/shared.h index 922ace06..cadb36e3 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -68,6 +68,7 @@ enum configuration_options { OPT_ROTATION_ANGLE, OPT_PRESSURE_RANGE, OPT_CALIBRATION, + OPT_AREA, }; #define CONFIGURATION_OPTIONS \ @@ -103,7 +104,8 @@ enum configuration_options { { "set-custom-type", required_argument, 0, OPT_CUSTOM_TYPE },\ { "set-rotation-angle", required_argument, 0, OPT_ROTATION_ANGLE }, \ { "set-pressure-range", required_argument, 0, OPT_PRESSURE_RANGE }, \ - { "set-calibration", required_argument, 0, OPT_CALIBRATION } + { "set-calibration", required_argument, 0, OPT_CALIBRATION }, \ + { "set-area", required_argument, 0, OPT_AREA } enum tools_backend { BACKEND_NONE, @@ -138,6 +140,7 @@ struct tools_options { unsigned int angle; double pressure_range[2]; float calibration[6]; + struct libinput_config_area_rectangle area; }; void tools_init_options(struct tools_options *options);