Fix linux pen pressure

This commit is contained in:
luboslenco
2025-09-14 16:40:34 +02:00
parent 03f9fe2251
commit 26b3e7318a
3 changed files with 94 additions and 89 deletions
+87 -83
View File
@@ -33,8 +33,8 @@ static bool saveInitialized = false;
static const char *videoFormats[] = {"ogv", NULL};
static struct timeval start;
static bool mouse_hidden = false;
static int xi_extension = -1;
static void init_pen_device(XDeviceInfo *info, struct x11_pen_device *pen, bool eraser);
bool iron_x11_init();
void iron_display_init() {
@@ -275,10 +275,16 @@ void iron_window_create(iron_window_options_t *win) {
iron_window_set_title(win->title);
if (x11_ctx.pen.id != -1) {
xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.pen.motionClass, 1);
}
if (x11_ctx.eraser.id != -1) {
xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.eraser.motionClass, 1);
XIEventMask mask;
unsigned char mask_data[XIMaskLen(XI_LASTEVENT)];
memset(mask_data, 0, sizeof(mask_data));
mask.deviceid = x11_ctx.pen.id;
mask.mask_len = sizeof(mask_data);
mask.mask = mask_data;
XISetMask(mask.mask, XI_Motion);
XISetMask(mask.mask, XI_ButtonPress);
XISetMask(mask.mask, XI_ButtonRelease);
XISelectEvents(x11_ctx.display, window->window, &mask, 1);
}
gpu_init(win->depth_bits, win->vsync);
@@ -312,9 +318,7 @@ bool iron_internal_call_close_callback() {
if (iron_internal_window_callbacks[0].close_callback != NULL) {
return iron_internal_window_callbacks[0].close_callback(iron_internal_window_callbacks[0].close_data);
}
else {
return true;
}
return true;
}
iron_window_mode_t iron_window_get_mode() {
@@ -464,77 +468,86 @@ bool iron_x11_init() {
xlib.XInternAtoms(x11_ctx.display, atom_names, sizeof atom_names / sizeof atom_names[0], False, (Atom *)&x11_ctx.atoms);
clipboardString = (char *)malloc(clipboardStringSize);
int event, error;
XQueryExtension(x11_ctx.display, "XInputExtension", &xi_extension, &event, &error);
int num_devices;
XIDeviceInfo *devices = XIQueryDevice(x11_ctx.display, XIAllDevices, &num_devices);
x11_ctx.pen.id = -1;
x11_ctx.eraser.id = -1;
int count;
XDeviceInfoPtr devices = (XDeviceInfoPtr)xlib.XListInputDevices(x11_ctx.display, &count);
for (int i = 0; i < count; i++) {
strncpy(buffer, devices[i].name, 1023);
for (int i = 0; i < num_devices; i++) {
XIDeviceInfo *info = &devices[i];
strncpy(buffer, info->name, 1023);
buffer[1023] = 0;
for (int j = 0; buffer[j]; j++) {
buffer[j] = tolower(buffer[j]);
}
if (strstr(buffer, "stylus") || strstr(buffer, "pen") || strstr(buffer, "wacom")) {
init_pen_device(&devices[i], &x11_ctx.pen, false);
}
if (strstr(buffer, "eraser")) {
init_pen_device(&devices[i], &x11_ctx.eraser, true);
}
}
if (devices != NULL) {
xlib.XFreeDeviceList(devices);
bool is_pen = strstr(buffer, "stylus") || strstr(buffer, "pen") || strstr(buffer, "wacom");
bool is_eraser = strstr(buffer, "eraser");
if (!is_pen && !is_eraser) {
continue;
}
int pressure_index = -1;
double max = 0;
for (int j = 0; j < info->num_classes; j++) {
if (info->classes[j]->type == XIValuatorClass) {
XIValuatorClassInfo *val = (XIValuatorClassInfo *)info->classes[j];
if (val->label == XInternAtom(x11_ctx.display, "Abs Pressure", False)) {
pressure_index = val->number;
max = val->max;
break;
}
}
}
if (pressure_index == -1) continue;
struct x11_pen_device *d = is_pen ? &x11_ctx.pen : &x11_ctx.eraser;
d->id = info->deviceid;
d->pressure_index = pressure_index;
d->pressure_max = max;
d->press = is_pen ? iron_internal_pen_trigger_press : iron_internal_eraser_trigger_press;
d->move = is_pen ? iron_internal_pen_trigger_move : iron_internal_eraser_trigger_move;
d->release = is_pen ? iron_internal_pen_trigger_release : iron_internal_eraser_trigger_release;
}
XIFreeDeviceInfo(devices);
return true;
}
static void init_pen_device(XDeviceInfo *info, struct x11_pen_device *pen, bool eraser) {
XDevice *device = xlib.XOpenDevice(x11_ctx.display, info->id);
XAnyClassPtr c = info->inputclassinfo;
for (int j = 0; j < device->num_classes; j++) {
if (c->class == ValuatorClass) {
XValuatorInfo *valuator_info = (XValuatorInfo *)c;
if (valuator_info->num_axes > 2) {
pen->maxPressure = valuator_info->axes[2].max_value;
}
pen->id = info->id;
DeviceMotionNotify(device, pen->motionEvent, pen->motionClass);
if (eraser) {
pen->press = iron_internal_eraser_trigger_press;
pen->move = iron_internal_eraser_trigger_move;
pen->release = iron_internal_eraser_trigger_release;
}
else {
pen->press = iron_internal_pen_trigger_press;
pen->move = iron_internal_pen_trigger_move;
pen->release = iron_internal_pen_trigger_release;
}
return;
}
c = (XAnyClassPtr)((uint8_t *)c + c->length);
}
xlib.XCloseDevice(x11_ctx.display, device);
}
static void check_pen_device(struct iron_x11_window *window, XEvent *event, struct x11_pen_device *pen) {
if (event->type == pen->motionEvent) {
XDeviceMotionEvent *motion = (XDeviceMotionEvent *)(event);
if (motion->deviceid == pen->id) {
float p = (float)motion->axis_data[2] / (float)pen->maxPressure;
if (p > 0 && x11_ctx.pen.current_pressure == 0) {
pen->press(motion->x, motion->y, p);
}
else if (p == 0 && pen->current_pressure > 0) {
pen->release(motion->x, motion->y, p);
}
else if (p > 0) {
pen->move(motion->x, motion->y, p);
}
pen->current_pressure = p;
}
if (!XGetEventData(x11_ctx.display, &event->xcookie)) {
return;
}
XIDeviceEvent *dev_event = (XIDeviceEvent *)event->xcookie.data;
if (dev_event->event != window->window || dev_event->deviceid != pen->id) {
XFreeEventData(x11_ctx.display, &event->xcookie);
return;
}
float p = pen->pressure_current;
if (XIMaskIsSet(dev_event->valuators.mask, pen->pressure_index)) {
double *values = dev_event->valuators.values;
int idx = 0;
for (int i = 0; i < pen->pressure_index; i++) {
if (XIMaskIsSet(dev_event->valuators.mask, i)) idx++;
}
p = values[idx] / pen->pressure_max;
}
if (event->xcookie.evtype == XI_ButtonPress && dev_event->detail == 1) {
pen->press(dev_event->event_x, dev_event->event_y, p);
}
else if (event->xcookie.evtype == XI_ButtonRelease && dev_event->detail == 1) {
pen->release(dev_event->event_x, dev_event->event_y, p);
}
else if (event->xcookie.evtype == XI_Motion && p > 0) {
pen->move(dev_event->event_x, dev_event->event_y, p);
}
pen->pressure_current = p;
XFreeEventData(x11_ctx.display, &event->xcookie);
}
struct iron_x11_window *window_from_window(Window window) {
@@ -706,13 +719,19 @@ static bool _handle_messages() {
while (xlib.XPending(x11_ctx.display)) {
XEvent event;
xlib.XNextEvent(x11_ctx.display, &event);
if (event.type == GenericEvent && event.xcookie.extension == xi_extension) {
check_pen_device(&x11_ctx.windows[0], &event, &x11_ctx.pen);
check_pen_device(&x11_ctx.windows[0], &event, &x11_ctx.eraser);
continue;
}
Window window = event.xclient.window;
struct iron_x11_window *k_window = window_from_window(window);
if (k_window == NULL) {
continue;
}
check_pen_device(k_window, &event, &x11_ctx.pen);
check_pen_device(k_window, &event, &x11_ctx.eraser);
switch (event.type) {
case MappingNotify: {
xlib.XRefreshKeyboardMapping(&event.xmapping);
@@ -1025,10 +1044,7 @@ void iron_load_url(const char *url) {
if (strncmp(url, "http://", sizeof("http://") - 1) == 0 || strncmp(url, "https://", sizeof("https://") - 1) == 0) {
char openUrlCommand[256];
snprintf(openUrlCommand, 256, "xdg-open %s", url);
int err = system(openUrlCommand);
if (err != 0) {
iron_log("Error opening url %s", url);
}
system(openUrlCommand);
}
}
@@ -1136,18 +1152,6 @@ void iron_copy_to_clipboard(const char *text) {
strcpy(clipboardString, text);
}
static int parse_number_at_end_of_line(char *line) {
char *end = &line[strlen(line) - 2];
int num = 0;
int multi = 1;
while (*end >= '0' && *end <= '9') {
num += (*end - '0') * multi;
multi *= 10;
--end;
}
return num;
}
int iron_hardware_threads(void) {
return sysconf(_SC_NPROCESSORS_ONLN);
}