Add TabletTool config options for eraser button and tool pressure

Tested on a XP-Pen Artist Pro Gen 2.

The eraser button mode has some quirks to it such as not being able to
set keyboard buttons but otherwise works well. Krita recognizes
keycode 332 as a right-click action for example.

Pressure range seems to work without issue, a very low max range means
it takes very minimal pressure to reach the maximum. While a very high
min range means you need to put a lot of pressure before the input is
even recognized.

The exact moment these settings are set, happens within the
`CInputManager::ensureTabletToolPresent()` function after a new
TabletTool has been added. The newly created `setTabletToolconfigs()`
function is called to set up the configuration.
This commit is contained in:
Marcello Haddeman 2026-04-16 19:44:17 +02:00
parent 1681bea42d
commit 0e9883b564
7 changed files with 62 additions and 0 deletions

View file

@ -269,6 +269,10 @@
---| "input.tablet.region_size"
---| "input.tablet.relative_input"
---| "input.tablet.transform"
---| "input.tablettool.eraser_button_mode"
---| "input.tablettool.eraser_button_override"
---| "input.tablettool.pressure_range_max"
---| "input.tablettool.pressure_range_min"
---| "input.touchdevice.enabled"
---| "input.touchdevice.output"
---| "input.touchdevice.transform"
@ -1139,6 +1143,10 @@ hl = {}
---@field ['input.tablet.region_size'] HL.Vec2Like
---@field ['input.tablet.relative_input'] boolean
---@field ['input.tablet.transform'] integer|boolean
---@field ['input.tablettool.eraser_button_mode'] integer|boolean
---@field ['input.tablettool.eraser_button_override'] integer|boolean
---@field ['input.tablettool.pressure_range_max'] number|boolean
---@field ['input.tablettool.pressure_range_min'] number|boolean
---@field ['input.touchdevice.enabled'] boolean
---@field ['input.touchdevice.output'] string
---@field ['input.touchdevice.transform'] integer|boolean

View file

@ -556,6 +556,10 @@ CConfigManager::CConfigManager() {
m_config->addSpecialConfigValue("device", "share_states", Hyprlang::INT{0}); // only for virtualkeyboards
m_config->addSpecialConfigValue("device", "release_pressed_on_close", Hyprlang::INT{0}); // only for virtualkeyboards
m_config->addSpecialConfigValue("device", "tags", STRVAL_EMPTY); // only for keyboards and mice
m_config->addSpecialConfigValue("device", "eraser_button_mode", Hyprlang::INT{0}); // only for tablettools
m_config->addSpecialConfigValue("device", "eraser_button_override", Hyprlang::INT{0}); // only for tablettools
m_config->addSpecialConfigValue("device", "pressure_range_min", Hyprlang::FLOAT{-1.0}); // only for tablettools
m_config->addSpecialConfigValue("device", "pressure_range_max", Hyprlang::FLOAT{-1.0}); // only for tablettools
m_config->addSpecialCategory("monitorv2", {.key = "output"});
m_config->addSpecialConfigValue("monitorv2", "disabled", Hyprlang::INT{0});

View file

@ -360,6 +360,25 @@ std::vector<SP<IValue>> Values::getConfigValues() {
MS<Vec2>("input:tablet:active_area_position", "position of the active area in mm", Config::VEC2{},
{.validator = vec2Range(0, 0, 500, 500), .refresh = Supplementary::REFRESH_INPUT_DEVICES}),
/*
* input:tablettool:
*/
MS<Int>("input:tablettool:eraser_button_mode",
"Change the eraser button behavior on the tool. When set to 0, use the default hardware behavior of the tool. "
"When set to 1, the eraser button on the tool sends a button event instead.",
0, {.min = 0, .max = 6, .refresh = Supplementary::REFRESH_INPUT_DEVICES}),
MS<Int>("input:tablettool:eraser_button_override",
"Set a button to be button event when eraser_button_mode is set to 1. Has to be an int, cannot be a string. Must be a valid button (e.g. BTN_STYLUS) "
"excluding fake buttons (e.g. BTN_TOOL_*) and keys (KEY_*). Check wev if you have any doubts regarding the ID. 0 means default.",
0, {.min = 0, .refresh = Supplementary::REFRESH_INPUT_DEVICES}),
MS<Float>("input:tablettool:pressure_range_min",
"Set the minimum pressure range for the tool, a negative number will set the default minimum pressure value. This is usually 0.0",
-1.0, {.min = -1.0, .max = 1.0, .refresh = Supplementary::REFRESH_INPUT_DEVICES}),
MS<Float>("input:tablettool:pressure_range_max",
"Set the maximum pressure range for the tool, a negative number will set the default maximum pressure value. This is usually 1.0",
-1.0, {.min = -1.0, .max = 1.0, .refresh = Supplementary::REFRESH_INPUT_DEVICES}),
/*
* gestures:
*/

View file

@ -163,6 +163,10 @@ namespace Config::Values {
"input:tablet:relative_input",
"input:tablet:active_area_position",
"input:tablet:active_area_size",
"input:tablettool:eraser_button_mode",
"input:tablettool:eraser_button_override",
"input:tablettool:pressure_range_min",
"input:tablettool:pressure_range_max",
"input:touchpad:flip_x",
"input:touchpad:flip_y",
"input:touchpad:drag_3fg",

View file

@ -1952,6 +1952,30 @@ void CInputManager::setTabletConfigs() {
}
}
void CInputManager::setTabletToolConfigs() {
for (auto const& t : m_tabletTools) {
if (t->aq()->getLibinputTool()) {
const auto NAME = t->m_hlName;
const auto LIBINPUTTOOL = t->aq()->getLibinputTool();
if (Config::mgr()->getDeviceInt(NAME, "eraser_button_mode", "input:tablettool:eraser_button_mode") == 1)
libinput_tablet_tool_config_eraser_button_set_mode(LIBINPUTTOOL, LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON);
else
libinput_tablet_tool_config_eraser_button_set_mode(LIBINPUTTOOL, LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT);
const auto ERASER_BUTTON_OVERRIDE = Config::mgr()->getDeviceInt(NAME, "eraser_button_override", "input:tablettool:eraser_button_override");
libinput_tablet_tool_config_eraser_button_set_button(
LIBINPUTTOOL, ERASER_BUTTON_OVERRIDE == 0 ? libinput_tablet_tool_config_eraser_button_get_default_button(LIBINPUTTOOL) : ERASER_BUTTON_OVERRIDE);
const auto PRESSURE_RANGE_MIN = Config::mgr()->getDeviceFloat(NAME, "pressure_range_min", "input:tablettool:pressure_range_min");
const auto PRESSURE_RANGE_MAX = Config::mgr()->getDeviceFloat(NAME, "pressure_range_max", "input:tablettool:pressure_range_max");
libinput_tablet_tool_config_pressure_range_set(
LIBINPUTTOOL, PRESSURE_RANGE_MIN < 0.0 ? libinput_tablet_tool_config_pressure_range_get_default_minimum(LIBINPUTTOOL) : PRESSURE_RANGE_MIN,
PRESSURE_RANGE_MAX < 0.0 ? libinput_tablet_tool_config_pressure_range_get_default_maximum(LIBINPUTTOOL) : PRESSURE_RANGE_MAX);
}
}
}
void CInputManager::newSwitch(SP<Aquamarine::ISwitch> pDevice) {
const auto PNEWDEV = &m_switches.emplace_back();
PNEWDEV->pDevice = pDevice;

View file

@ -127,6 +127,7 @@ class CInputManager {
void setPointerConfigs();
void setTouchDeviceConfigs(SP<ITouch> dev = nullptr);
void setTabletConfigs();
void setTabletToolConfigs();
void updateCapabilities();
void updateKeyboardsLeds(SP<IKeyboard>);

View file

@ -285,6 +285,8 @@ SP<CTabletTool> CInputManager::ensureTabletToolPresent(SP<Aquamarine::ITabletToo
destroyTabletTool(TOOL.lock());
});
setTabletToolConfigs();
return PTOOL;
}