diff --git a/examples/multi-pointer.c b/examples/multi-pointer.c index 920ee0fca..83d6e794d 100644 --- a/examples/multi-pointer.c +++ b/examples/multi-pointer.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -28,32 +29,13 @@ struct sample_state { float default_color[4]; float clear_color[4]; struct wlr_output_layout *layout; - struct wl_list cursors; // sample_cursor::link - struct wl_list pointers; // sample_pointer::link - struct wl_list outputs; // sample_output::link - struct timespec last_frame; + struct wlr_input_mapper *input_mapper; + struct wl_list outputs; // sample_output.link + struct wl_list pointers; // sample_pointers.link struct wl_listener new_output; struct wl_listener new_input; }; -struct sample_cursor { - struct sample_state *sample; - struct wlr_input_device *device; - struct wlr_cursor *cursor; - struct wl_list link; - - struct wl_listener cursor_motion; - struct wl_listener cursor_motion_absolute; - struct wl_listener cursor_button; - struct wl_listener cursor_axis; - struct wl_listener destroy; -}; - -struct sample_pointer { - struct wlr_input_device *device; - struct wl_list link; -}; - struct sample_output { struct sample_state *sample; struct wlr_output *output; @@ -69,25 +51,15 @@ struct sample_keyboard { struct wl_listener destroy; }; -static void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device, - struct sample_state *sample) { - struct sample_output *output; - wlr_log(WLR_INFO, "Configuring cursor %p for device %p", cursor, device); - - // reset mappings - wlr_cursor_map_to_output(cursor, NULL); - wlr_cursor_detach_input_device(cursor, device); - wlr_cursor_map_input_to_output(cursor, device, NULL); - - wlr_cursor_attach_input_device(cursor, device); - - // configure device to output mappings - wl_list_for_each(output, &sample->outputs, link) { - wlr_cursor_map_to_output(cursor, output->output); - - wlr_cursor_map_input_to_output(cursor, device, output->output); - } -} +struct sample_pointer { + struct sample_state *sample; + struct wlr_pointer *wlr_pointer; + struct wlr_cursor *cursor; + struct wl_listener motion; + struct wl_listener motion_absolute; + struct wl_listener destroy; + struct wl_list link; +}; static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *output = wl_container_of(listener, output, frame); @@ -106,43 +78,39 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { wlr_output_commit(wlr_output); } -static void handle_cursor_motion(struct wl_listener *listener, void *data) { - struct sample_cursor *cursor = - wl_container_of(listener, cursor, cursor_motion); +static void pointer_motion_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = wl_container_of(listener, pointer, motion); struct wlr_pointer_motion_event *event = data; - wlr_cursor_move(cursor->cursor, &event->pointer->base, event->delta_x, - event->delta_y); + wlr_cursor_warp(pointer->cursor, pointer->cursor->x + event->delta_x, + pointer->cursor->y + event->delta_y); } -static void handle_cursor_motion_absolute(struct wl_listener *listener, - void *data) { - struct sample_cursor *cursor = - wl_container_of(listener, cursor, cursor_motion_absolute); +static void pointer_motion_absolute_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = + wl_container_of(listener, pointer, motion_absolute); struct wlr_pointer_motion_absolute_event *event = data; - wlr_cursor_warp_absolute(cursor->cursor, &event->pointer->base, event->x, - event->y); + double lx, ly; + wlr_input_mapper_absolute_to_layout(pointer->sample->input_mapper, + &pointer->wlr_pointer->base, event->x, event->y, &lx, &ly); + wlr_cursor_warp(pointer->cursor, lx, ly); } -static void cursor_destroy(struct sample_cursor *cursor) { - wl_list_remove(&cursor->link); - wl_list_remove(&cursor->cursor_motion.link); - wl_list_remove(&cursor->cursor_motion_absolute.link); - wlr_cursor_destroy(cursor->cursor); - free(cursor); +static void pointer_destroy_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = wl_container_of(listener, pointer, destroy); + wlr_cursor_destroy(pointer->cursor); + wl_list_remove(&pointer->link); + wl_list_remove(&pointer->destroy.link); + wl_list_remove(&pointer->motion.link); + wl_list_remove(&pointer->motion_absolute.link); + free(pointer); } static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); - struct sample_state *sample = sample_output->sample; wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); wl_list_remove(&sample_output->link); free(sample_output); - - struct sample_cursor *cursor; - wl_list_for_each(cursor, &sample->cursors, link) { - configure_cursor(cursor->cursor, cursor->device, sample); - } } static void new_output_notify(struct wl_listener *listener, void *data) { @@ -161,19 +129,15 @@ static void new_output_notify(struct wl_listener *listener, void *data) { wlr_output_layout_add_auto(sample->layout, output); - struct sample_cursor *cursor; - wl_list_for_each(cursor, &sample->cursors, link) { - configure_cursor(cursor->cursor, cursor->device, sample); - - struct wlr_xcursor_image *image = sample->xcursor->images[0]; - wlr_cursor_set_image(cursor->cursor, image->buffer, image->width * 4, - image->width, image->height, image->hotspot_x, image->hotspot_y, 0); - - wlr_cursor_warp(cursor->cursor, NULL, cursor->cursor->x, - cursor->cursor->y); - } wl_list_insert(&sample->outputs, &sample_output->link); + struct sample_pointer *pointer; + wl_list_for_each(pointer, &sample->pointers, link) { + struct wlr_xcursor_image *image = sample->xcursor->images[0]; + wlr_cursor_set_image(pointer->cursor, image->buffer, image->width * 4, + image->width, image->height, image->hotspot_x, image->hotspot_y, 0); + } + struct wlr_output_mode *mode = wlr_output_preferred_mode(output); if (mode != NULL) { wlr_output_set_mode(output, mode); @@ -233,31 +197,24 @@ static void new_input_notify(struct wl_listener *listener, void *data) { xkb_context_unref(context); break; case WLR_INPUT_DEVICE_POINTER:; - struct sample_cursor *cursor = calloc(1, sizeof(struct sample_cursor)); struct sample_pointer *pointer = calloc(1, sizeof(struct sample_pointer)); - pointer->device = device; - cursor->sample = sample; - cursor->device = device; + pointer->wlr_pointer = wlr_pointer_from_input_device(device); + pointer->sample = sample; + pointer->cursor = wlr_cursor_create(sample->layout); - cursor->cursor = wlr_cursor_create(); + wl_list_insert(&sample->pointers, &pointer->link); - wlr_cursor_attach_output_layout(cursor->cursor, sample->layout); - - wl_signal_add(&cursor->cursor->events.motion, &cursor->cursor_motion); - cursor->cursor_motion.notify = handle_cursor_motion; - wl_signal_add(&cursor->cursor->events.motion_absolute, - &cursor->cursor_motion_absolute); - cursor->cursor_motion_absolute.notify = handle_cursor_motion_absolute; - - wlr_cursor_attach_input_device(cursor->cursor, device); - configure_cursor(cursor->cursor, device, sample); + wl_signal_add(&device->events.destroy, &pointer->destroy); + pointer->destroy.notify = pointer_destroy_notify; + wl_signal_add(&pointer->wlr_pointer->events.motion, &pointer->motion); + pointer->motion.notify = pointer_motion_notify; + wl_signal_add(&pointer->wlr_pointer->events.motion_absolute, + &pointer->motion_absolute); + pointer->motion_absolute.notify = pointer_motion_absolute_notify; struct wlr_xcursor_image *image = sample->xcursor->images[0]; - wlr_cursor_set_image(cursor->cursor, image->buffer, image->width * 4, + wlr_cursor_set_image(pointer->cursor, image->buffer, image->width * 4, image->width, image->height, image->hotspot_x, image->hotspot_y, 0); - - wl_list_insert(&sample->cursors, &cursor->link); - wl_list_insert(&sample->pointers, &pointer->link); break; default: break; @@ -280,19 +237,16 @@ int main(int argc, char *argv[]) { state.renderer = wlr_renderer_autocreate(wlr); state.allocator = wlr_allocator_autocreate(wlr, state.renderer); - wl_list_init(&state.cursors); - wl_list_init(&state.pointers); wl_list_init(&state.outputs); - state.layout = wlr_output_layout_create(); + wl_list_init(&state.pointers); + wl_signal_add(&wlr->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; - clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - struct wlr_xcursor_theme *theme = wlr_xcursor_theme_load("default", 16); if (!theme) { wlr_log(WLR_ERROR, "Failed to load cursor theme"); @@ -312,16 +266,6 @@ int main(int argc, char *argv[]) { wl_display_run(display); wl_display_destroy(display); - struct sample_cursor *cursor, *tmp_cursor; - wl_list_for_each_safe(cursor, tmp_cursor, &state.cursors, link) { - cursor_destroy(cursor); - } - - struct sample_pointer *pointer, *tmp_pointer; - wl_list_for_each_safe(pointer, tmp_pointer, &state.pointers, link) { - free(pointer); - } - wlr_xcursor_theme_destroy(theme); wlr_output_layout_destroy(state.layout); } diff --git a/examples/pointer.c b/examples/pointer.c index db85b13a7..3a1d2b352 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -24,35 +25,17 @@ struct sample_state { struct wl_display *display; - struct compositor_state *compositor; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_xcursor_manager *xcursor_manager; struct wlr_cursor *cursor; - double cur_x, cur_y; float default_color[4]; float clear_color[4]; struct wlr_output_layout *layout; - struct wl_list devices; - struct timespec last_frame; + struct wlr_input_mapper *input_mapper; struct wl_listener new_output; struct wl_listener new_input; - struct wl_listener cursor_motion; - struct wl_listener cursor_motion_absolute; - struct wl_listener cursor_button; - struct wl_listener cursor_axis; - - struct wl_listener touch_motion; - struct wl_listener touch_up; - struct wl_listener touch_down; - struct wl_listener touch_cancel; - struct wl_list touch_points; - - struct wl_listener tablet_tool_axis; - struct wl_listener tablet_tool_proxmity; - struct wl_listener tablet_tool_tip; - struct wl_listener tablet_tool_button; }; struct touch_point { @@ -75,23 +58,59 @@ struct sample_keyboard { struct wl_listener destroy; }; -static void warp_to_touch(struct sample_state *state, - struct wlr_input_device *dev) { - if (wl_list_empty(&state->touch_points)) { +struct sample_pointer { + struct sample_state *state; + struct wlr_pointer *wlr_pointer; + struct wl_listener motion; + struct wl_listener motion_absolute; + struct wl_listener button; + struct wl_listener axis; + struct wl_listener frame; + struct wl_listener destroy; +}; + +struct sample_touch { + struct sample_state *state; + struct wlr_touch *wlr_touch; + struct wl_list points; + struct wl_listener motion; + struct wl_listener down; + struct wl_listener up; + struct wl_listener destroy; +}; + +struct sample_tablet { + struct sample_state *state; + struct wlr_tablet *wlr_tablet; + double x, y; + struct wl_listener axis; + struct wl_listener destroy; +}; + +static void warp_absolute(struct sample_state *state, + struct wlr_input_device *device, double x, double y) { + double lx, ly; + wlr_input_mapper_absolute_to_layout(state->input_mapper, + device, x, y, &lx, &ly); + wlr_cursor_warp(state->cursor, lx, ly); +} + +static void warp_to_touch(struct sample_touch *touch) { + if (wl_list_empty(&touch->points)) { return; } double x = 0, y = 0; size_t n = 0; struct touch_point *point; - wl_list_for_each(point, &state->touch_points, link) { + wl_list_for_each(point, &touch->points, link) { x += point->x; y += point->y; n++; } x /= n; y /= n; - wlr_cursor_warp_absolute(state->cursor, dev, x, y); + warp_absolute(touch->state, &touch->wlr_touch->base, x, y); } static void output_frame_notify(struct wl_listener *listener, void *data) { @@ -109,32 +128,30 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { wlr_output_commit(wlr_output); } -static void handle_cursor_motion(struct wl_listener *listener, void *data) { - struct sample_state *sample = - wl_container_of(listener, sample, cursor_motion); +static void pointer_motion_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = wl_container_of(listener, pointer, motion); struct wlr_pointer_motion_event *event = data; - wlr_cursor_move(sample->cursor, &event->pointer->base, event->delta_x, - event->delta_y); + + struct sample_state *sample = pointer->state; + + wlr_cursor_warp(sample->cursor, sample->cursor->x + event->delta_x, + sample->cursor->y + event->delta_y); } -static void handle_cursor_motion_absolute(struct wl_listener *listener, - void *data) { - struct sample_state *sample = - wl_container_of(listener, sample, cursor_motion_absolute); +static void pointer_motion_absolute_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = + wl_container_of(listener, pointer, motion_absolute); struct wlr_pointer_motion_absolute_event *event = data; - sample->cur_x = event->x; - sample->cur_y = event->y; - - wlr_cursor_warp_absolute(sample->cursor, &event->pointer->base, - sample->cur_x, sample->cur_y); + warp_absolute(pointer->state, &pointer->wlr_pointer->base, event->x, event->y); } -static void handle_cursor_button(struct wl_listener *listener, void *data) { - struct sample_state *sample = - wl_container_of(listener, sample, cursor_button); +static void pointer_button_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = wl_container_of(listener, pointer, button); struct wlr_pointer_button_event *event = data; + struct sample_state *sample = pointer->state; + float (*color)[4]; if (event->state == WLR_BUTTON_RELEASED) { color = &sample->default_color; @@ -147,11 +164,12 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { } } -static void handle_cursor_axis(struct wl_listener *listener, void *data) { - struct sample_state *sample = - wl_container_of(listener, sample, cursor_axis); +static void pointer_axis_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = wl_container_of(listener, pointer, axis); struct wlr_pointer_axis_event *event = data; + struct sample_state *sample = pointer->state; + for (size_t i = 0; i < 3; ++i) { sample->default_color[i] += event->delta > 0 ? -0.05f : 0.05f; if (sample->default_color[i] > 1.0f) { @@ -166,40 +184,50 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) { sizeof(sample->clear_color)); } -static void handle_touch_up(struct wl_listener *listener, void *data) { - struct sample_state *sample = wl_container_of(listener, sample, touch_up); - struct wlr_touch_up_event *event = data; +static void pointer_destroy_notify(struct wl_listener *listener, void *data) { + struct sample_pointer *pointer = wl_container_of(listener, pointer, destroy); + wl_list_remove(&pointer->destroy.link); + wl_list_remove(&pointer->motion.link); + wl_list_remove(&pointer->motion_absolute.link); + wl_list_remove(&pointer->button.link); + wl_list_remove(&pointer->axis.link); + free(pointer); +} + +static void touch_up_notify(struct wl_listener *listener, void *data) { + struct sample_touch *touch = wl_container_of(listener, touch, up); + struct wlr_touch_up_event *event = data; struct touch_point *point, *tmp; - wl_list_for_each_safe(point, tmp, &sample->touch_points, link) { + wl_list_for_each_safe(point, tmp, &touch->points, link) { if (point->touch_id == event->touch_id) { wl_list_remove(&point->link); break; } } - warp_to_touch(sample, &event->touch->base); + warp_to_touch(touch); } -static void handle_touch_down(struct wl_listener *listener, void *data) { - struct sample_state *sample = wl_container_of(listener, sample, touch_down); - struct wlr_touch_down_event *event = data; +static void touch_down_notify(struct wl_listener *listener, void *data) { + struct sample_touch *touch = wl_container_of(listener, touch, down); + struct wlr_touch_down_event *event = data; + struct touch_point *point = calloc(1, sizeof(struct touch_point)); point->touch_id = event->touch_id; point->x = event->x; point->y = event->y; - wl_list_insert(&sample->touch_points, &point->link); + wl_list_insert(&touch->points, &point->link); - warp_to_touch(sample, &event->touch->base); + warp_to_touch(touch); } -static void handle_touch_motion(struct wl_listener *listener, void *data) { - struct sample_state *sample = - wl_container_of(listener, sample, touch_motion); - struct wlr_touch_motion_event *event = data; +static void touch_motion_notify(struct wl_listener *listener, void *data) { + struct sample_touch *touch = wl_container_of(listener, touch, motion); + struct wlr_touch_motion_event *event = data; struct touch_point *point; - wl_list_for_each(point, &sample->touch_points, link) { + wl_list_for_each(point, &touch->points, link) { if (point->touch_id == event->touch_id) { point->x = event->x; point->y = event->y; @@ -207,22 +235,36 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { } } - warp_to_touch(sample, &event->touch->base); + warp_to_touch(touch); } -static void handle_touch_cancel(struct wl_listener *listener, void *data) { - wlr_log(WLR_DEBUG, "TODO: touch cancel"); +static void touch_destroy_notify(struct wl_listener *listener, void *data) { + struct sample_touch *touch = wl_container_of(listener, touch, destroy); + wl_list_remove(&touch->destroy.link); + wl_list_remove(&touch->up.link); + wl_list_remove(&touch->down.link); + wl_list_remove(&touch->motion.link); + free(touch); } -static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) { - struct sample_state *sample = - wl_container_of(listener, sample, tablet_tool_axis); +static void tablet_axis_notify(struct wl_listener *listener, void *data) { + struct sample_tablet *tablet = wl_container_of(listener, tablet, axis); struct wlr_tablet_tool_axis_event *event = data; - if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) && - (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { - wlr_cursor_warp_absolute(sample->cursor, &event->tablet->base, - event->x, event->y); + + if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) != 0) { + tablet->x = event->x; } + if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y) != 0) { + tablet->y = event->y; + } + warp_absolute(tablet->state, &tablet->wlr_tablet->base, tablet->x, tablet->y); +} + +static void tablet_destroy_notify(struct wl_listener *listener, void *data) { + struct sample_tablet *tablet = wl_container_of(listener, tablet, destroy); + wl_list_remove(&tablet->destroy.link); + wl_list_remove(&tablet->axis.link); + free(tablet); } static void keyboard_key_notify(struct wl_listener *listener, void *data) { @@ -241,6 +283,13 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) { } } +static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { + struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); + wl_list_remove(&keyboard->destroy.link); + wl_list_remove(&keyboard->key.link); + free(keyboard); +} + static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); struct sample_state *sample = sample_output->state; @@ -277,24 +326,49 @@ static void new_output_notify(struct wl_listener *listener, void *data) { wlr_output_commit(output); } - -static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { - struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); - wl_list_remove(&keyboard->destroy.link); - wl_list_remove(&keyboard->key.link); - free(keyboard); -} - static void new_input_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct sample_state *state = wl_container_of(listener, state, new_input); switch (device->type) { - case WLR_INPUT_DEVICE_POINTER: - case WLR_INPUT_DEVICE_TOUCH: - case WLR_INPUT_DEVICE_TABLET_TOOL: - wlr_cursor_attach_input_device(state->cursor, device); + case WLR_INPUT_DEVICE_POINTER:; + struct sample_pointer *pointer = calloc(1, sizeof(struct sample_pointer)); + pointer->wlr_pointer = wlr_pointer_from_input_device(device); + pointer->state = state; + wl_signal_add(&device->events.destroy, &pointer->destroy); + pointer->destroy.notify = pointer_destroy_notify; + wl_signal_add(&pointer->wlr_pointer->events.motion, &pointer->motion); + pointer->motion.notify = pointer_motion_notify; + wl_signal_add(&pointer->wlr_pointer->events.motion_absolute, + &pointer->motion_absolute); + pointer->motion_absolute.notify = pointer_motion_absolute_notify; + wl_signal_add(&pointer->wlr_pointer->events.button, &pointer->button); + pointer->button.notify = pointer_button_notify; + wl_signal_add(&pointer->wlr_pointer->events.axis, &pointer->axis); + pointer->axis.notify = pointer_axis_notify; + break; + case WLR_INPUT_DEVICE_TOUCH:; + struct sample_touch *touch = calloc(1, sizeof(struct sample_touch)); + touch->wlr_touch = wlr_touch_from_input_device(device); + touch->state = state; + wl_list_init(&touch->points); + wl_signal_add(&device->events.destroy, &touch->destroy); + touch->destroy.notify = touch_destroy_notify; + wl_signal_add(&touch->wlr_touch->events.up, &touch->up); + touch->up.notify = touch_up_notify; + wl_signal_add(&touch->wlr_touch->events.down, &touch->down); + touch->down.notify = touch_down_notify; + wl_signal_add(&touch->wlr_touch->events.motion, &touch->motion); + touch->motion.notify = touch_motion_notify; + break; + case WLR_INPUT_DEVICE_TABLET_TOOL:; + struct sample_tablet *tablet = calloc(1, sizeof(struct sample_tablet)); + tablet->wlr_tablet = wlr_tablet_from_input_device(device); + tablet->state = state; + wl_signal_add(&device->events.destroy, &tablet->destroy); + tablet->destroy.notify = tablet_destroy_notify; + wl_signal_add(&tablet->wlr_tablet->events.axis, &tablet->axis); + tablet->axis.notify = tablet_axis_notify; break; - case WLR_INPUT_DEVICE_KEYBOARD:; struct sample_keyboard *keyboard = calloc(1, sizeof(struct sample_keyboard)); keyboard->wlr_keyboard = wlr_keyboard_from_input_device(device); @@ -330,7 +404,7 @@ int main(int argc, char *argv[]) { struct sample_state state = { .default_color = { 0.25f, 0.25f, 0.25f, 1 }, .clear_color = { 0.25f, 0.25f, 0.25f, 1 }, - .display = display + .display = display, }; struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); @@ -341,39 +415,11 @@ int main(int argc, char *argv[]) { state.renderer = wlr_renderer_autocreate(wlr); state.allocator = wlr_allocator_autocreate(wlr, state.renderer); - state.cursor = wlr_cursor_create(); state.layout = wlr_output_layout_create(); - wlr_cursor_attach_output_layout(state.cursor, state.layout); - //wlr_cursor_map_to_region(state.cursor, state.config->cursor.mapped_box); - wl_list_init(&state.devices); - wl_list_init(&state.touch_points); + state.cursor = wlr_cursor_create(state.layout); - // pointer events - wl_signal_add(&state.cursor->events.motion, &state.cursor_motion); - state.cursor_motion.notify = handle_cursor_motion; - - wl_signal_add(&state.cursor->events.motion_absolute, - &state.cursor_motion_absolute); - state.cursor_motion_absolute.notify = handle_cursor_motion_absolute; - - wl_signal_add(&state.cursor->events.button, &state.cursor_button); - state.cursor_button.notify = handle_cursor_button; - - wl_signal_add(&state.cursor->events.axis, &state.cursor_axis); - state.cursor_axis.notify = handle_cursor_axis; - - // touch events - wl_signal_add(&state.cursor->events.touch_up, &state.touch_up); - state.touch_up.notify = handle_touch_up; - - wl_signal_add(&state.cursor->events.touch_down, &state.touch_down); - state.touch_down.notify = handle_touch_down; - - wl_signal_add(&state.cursor->events.touch_motion, &state.touch_motion); - state.touch_motion.notify = handle_touch_motion; - - wl_signal_add(&state.cursor->events.touch_cancel, &state.touch_cancel); - state.touch_cancel.notify = handle_touch_cancel; + state.input_mapper = wlr_input_mapper_create(); + wlr_input_mapper_attach_output_layout(state.input_mapper, state.layout); wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; @@ -381,11 +427,6 @@ int main(int argc, char *argv[]) { wl_signal_add(&wlr->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; - // tool events - wl_signal_add(&state.cursor->events.tablet_tool_axis, - &state.tablet_tool_axis); - state.tablet_tool_axis.notify = handle_tablet_tool_axis; - state.xcursor_manager = wlr_xcursor_manager_create("default", 24); if (!state.xcursor_manager) { wlr_log(WLR_ERROR, "Failed to load left_ptr cursor"); @@ -395,8 +436,6 @@ int main(int argc, char *argv[]) { wlr_xcursor_manager_set_cursor_image(state.xcursor_manager, "left_ptr", state.cursor); - clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 06ae2f1f0..7d6864df6 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -11,129 +11,49 @@ #include #include -#include - -struct wlr_input_device; /** - * wlr_cursor implements the behavior of the "cursor", that is, the image on the - * screen typically moved about with a mouse or so. It provides tracking for - * this in global coordinates, and integrates with struct wlr_output, - * struct wlr_output_layout, and struct wlr_input_device. You can use it to - * abstract multiple input devices over a single cursor, constrain cursor - * movement to the usable area of a struct wlr_output_layout and communicate - * position updates to the hardware cursor, constrain specific input devices to - * specific outputs or regions of the screen, and so on. + * A helper to keep track of a cursor's position in an output layout and + * manage output cursors. + * + * When the output layout is destroyed, the cursor is destroyed as well. */ - -struct wlr_box; -struct wlr_cursor_state; - struct wlr_cursor { - struct wlr_cursor_state *state; double x, y; - /** - * The interpretation of these signals is the responsibility of the - * compositor, but some helpers are provided for your benefit. If you - * receive a relative motion event, for example, you may want to call - * wlr_cursor_move(). If you receive an absolute event, call - * wlr_cursor_warp_absolute(). If you pass an input device into these - * functions, it will apply the region/output constraints associated with - * that device to the resulting cursor motion. If an output layout is - * attached, these functions will constrain the resulting cursor motion to - * within the usable space of the output layout. - * - * Re-broadcasting these signals to, for example, a struct wlr_seat, is also - * your responsibility. - */ + struct wl_list output_cursors; // wlr_cursor_output_cursor.link + struct wlr_output_layout *layout; + struct { - struct wl_signal motion; - struct wl_signal motion_absolute; - struct wl_signal button; - struct wl_signal axis; - struct wl_signal frame; - struct wl_signal swipe_begin; - struct wl_signal swipe_update; - struct wl_signal swipe_end; - struct wl_signal pinch_begin; - struct wl_signal pinch_update; - struct wl_signal pinch_end; - struct wl_signal hold_begin; - struct wl_signal hold_end; - - struct wl_signal touch_up; - struct wl_signal touch_down; - struct wl_signal touch_motion; - struct wl_signal touch_cancel; - struct wl_signal touch_frame; - - struct wl_signal tablet_tool_axis; - struct wl_signal tablet_tool_proximity; - struct wl_signal tablet_tool_tip; - struct wl_signal tablet_tool_button; + struct wl_signal destroy; } events; void *data; + + // private state + + struct wl_listener layout_add; + struct wl_listener layout_destroy; }; -struct wlr_cursor *wlr_cursor_create(void); +struct wlr_cursor_output_cursor { + struct wlr_cursor *cursor; + struct wlr_output_cursor *output_cursor; + struct wl_list link; // wlr_cursor.output_cursors -void wlr_cursor_destroy(struct wlr_cursor *cur); + // private state + + struct wl_listener layout_output_destroy; +}; + +struct wlr_cursor *wlr_cursor_create(struct wlr_output_layout *layout); + +void wlr_cursor_destroy(struct wlr_cursor *cursor); /** - * Warp the cursor to the given x and y in layout coordinates. If x and y are - * out of the layout boundaries or constraints, no warp will happen. - * - * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, - * device mapping constraints will be ignored. - * - * Returns true when the cursor warp was successful. + * Warp the cursor to the given x and y in layout coordinates. */ -bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, - double lx, double ly); - -/** - * Convert absolute 0..1 coordinates to layout coordinates. - * - * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, - * device mapping constraints will be ignored. - */ -void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, - struct wlr_input_device *dev, double x, double y, double *lx, double *ly); - - -/** - * Warp the cursor to the given x and y coordinates. If the given point is out - * of the layout boundaries or constraints, the closest point will be used. - * If one coordinate is NAN, it will be ignored. - * - * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, - * device mapping constraints will be ignored. - */ -void wlr_cursor_warp_closest(struct wlr_cursor *cur, - struct wlr_input_device *dev, double x, double y); - -/** - * Warp the cursor to the given x and y in absolute 0..1 coordinates. If the - * given point is out of the layout boundaries or constraints, the closest point - * will be used. If one coordinate is NAN, it will be ignored. - * - * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, - * device mapping constraints will be ignored. - */ -void wlr_cursor_warp_absolute(struct wlr_cursor *cur, - struct wlr_input_device *dev, double x, double y); - -/** - * Move the cursor in the direction of the given x and y layout coordinates. If - * one coordinate is NAN, it will be ignored. - * - * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, - * device mapping constraints will be ignored. - */ -void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, - double delta_x, double delta_y); +void wlr_cursor_warp(struct wlr_cursor *cursor, double lx, double ly); /** * Set the cursor image. stride is given in bytes. If pixels is NULL, hides the @@ -142,7 +62,7 @@ void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, * If scale isn't zero, the image is only set on outputs having the provided * scale. */ -void wlr_cursor_set_image(struct wlr_cursor *cur, const uint8_t *pixels, +void wlr_cursor_set_image(struct wlr_cursor *cursor, const uint8_t *pixels, int32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y, float scale); @@ -151,56 +71,7 @@ void wlr_cursor_set_image(struct wlr_cursor *cur, const uint8_t *pixels, * image. The surface position is subtracted from the hotspot. A NULL surface * commit hides the cursor. */ -void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface, +void wlr_cursor_set_surface(struct wlr_cursor *cursor, struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y); -/** - * Attaches this input device to this cursor. The input device must be one of: - * - * - WLR_INPUT_DEVICE_POINTER - * - WLR_INPUT_DEVICE_TOUCH - * - WLR_INPUT_DEVICE_TABLET_TOOL - */ -void wlr_cursor_attach_input_device(struct wlr_cursor *cur, - struct wlr_input_device *dev); - -void wlr_cursor_detach_input_device(struct wlr_cursor *cur, - struct wlr_input_device *dev); -/** - * Uses the given layout to establish the boundaries and movement semantics of - * this cursor. Cursors without an output layout allow infinite movement in any - * direction and do not support absolute input events. - */ -void wlr_cursor_attach_output_layout(struct wlr_cursor *cur, - struct wlr_output_layout *l); - -/** - * Attaches this cursor to the given output, which must be among the outputs in - * the current output_layout for this cursor. This call is invalid for a cursor - * without an associated output layout. - */ -void wlr_cursor_map_to_output(struct wlr_cursor *cur, - struct wlr_output *output); - -/** - * Maps all input from a specific input device to a given output. The input - * device must be attached to this cursor and the output must be among the - * outputs in the attached output layout. - */ -void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, - struct wlr_input_device *dev, struct wlr_output *output); - -/** - * Maps this cursor to an arbitrary region on the associated - * struct wlr_output_layout. - */ -void wlr_cursor_map_to_region(struct wlr_cursor *cur, const struct wlr_box *box); - -/** - * Maps inputs from this input device to an arbitrary region on the associated - * struct wlr_output_layout. - */ -void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, - struct wlr_input_device *dev, const struct wlr_box *box); - #endif diff --git a/include/wlr/types/wlr_input_device.h b/include/wlr/types/wlr_input_device.h index 0bbf517bc..9341b7ca6 100644 --- a/include/wlr/types/wlr_input_device.h +++ b/include/wlr/types/wlr_input_device.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_INPUT_DEVICE_H #include +#include enum wlr_button_state { WLR_BUTTON_RELEASED, @@ -34,6 +35,8 @@ struct wlr_input_device { struct wl_signal destroy; } events; + struct wlr_addon_set addons; + void *data; }; diff --git a/include/wlr/types/wlr_input_mapper.h b/include/wlr/types/wlr_input_mapper.h new file mode 100644 index 000000000..16310a0e5 --- /dev/null +++ b/include/wlr/types/wlr_input_mapper.h @@ -0,0 +1,123 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_INPUT_MAPPER_H +#define WLR_TYPES_WLR_INPUT_MAPPER_H + +#include +#include +#include +#include +#include + +struct wlr_input_device; + +struct wlr_input_constraint { + struct wlr_output *output; // NULL if unset + struct wlr_box box; // Empty if unset + + // private state + + struct wl_listener output_destroy; +}; + +/** +* A helper for converting absolute coordinates received from input devices to +* layout-local coordinates and applying coordinate constraints. + * + * The constraints precendence is as follows: + * 1) Device-specific box + * 2) Device-specific output + * 3) Global box + * 4) Global output + * + * If no output layout is attached to the input mapper, all output constraints + * are ignored. + */ +struct wlr_input_mapper { + struct wlr_output_layout *layout; + struct wlr_input_constraint global; + + struct wl_list mappings; // wlr_input_mapping.link + + struct { + struct wl_signal destroy; + } events; + + // private state + + struct wl_listener layout_destroy; +}; + +struct wlr_input_mapping { + struct wlr_input_constraint constraint; + struct wl_list link; // wlr_input_mapper.mappings + + // private state + + struct wlr_addon addon; // wlr_input_device.addons +}; + +struct wlr_input_mapper *wlr_input_mapper_create(void); + +void wlr_input_mapper_destroy(struct wlr_input_mapper *mapper); + +/** + * Attach an output layout to the input mapper. This detaches the previous + * output layout, if any. + * + * layout may be NULL. + */ +void wlr_input_mapper_attach_output_layout(struct wlr_input_mapper *mapper, + struct wlr_output_layout *layout); + +/** + * Convert absolute coordinates in 0..1 range to layout-local coordinates. + * + * If device is not NULL, its constraints are used, if any. + * + * If no matching constraint is found, the absolute coordinates are mapped to + * the entire layout, unless none is attached, in which case lx and ly are set + * to 0. + */ +void wlr_input_mapper_absolute_to_layout(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, double x, double y, + double *lx, double *ly); + +/** + * Get the closest point satisfying constraints from the given point. + * + * If device is not NULL, its constraints are used, if any. + * + * If no matching constraint is found, get the closest point from the layout. + */ +void wlr_input_mapper_closest_point(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, double lx, double ly, + double *closest_lx, double *closest_ly); + +/** + * Map device to output. + * + * If device is NULL, sets the default output constraint. + * If output is NULL, the output constraint is reset. + * + * When the output is destroyed, the output constraint is reset. + */ +void wlr_input_mapper_map_to_output(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, struct wlr_output *output); + +/** + * Map device to box. + * + * If device is NULL, sets the default box constraint. + * If box is empty, the box constraint is reset. + */ +void wlr_input_mapper_map_to_box(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, const struct wlr_box *box); + +#endif diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index af57284b0..5c046fe4c 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -44,13 +45,10 @@ struct tinywl_server { struct wl_listener new_xdg_surface; struct wl_list views; + struct wlr_input_mapper *input_mapper; + struct wlr_cursor *cursor; struct wlr_xcursor_manager *cursor_mgr; - struct wl_listener cursor_motion; - struct wl_listener cursor_motion_absolute; - struct wl_listener cursor_button; - struct wl_listener cursor_axis; - struct wl_listener cursor_frame; struct wlr_seat *seat; struct wl_listener new_input; @@ -101,6 +99,18 @@ struct tinywl_keyboard { struct wl_listener destroy; }; +struct tinywl_pointer { + struct tinywl_server *server; + struct wlr_pointer *wlr_pointer; + + struct wl_listener motion; + struct wl_listener motion_absolute; + struct wl_listener button; + struct wl_listener axis; + struct wl_listener frame; + struct wl_listener destroy; +}; + static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ if (view == NULL) { @@ -142,6 +152,34 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { } } +static struct tinywl_view *desktop_view_at( + struct tinywl_server *server, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + /* This returns the topmost node in the scene at the given layout coords. + * we only care about surface nodes as we are specifically looking for a + * surface in the surface tree of a tinywl_view. */ + struct wlr_scene_node *node = wlr_scene_node_at( + &server->scene->tree.node, lx, ly, sx, sy); + if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { + return NULL; + } + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_buffer(scene_buffer); + if (!scene_surface) { + return NULL; + } + + *surface = scene_surface->surface; + /* Find the node corresponding to the tinywl_view at the root of this + * surface tree, it is the only one for which we set the data field. */ + struct wlr_scene_tree *tree = node->parent; + while (tree != NULL && tree->node.data == NULL) { + tree = tree->node.parent; + } + return tree->node.data; +} + static void keyboard_handle_modifiers( struct wl_listener *listener, void *data) { /* This event is raised when a modifier key, such as shift or alt, is @@ -270,99 +308,6 @@ static void server_new_keyboard(struct tinywl_server *server, wl_list_insert(&server->keyboards, &keyboard->link); } -static void server_new_pointer(struct tinywl_server *server, - struct wlr_input_device *device) { - /* We don't do anything special with pointers. All of our pointer handling - * is proxied through wlr_cursor. On another compositor, you might take this - * opportunity to do libinput configuration on the device to set - * acceleration, etc. */ - wlr_cursor_attach_input_device(server->cursor, device); -} - -static void server_new_input(struct wl_listener *listener, void *data) { - /* This event is raised by the backend when a new input device becomes - * available. */ - struct tinywl_server *server = - wl_container_of(listener, server, new_input); - struct wlr_input_device *device = data; - switch (device->type) { - case WLR_INPUT_DEVICE_KEYBOARD: - server_new_keyboard(server, device); - break; - case WLR_INPUT_DEVICE_POINTER: - server_new_pointer(server, device); - break; - default: - break; - } - /* We need to let the wlr_seat know what our capabilities are, which is - * communiciated to the client. In TinyWL we always have a cursor, even if - * there are no pointer devices, so we always include that capability. */ - uint32_t caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&server->keyboards)) { - caps |= WL_SEAT_CAPABILITY_KEYBOARD; - } - wlr_seat_set_capabilities(server->seat, caps); -} - -static void seat_request_cursor(struct wl_listener *listener, void *data) { - struct tinywl_server *server = wl_container_of( - listener, server, request_cursor); - /* This event is raised by the seat when a client provides a cursor image */ - struct wlr_seat_pointer_request_set_cursor_event *event = data; - struct wlr_seat_client *focused_client = - server->seat->pointer_state.focused_client; - /* This can be sent by any client, so we check to make sure this one is - * actually has pointer focus first. */ - if (focused_client == event->seat_client) { - /* Once we've vetted the client, we can tell the cursor to use the - * provided surface as the cursor image. It will set the hardware cursor - * on the output that it's currently on and continue to do so as the - * cursor moves between outputs. */ - wlr_cursor_set_surface(server->cursor, event->surface, - event->hotspot_x, event->hotspot_y); - } -} - -static void seat_request_set_selection(struct wl_listener *listener, void *data) { - /* This event is raised by the seat when a client wants to set the selection, - * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in tinywl we always honor - */ - struct tinywl_server *server = wl_container_of( - listener, server, request_set_selection); - struct wlr_seat_request_set_selection_event *event = data; - wlr_seat_set_selection(server->seat, event->source, event->serial); -} - -static struct tinywl_view *desktop_view_at( - struct tinywl_server *server, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - /* This returns the topmost node in the scene at the given layout coords. - * we only care about surface nodes as we are specifically looking for a - * surface in the surface tree of a tinywl_view. */ - struct wlr_scene_node *node = wlr_scene_node_at( - &server->scene->tree.node, lx, ly, sx, sy); - if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { - return NULL; - } - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_buffer(scene_buffer); - if (!scene_surface) { - return NULL; - } - - *surface = scene_surface->surface; - /* Find the node corresponding to the tinywl_view at the root of this - * surface tree, it is the only one for which we set the data field. */ - struct wlr_scene_tree *tree = node->parent; - while (tree != NULL && tree->node.data == NULL) { - tree = tree->node.parent; - } - return tree->node.data; -} - static void reset_cursor_mode(struct tinywl_server *server) { /* Reset the cursor mode to passthrough. */ server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; @@ -429,7 +374,13 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height); } -static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { +static void process_cursor_motion(struct tinywl_server *server, + struct wlr_input_device *device, uint32_t time, double lx, double ly) { + /* Move the cursor to the closest point in the output layout, ensuring that + * it doesn't go outside of it. */ + wlr_input_mapper_closest_point(server->input_mapper, device, lx, ly, &lx, &ly); + wlr_cursor_warp(server->cursor, lx, ly); + /* If the mode is non-passthrough, delegate to those functions. */ if (server->cursor_mode == TINYWL_CURSOR_MOVE) { process_cursor_move(server, time); @@ -443,8 +394,7 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { double sx, sy; struct wlr_seat *seat = server->seat; struct wlr_surface *surface = NULL; - struct tinywl_view *view = desktop_view_at(server, - server->cursor->x, server->cursor->y, &surface, &sx, &sy); + struct tinywl_view *view = desktop_view_at(server, lx, ly, &surface, &sx, &sy); if (!view) { /* If there's no view under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it @@ -473,51 +423,56 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { } } -static void server_cursor_motion(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits a _relative_ - * pointer motion event (i.e. a delta) */ - struct tinywl_server *server = - wl_container_of(listener, server, cursor_motion); +static void pointer_handle_motion(struct wl_listener *listener, void *data) { + /* This event is raised on _relative_ pointer motion (i.e. a delta). */ + struct tinywl_pointer *pointer = wl_container_of(listener, pointer, motion); struct wlr_pointer_motion_event *event = data; - /* The cursor doesn't move unless we tell it to. The cursor automatically - * handles constraining the motion to the output layout, as well as any - * special configuration applied for the specific input device which - * generated the event. You can pass NULL for the device if you want to move - * the cursor around without any input. */ - wlr_cursor_move(server->cursor, &event->pointer->base, - event->delta_x, event->delta_y); - process_cursor_motion(server, event->time_msec); + + struct tinywl_server *server = pointer->server; + + double lx = server->cursor->x + event->delta_x; + double ly = server->cursor->y + event->delta_y; + process_cursor_motion(server, &pointer->wlr_pointer->base, + event->time_msec, lx, ly); } -static void server_cursor_motion_absolute( +static void pointer_handle_motion_absolute( struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an _absolute_ - * motion event, from 0..1 on each axis. This happens, for example, when - * wlroots is running under a Wayland window rather than KMS+DRM, and you - * move the mouse over the window. You could enter the window from any edge, - * so we have to warp the mouse there. There is also some hardware which - * emits these events. */ - struct tinywl_server *server = - wl_container_of(listener, server, cursor_motion_absolute); + /* This event is raised on _absolute_ pointer motion, in 0..1 range on + * each axis. This happens, for example, when wlroots is running under + * a Wayland window rather than KMS+DRM, and you move the mouse over + * the window. You could enter the window from any edge, so we have to warp + * the mouse there. There is also some hardware which emits these events. */ + struct tinywl_pointer *pointer = + wl_container_of(listener, pointer, motion_absolute); struct wlr_pointer_motion_absolute_event *event = data; - wlr_cursor_warp_absolute(server->cursor, &event->pointer->base, event->x, - event->y); - process_cursor_motion(server, event->time_msec); + + struct tinywl_server *server = pointer->server; + + /* Convert absolute coordinates in 0..1 range to layout-local + * coordinates. */ + struct wlr_input_device *device = &pointer->wlr_pointer->base; + double lx, ly; + wlr_input_mapper_absolute_to_layout(server->input_mapper, + device, event->x, event->y, &lx, &ly); + + process_cursor_motion(server, device, event->time_msec, lx, ly); } -static void server_cursor_button(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits a button - * event. */ - struct tinywl_server *server = - wl_container_of(listener, server, cursor_button); +static void pointer_handle_button(struct wl_listener *listener, void *data) { + /* This event is raised a button is pressed or released. */ + struct tinywl_pointer *pointer = wl_container_of(listener, pointer, button); struct wlr_pointer_button_event *event = data; + + struct tinywl_server *server = pointer->server; + /* Notify the client with pointer focus that a button press has occurred */ wlr_seat_pointer_notify_button(server->seat, - event->time_msec, event->button, event->state); + event->time_msec, event->button, event->state); double sx, sy; struct wlr_surface *surface = NULL; struct tinywl_view *view = desktop_view_at(server, - server->cursor->x, server->cursor->y, &surface, &sx, &sy); + server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ reset_cursor_mode(server); @@ -527,27 +482,120 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { } } -static void server_cursor_axis(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an axis event, - * for example when you move the scroll wheel. */ - struct tinywl_server *server = - wl_container_of(listener, server, cursor_axis); +static void pointer_handle_axis(struct wl_listener *listener, void *data) { + /* This event is raised, for example, when you roll the scroll wheel. */ + struct tinywl_pointer *pointer = wl_container_of(listener, pointer, axis); struct wlr_pointer_axis_event *event = data; /* Notify the client with pointer focus of the axis event. */ - wlr_seat_pointer_notify_axis(server->seat, - event->time_msec, event->orientation, event->delta, - event->delta_discrete, event->source); + wlr_seat_pointer_notify_axis(pointer->server->seat, + event->time_msec, event->orientation, event->delta, + event->delta_discrete, event->source); } -static void server_cursor_frame(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an frame - * event. Frame events are sent after regular pointer events to group - * multiple events together. For instance, two axis events may happen at the - * same time, in which case a frame event won't be sent in between. */ - struct tinywl_server *server = - wl_container_of(listener, server, cursor_frame); +static void pointer_handle_frame(struct wl_listener *listener, void *data) { + /* This event is sent after regular pointer events to group multiple + * events together. For instance, two axis events may happen at the same + * time, in which case a frame event won't be sent in between. */ + struct tinywl_pointer *pointer = wl_container_of(listener, pointer, frame); /* Notify the client with pointer focus of the frame event. */ - wlr_seat_pointer_notify_frame(server->seat); + wlr_seat_pointer_notify_frame(pointer->server->seat); +} + +static void pointer_handle_destroy(struct wl_listener *listener, void *data) { + /* This event is raised by the pointer base wlr_input_device to signal + * the destruction of the wlr_pointer. It will no longer receive events + * and should be destroyed. + */ + struct tinywl_pointer *pointer = + wl_container_of(listener, pointer, destroy); + wl_list_remove(&pointer->motion.link); + wl_list_remove(&pointer->motion_absolute.link); + wl_list_remove(&pointer->button.link); + wl_list_remove(&pointer->axis.link); + wl_list_remove(&pointer->frame.link); + wl_list_remove(&pointer->destroy.link); + free(pointer); +} + +static void server_new_pointer(struct tinywl_server *server, + struct wlr_input_device *device) { + struct wlr_pointer *wlr_pointer = wlr_pointer_from_input_device(device); + + struct tinywl_pointer *pointer = + calloc(1, sizeof(struct tinywl_pointer)); + pointer->server = server; + pointer->wlr_pointer = wlr_pointer; + + /* Here we set up listeners for pointer events. */ + pointer->motion.notify = pointer_handle_motion; + wl_signal_add(&wlr_pointer->events.motion, &pointer->motion); + pointer->motion_absolute.notify = pointer_handle_motion_absolute; + wl_signal_add(&wlr_pointer->events.motion_absolute, + &pointer->motion_absolute); + pointer->button.notify = pointer_handle_button; + wl_signal_add(&wlr_pointer->events.button, &pointer->button); + pointer->axis.notify = pointer_handle_axis; + wl_signal_add(&wlr_pointer->events.axis, &pointer->axis); + pointer->frame.notify = pointer_handle_frame; + wl_signal_add(&wlr_pointer->events.frame, &pointer->frame); + pointer->destroy.notify = pointer_handle_destroy; + wl_signal_add(&device->events.destroy, &pointer->destroy); +} + +static void server_new_input(struct wl_listener *listener, void *data) { + /* This event is raised by the backend when a new input device becomes + * available. */ + struct tinywl_server *server = + wl_container_of(listener, server, new_input); + struct wlr_input_device *device = data; + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + server_new_keyboard(server, device); + break; + case WLR_INPUT_DEVICE_POINTER: + server_new_pointer(server, device); + break; + default: + break; + } + /* We need to let the wlr_seat know what our capabilities are, which is + * communiciated to the client. In TinyWL we always have a cursor, even if + * there are no pointer devices, so we always include that capability. */ + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&server->keyboards)) { + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + } + wlr_seat_set_capabilities(server->seat, caps); +} + +static void seat_request_cursor(struct wl_listener *listener, void *data) { + struct tinywl_server *server = wl_container_of( + listener, server, request_cursor); + /* This event is raised by the seat when a client provides a cursor image */ + struct wlr_seat_pointer_request_set_cursor_event *event = data; + struct wlr_seat_client *focused_client = + server->seat->pointer_state.focused_client; + /* This can be sent by any client, so we check to make sure this one is + * actually has pointer focus first. */ + if (focused_client == event->seat_client) { + /* Once we've vetted the client, we can tell the cursor to use the + * provided surface as the cursor image. It will set the hardware cursor + * on the output that it's currently on and continue to do so as the + * cursor moves between outputs. */ + wlr_cursor_set_surface(server->cursor, event->surface, + event->hotspot_x, event->hotspot_y); + } +} + +static void seat_request_set_selection(struct wl_listener *listener, void *data) { + /* This event is raised by the seat when a client wants to set the selection, + * usually when the user copies something. wlroots allows compositors to + * ignore such requests if they so choose, but in tinywl we always honor + */ + struct tinywl_server *server = wl_container_of( + listener, server, request_set_selection); + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(server->seat, event->source, event->serial); } static void output_frame(struct wl_listener *listener, void *data) { @@ -913,12 +961,18 @@ int main(int argc, char *argv[]) { wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface); + /* Create an input mapper, which is a wlroots utility for converting + * absolute coordinates received from input devices to layout-local + * coordinates and applying coordinate constraints. */ + server.input_mapper = wlr_input_mapper_create(); + wlr_input_mapper_attach_output_layout(server.input_mapper, + server.output_layout); + /* * Creates a cursor, which is a wlroots utility for tracking the cursor * image shown on screen. */ - server.cursor = wlr_cursor_create(); - wlr_cursor_attach_output_layout(server.cursor, server.output_layout); + server.cursor = wlr_cursor_create(server.output_layout); /* Creates an xcursor manager, another wlroots utility which loads up * Xcursor themes to source cursor images from and makes sure that cursor @@ -927,30 +981,7 @@ int main(int argc, char *argv[]) { server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); wlr_xcursor_manager_load(server.cursor_mgr, 1); - /* - * wlr_cursor *only* displays an image on screen. It does not move around - * when the pointer moves. However, we can attach input devices to it, and - * it will generate aggregate events for all of them. In these events, we - * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in my - * input handling blog post: - * - * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html - * - * And more comments are sprinkled throughout the notify functions above. - */ server.cursor_mode = TINYWL_CURSOR_PASSTHROUGH; - server.cursor_motion.notify = server_cursor_motion; - wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); - server.cursor_motion_absolute.notify = server_cursor_motion_absolute; - wl_signal_add(&server.cursor->events.motion_absolute, - &server.cursor_motion_absolute); - server.cursor_button.notify = server_cursor_button; - wl_signal_add(&server.cursor->events.button, &server.cursor_button); - server.cursor_axis.notify = server_cursor_axis; - wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); - server.cursor_frame.notify = server_cursor_frame; - wl_signal_add(&server.cursor->events.frame, &server.cursor_frame); /* * Configures a seat, which is a single "seat" at which a user sits and diff --git a/types/meson.build b/types/meson.build index 5bc217ebb..6eafdeb9a 100644 --- a/types/meson.build +++ b/types/meson.build @@ -48,6 +48,7 @@ wlr_files += files( 'wlr_idle_notify_v1.c', 'wlr_input_device.c', 'wlr_input_inhibitor.c', + 'wlr_input_mapper.c', 'wlr_input_method_v2.c', 'wlr_keyboard.c', 'wlr_keyboard_group.c', diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 46a196df3..6cf1ae3c0 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -1,363 +1,121 @@ #include -#include #include #include #include #include -#include #include #include -#include -#include -#include -#include #include -struct wlr_cursor_device { - struct wlr_cursor *cursor; - struct wlr_input_device *device; - struct wl_list link; - struct wlr_output *mapped_output; - struct wlr_box mapped_box; // empty if unset - - struct wl_listener motion; - struct wl_listener motion_absolute; - struct wl_listener button; - struct wl_listener axis; - struct wl_listener frame; - struct wl_listener swipe_begin; - struct wl_listener swipe_update; - struct wl_listener swipe_end; - struct wl_listener pinch_begin; - struct wl_listener pinch_update; - struct wl_listener pinch_end; - struct wl_listener hold_begin; - struct wl_listener hold_end; - - struct wl_listener touch_down; - struct wl_listener touch_up; - struct wl_listener touch_motion; - struct wl_listener touch_cancel; - struct wl_listener touch_frame; - - struct wl_listener tablet_tool_axis; - struct wl_listener tablet_tool_proximity; - struct wl_listener tablet_tool_tip; - struct wl_listener tablet_tool_button; - - struct wl_listener destroy; -}; - -struct wlr_cursor_output_cursor { - struct wlr_cursor *cursor; - struct wlr_output_cursor *output_cursor; - struct wl_list link; - - struct wl_listener layout_output_destroy; -}; - -struct wlr_cursor_state { - struct wlr_cursor *cursor; - struct wl_list devices; // wlr_cursor_device::link - struct wl_list output_cursors; // wlr_cursor_output_cursor::link - struct wlr_output_layout *layout; - struct wlr_output *mapped_output; - struct wlr_box mapped_box; // empty if unset - - struct wl_listener layout_add; - struct wl_listener layout_change; - struct wl_listener layout_destroy; -}; - -struct wlr_cursor *wlr_cursor_create(void) { - struct wlr_cursor *cur = calloc(1, sizeof(struct wlr_cursor)); - if (!cur) { - wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor"); - return NULL; - } - - cur->state = calloc(1, sizeof(struct wlr_cursor_state)); - if (!cur->state) { - wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_state"); - free(cur); - return NULL; - } - - cur->state->cursor = cur; - cur->state->mapped_output = NULL; - - wl_list_init(&cur->state->devices); - wl_list_init(&cur->state->output_cursors); - - // pointer signals - wl_signal_init(&cur->events.motion); - wl_signal_init(&cur->events.motion_absolute); - wl_signal_init(&cur->events.button); - wl_signal_init(&cur->events.axis); - wl_signal_init(&cur->events.frame); - wl_signal_init(&cur->events.swipe_begin); - wl_signal_init(&cur->events.swipe_update); - wl_signal_init(&cur->events.swipe_end); - wl_signal_init(&cur->events.pinch_begin); - wl_signal_init(&cur->events.pinch_update); - wl_signal_init(&cur->events.pinch_end); - wl_signal_init(&cur->events.hold_begin); - wl_signal_init(&cur->events.hold_end); - - // touch signals - wl_signal_init(&cur->events.touch_up); - wl_signal_init(&cur->events.touch_down); - wl_signal_init(&cur->events.touch_motion); - wl_signal_init(&cur->events.touch_cancel); - wl_signal_init(&cur->events.touch_frame); - - // tablet tool signals - wl_signal_init(&cur->events.tablet_tool_tip); - wl_signal_init(&cur->events.tablet_tool_axis); - wl_signal_init(&cur->events.tablet_tool_button); - wl_signal_init(&cur->events.tablet_tool_proximity); - - cur->x = 100; - cur->y = 100; - - return cur; -} - -static void output_cursor_destroy( - struct wlr_cursor_output_cursor *output_cursor) { +static void output_cursor_destroy(struct wlr_cursor_output_cursor *output_cursor) { wl_list_remove(&output_cursor->layout_output_destroy.link); wl_list_remove(&output_cursor->link); wlr_output_cursor_destroy(output_cursor->output_cursor); free(output_cursor); } -static void cursor_detach_output_layout(struct wlr_cursor *cur) { - if (!cur->state->layout) { +static void handle_layout_destroy(struct wl_listener *listener, void *data) { + struct wlr_cursor *cursor = wl_container_of(listener, cursor, layout_destroy); + wlr_cursor_destroy(cursor); +} + +static void handle_layout_output_destroy(struct wl_listener *listener, void *data) { + struct wlr_cursor_output_cursor *output_cursor = + wl_container_of(listener, output_cursor, layout_output_destroy); + output_cursor_destroy(output_cursor); +} + +static void layout_add(struct wlr_cursor *cursor, struct wlr_output_layout_output *l_output) { + struct wlr_cursor_output_cursor *output_cursor = calloc(1, sizeof(*output_cursor)); + if (output_cursor == NULL) { + wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_output_cursor"); + return; + } + output_cursor->cursor = cursor; + + output_cursor->output_cursor = wlr_output_cursor_create(l_output->output); + if (output_cursor->output_cursor == NULL) { + wlr_log(WLR_ERROR, "Failed to allocate wlr_output_cursor"); + free(output_cursor); return; } + output_cursor->layout_output_destroy.notify = handle_layout_output_destroy; + wl_signal_add(&l_output->events.destroy, &output_cursor->layout_output_destroy); + + wl_list_insert(&cursor->output_cursors, &output_cursor->link); +} + +static void handle_layout_add(struct wl_listener *listener, void *data) { + struct wlr_cursor *cursor = wl_container_of(listener, cursor, layout_add); + struct wlr_output_layout_output *l_output = data; + layout_add(cursor, l_output); +} + +struct wlr_cursor *wlr_cursor_create(struct wlr_output_layout *layout) { + struct wlr_cursor *cursor = calloc(1, sizeof(*cursor)); + if (!cursor) { + wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor"); + return NULL; + } + + wl_list_init(&cursor->output_cursors); + + wl_signal_init(&cursor->events.destroy); + + wl_signal_add(&layout->events.add, &cursor->layout_add); + cursor->layout_add.notify = handle_layout_add; + wl_signal_add(&layout->events.destroy, &cursor->layout_destroy); + cursor->layout_destroy.notify = handle_layout_destroy; + + cursor->layout = layout; + + struct wlr_output_layout_output *l_output; + wl_list_for_each(l_output, &layout->outputs, link) { + layout_add(cursor, l_output); + } + + return cursor; +} + +void wlr_cursor_destroy(struct wlr_cursor *cursor) { + wl_signal_emit_mutable(&cursor->events.destroy, NULL); + struct wlr_cursor_output_cursor *output_cursor, *tmp; - wl_list_for_each_safe(output_cursor, tmp, &cur->state->output_cursors, - link) { + wl_list_for_each_safe(output_cursor, tmp, &cursor->output_cursors, link) { output_cursor_destroy(output_cursor); } - wl_list_remove(&cur->state->layout_destroy.link); - wl_list_remove(&cur->state->layout_change.link); - wl_list_remove(&cur->state->layout_add.link); + wl_list_remove(&cursor->layout_destroy.link); + wl_list_remove(&cursor->layout_add.link); - cur->state->layout = NULL; + free(cursor); } -static void cursor_device_destroy(struct wlr_cursor_device *c_device) { - struct wlr_input_device *dev = c_device->device; - if (dev->type == WLR_INPUT_DEVICE_POINTER) { - wl_list_remove(&c_device->motion.link); - wl_list_remove(&c_device->motion_absolute.link); - wl_list_remove(&c_device->button.link); - wl_list_remove(&c_device->axis.link); - wl_list_remove(&c_device->frame.link); - wl_list_remove(&c_device->swipe_begin.link); - wl_list_remove(&c_device->swipe_update.link); - wl_list_remove(&c_device->swipe_end.link); - wl_list_remove(&c_device->pinch_begin.link); - wl_list_remove(&c_device->pinch_update.link); - wl_list_remove(&c_device->pinch_end.link); - wl_list_remove(&c_device->hold_begin.link); - wl_list_remove(&c_device->hold_end.link); - } else if (dev->type == WLR_INPUT_DEVICE_TOUCH) { - wl_list_remove(&c_device->touch_down.link); - wl_list_remove(&c_device->touch_up.link); - wl_list_remove(&c_device->touch_motion.link); - wl_list_remove(&c_device->touch_cancel.link); - wl_list_remove(&c_device->touch_frame.link); - } else if (dev->type == WLR_INPUT_DEVICE_TABLET_TOOL) { - wl_list_remove(&c_device->tablet_tool_axis.link); - wl_list_remove(&c_device->tablet_tool_proximity.link); - wl_list_remove(&c_device->tablet_tool_tip.link); - wl_list_remove(&c_device->tablet_tool_button.link); - } - - wl_list_remove(&c_device->link); - wl_list_remove(&c_device->destroy.link); - free(c_device); -} - -void wlr_cursor_destroy(struct wlr_cursor *cur) { - cursor_detach_output_layout(cur); - - struct wlr_cursor_device *device, *device_tmp = NULL; - wl_list_for_each_safe(device, device_tmp, &cur->state->devices, link) { - cursor_device_destroy(device); - } - - free(cur->state); - free(cur); -} - -static struct wlr_cursor_device *get_cursor_device(struct wlr_cursor *cur, - struct wlr_input_device *device) { - struct wlr_cursor_device *c_device, *ret = NULL; - wl_list_for_each(c_device, &cur->state->devices, link) { - if (c_device->device == device) { - ret = c_device; - break; - } - } - - return ret; -} - -static void cursor_warp_unchecked(struct wlr_cursor *cur, - double lx, double ly) { - assert(cur->state->layout); +void wlr_cursor_warp(struct wlr_cursor *cursor, double lx, double ly) { if (!isfinite(lx) || !isfinite(ly)) { assert(false); return; } struct wlr_cursor_output_cursor *output_cursor; - wl_list_for_each(output_cursor, &cur->state->output_cursors, link) { + wl_list_for_each(output_cursor, &cursor->output_cursors, link) { double output_x = lx, output_y = ly; - wlr_output_layout_output_coords(cur->state->layout, + wlr_output_layout_output_coords(cursor->layout, output_cursor->output_cursor->output, &output_x, &output_y); wlr_output_cursor_move(output_cursor->output_cursor, output_x, output_y); } - cur->x = lx; - cur->y = ly; + cursor->x = lx; + cursor->y = ly; } -/** - * Get the most specific mapping box for the device in this order: - * - * 1. device geometry mapping - * 2. device output mapping - * 3. cursor geometry mapping - * 4. cursor output mapping - * - * Absolute movement for touch and pen devices will be relative to this box and - * pointer movement will be constrained to this box. - * - * If none of these are set, the box is empty and absolute movement should be - * relative to the extents of the layout. - */ -static void get_mapping(struct wlr_cursor *cur, - struct wlr_input_device *dev, struct wlr_box *box) { - assert(cur->state->layout); - - memset(box, 0, sizeof(*box)); - - struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); - if (c_device) { - if (!wlr_box_empty(&c_device->mapped_box)) { - *box = c_device->mapped_box; - return; - } - if (c_device->mapped_output) { - wlr_output_layout_get_box(cur->state->layout, - c_device->mapped_output, box); - return; - } - } - - if (!wlr_box_empty(&cur->state->mapped_box)) { - *box = cur->state->mapped_box; - return; - } - if (cur->state->mapped_output) { - wlr_output_layout_get_box(cur->state->layout, - cur->state->mapped_output, box); - return; - } -} - -bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, - double lx, double ly) { - assert(cur->state->layout); - - bool result = false; - struct wlr_box mapping; - get_mapping(cur, dev, &mapping); - if (!wlr_box_empty(&mapping)) { - result = wlr_box_contains_point(&mapping, lx, ly); - } else { - result = wlr_output_layout_contains_point(cur->state->layout, NULL, - lx, ly); - } - - if (result) { - cursor_warp_unchecked(cur, lx, ly); - } - - return result; -} - -void wlr_cursor_warp_closest(struct wlr_cursor *cur, - struct wlr_input_device *dev, double lx, double ly) { - struct wlr_box mapping; - get_mapping(cur, dev, &mapping); - if (!wlr_box_empty(&mapping)) { - wlr_box_closest_point(&mapping, lx, ly, &lx, &ly); - } else if (!wl_list_empty(&cur->state->layout->outputs)) { - wlr_output_layout_closest_point(cur->state->layout, NULL, lx, ly, - &lx, &ly); - } else { - /* - * There is no mapping box for the input device and the - * output layout is empty. This can happen for example - * when external monitors are turned off/disconnected. - * In this case, all (x,y) points are equally invalid, - * so leave the cursor in its current location (better - * from a user standpoint than warping it to (0,0)). - */ - return; - } - - cursor_warp_unchecked(cur, lx, ly); -} - -void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, - struct wlr_input_device *dev, double x, double y, - double *lx, double *ly) { - assert(cur->state->layout); - - struct wlr_box mapping; - get_mapping(cur, dev, &mapping); - if (wlr_box_empty(&mapping)) { - wlr_output_layout_get_box(cur->state->layout, NULL, &mapping); - } - - *lx = !isnan(x) ? mapping.width * x + mapping.x : cur->x; - *ly = !isnan(y) ? mapping.height * y + mapping.y : cur->y; -} - -void wlr_cursor_warp_absolute(struct wlr_cursor *cur, - struct wlr_input_device *dev, double x, double y) { - assert(cur->state->layout); - - double lx, ly; - wlr_cursor_absolute_to_layout_coords(cur, dev, x, y, &lx, &ly); - - wlr_cursor_warp_closest(cur, dev, lx, ly); -} - -void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, - double delta_x, double delta_y) { - assert(cur->state->layout); - - double lx = !isnan(delta_x) ? cur->x + delta_x : cur->x; - double ly = !isnan(delta_y) ? cur->y + delta_y : cur->y; - - wlr_cursor_warp_closest(cur, dev, lx, ly); -} - -void wlr_cursor_set_image(struct wlr_cursor *cur, const uint8_t *pixels, +void wlr_cursor_set_image(struct wlr_cursor *cursor, const uint8_t *pixels, int32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y, float scale) { struct wlr_cursor_output_cursor *output_cursor; - wl_list_for_each(output_cursor, &cur->state->output_cursors, link) { + wl_list_for_each(output_cursor, &cursor->output_cursors, link) { float output_scale = output_cursor->output_cursor->output->scale; if (scale > 0 && output_scale != scale) { continue; @@ -368,549 +126,11 @@ void wlr_cursor_set_image(struct wlr_cursor *cur, const uint8_t *pixels, } } -void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface, +void wlr_cursor_set_surface(struct wlr_cursor *cursor, struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_cursor_output_cursor *output_cursor; - wl_list_for_each(output_cursor, &cur->state->output_cursors, link) { + wl_list_for_each(output_cursor, &cursor->output_cursors, link) { wlr_output_cursor_set_surface(output_cursor->output_cursor, surface, hotspot_x, hotspot_y); } } - -static void handle_pointer_motion(struct wl_listener *listener, void *data) { - struct wlr_pointer_motion_event *event = data; - struct wlr_cursor_device *device = - wl_container_of(listener, device, motion); - wl_signal_emit_mutable(&device->cursor->events.motion, event); -} - -static void apply_output_transform(double *x, double *y, - enum wl_output_transform transform) { - double dx = 0.0, dy = 0.0; - double width = 1.0, height = 1.0; - - switch (transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - dx = *x; - dy = *y; - break; - case WL_OUTPUT_TRANSFORM_90: - dx = height - *y; - dy = *x; - break; - case WL_OUTPUT_TRANSFORM_180: - dx = width - *x; - dy = height - *y; - break; - case WL_OUTPUT_TRANSFORM_270: - dx = *y; - dy = width - *x; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED: - dx = width - *x; - dy = *y; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - dx = *y; - dy = *x; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - dx = *x; - dy = height - *y; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - dx = height - *y; - dy = width - *x; - break; - } - *x = dx; - *y = dy; -} - - -static struct wlr_output *get_mapped_output(struct wlr_cursor_device *cursor_device) { - if (cursor_device->mapped_output) { - return cursor_device->mapped_output; - } - - struct wlr_cursor *cursor = cursor_device->cursor; - assert(cursor); - if (cursor->state->mapped_output) { - return cursor->state->mapped_output; - } - return NULL; -} - - -static void handle_pointer_motion_absolute(struct wl_listener *listener, - void *data) { - struct wlr_pointer_motion_absolute_event *event = data; - struct wlr_cursor_device *device = - wl_container_of(listener, device, motion_absolute); - - struct wlr_output *output = - get_mapped_output(device); - if (output) { - apply_output_transform(&event->x, &event->y, output->transform); - } - wl_signal_emit_mutable(&device->cursor->events.motion_absolute, event); -} - -static void handle_pointer_button(struct wl_listener *listener, void *data) { - struct wlr_pointer_button_event *event = data; - struct wlr_cursor_device *device = - wl_container_of(listener, device, button); - wl_signal_emit_mutable(&device->cursor->events.button, event); -} - -static void handle_pointer_axis(struct wl_listener *listener, void *data) { - struct wlr_pointer_axis_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, axis); - wl_signal_emit_mutable(&device->cursor->events.axis, event); -} - -static void handle_pointer_frame(struct wl_listener *listener, void *data) { - struct wlr_cursor_device *device = wl_container_of(listener, device, frame); - wl_signal_emit_mutable(&device->cursor->events.frame, device->cursor); -} - -static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { - struct wlr_pointer_swipe_begin_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, swipe_begin); - wl_signal_emit_mutable(&device->cursor->events.swipe_begin, event); -} - -static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { - struct wlr_pointer_swipe_update_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, swipe_update); - wl_signal_emit_mutable(&device->cursor->events.swipe_update, event); -} - -static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { - struct wlr_pointer_swipe_end_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, swipe_end); - wl_signal_emit_mutable(&device->cursor->events.swipe_end, event); -} - -static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { - struct wlr_pointer_pinch_begin_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, pinch_begin); - wl_signal_emit_mutable(&device->cursor->events.pinch_begin, event); -} - -static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { - struct wlr_pointer_pinch_update_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, pinch_update); - wl_signal_emit_mutable(&device->cursor->events.pinch_update, event); -} - -static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { - struct wlr_pointer_pinch_end_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, pinch_end); - wl_signal_emit_mutable(&device->cursor->events.pinch_end, event); -} - -static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { - struct wlr_pointer_hold_begin_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, hold_begin); - wl_signal_emit_mutable(&device->cursor->events.hold_begin, event); -} - -static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { - struct wlr_pointer_hold_end_event *event = data; - struct wlr_cursor_device *device = wl_container_of(listener, device, hold_end); - wl_signal_emit_mutable(&device->cursor->events.hold_end, event); -} - -static void handle_touch_up(struct wl_listener *listener, void *data) { - struct wlr_touch_up_event *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, touch_up); - wl_signal_emit_mutable(&device->cursor->events.touch_up, event); -} - -static void handle_touch_down(struct wl_listener *listener, void *data) { - struct wlr_touch_down_event *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, touch_down); - - struct wlr_output *output = - get_mapped_output(device); - if (output) { - apply_output_transform(&event->x, &event->y, output->transform); - } - wl_signal_emit_mutable(&device->cursor->events.touch_down, event); -} - -static void handle_touch_motion(struct wl_listener *listener, void *data) { - struct wlr_touch_motion_event *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, touch_motion); - - struct wlr_output *output = - get_mapped_output(device); - if (output) { - apply_output_transform(&event->x, &event->y, output->transform); - } - wl_signal_emit_mutable(&device->cursor->events.touch_motion, event); -} - -static void handle_touch_cancel(struct wl_listener *listener, void *data) { - struct wlr_touch_cancel_event *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, touch_cancel); - wl_signal_emit_mutable(&device->cursor->events.touch_cancel, event); -} - -static void handle_touch_frame(struct wl_listener *listener, void *data) { - struct wlr_cursor_device *device = - wl_container_of(listener, device, touch_frame); - wl_signal_emit_mutable(&device->cursor->events.touch_frame, NULL); -} - -static void handle_tablet_tool_tip(struct wl_listener *listener, void *data) { - struct wlr_tablet_tool_tip_event *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, tablet_tool_tip); - - struct wlr_output *output = - get_mapped_output(device); - if (output) { - apply_output_transform(&event->x, &event->y, output->transform); - } - wl_signal_emit_mutable(&device->cursor->events.tablet_tool_tip, event); -} - -static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) { - struct wlr_tablet_tool_axis_event *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, tablet_tool_axis); - - struct wlr_output *output = get_mapped_output(device); - if (output) { - // In the case that only one axis received an event, rotating the input can - // cause the change to actually happen on the other axis, as far as clients - // are concerned. - // - // Here, we feed apply_output_transform NAN on the axis that didn't change, - // and remap the axes flags based on whether it returns NAN itself. - double x = event->updated_axes & WLR_TABLET_TOOL_AXIS_X ? event->x : NAN; - double y = event->updated_axes & WLR_TABLET_TOOL_AXIS_Y ? event->y : NAN; - - apply_output_transform(&x, &y, output->transform); - - event->updated_axes &= ~(WLR_TABLET_TOOL_AXIS_X | WLR_TABLET_TOOL_AXIS_Y); - event->x = event->y = 0; - - if (!isnan(x)) { - event->updated_axes |= WLR_TABLET_TOOL_AXIS_X; - event->x = x; - } - - if (!isnan(y)) { - event->updated_axes |= WLR_TABLET_TOOL_AXIS_Y; - event->y = y; - } - } - - wl_signal_emit_mutable(&device->cursor->events.tablet_tool_axis, event); -} - -static void handle_tablet_tool_button(struct wl_listener *listener, - void *data) { - struct wlr_tablet_tool_button *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, tablet_tool_button); - wl_signal_emit_mutable(&device->cursor->events.tablet_tool_button, event); -} - -static void handle_tablet_tool_proximity(struct wl_listener *listener, - void *data) { - struct wlr_tablet_tool_proximity_event *event = data; - struct wlr_cursor_device *device; - device = wl_container_of(listener, device, tablet_tool_proximity); - - struct wlr_output *output = - get_mapped_output(device); - if (output) { - apply_output_transform(&event->x, &event->y, output->transform); - } - wl_signal_emit_mutable(&device->cursor->events.tablet_tool_proximity, event); -} - -static void handle_device_destroy(struct wl_listener *listener, void *data) { - struct wlr_cursor_device *c_device; - c_device = wl_container_of(listener, c_device, destroy); - wlr_cursor_detach_input_device(c_device->cursor, c_device->device); -} - -static struct wlr_cursor_device *cursor_device_create( - struct wlr_cursor *cursor, struct wlr_input_device *device) { - struct wlr_cursor_device *c_device = - calloc(1, sizeof(struct wlr_cursor_device)); - if (!c_device) { - wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_device"); - return NULL; - } - - c_device->cursor = cursor; - c_device->device = device; - - // listen to events - wl_signal_add(&device->events.destroy, &c_device->destroy); - c_device->destroy.notify = handle_device_destroy; - - if (device->type == WLR_INPUT_DEVICE_POINTER) { - struct wlr_pointer *pointer = wlr_pointer_from_input_device(device); - - wl_signal_add(&pointer->events.motion, &c_device->motion); - c_device->motion.notify = handle_pointer_motion; - - wl_signal_add(&pointer->events.motion_absolute, - &c_device->motion_absolute); - c_device->motion_absolute.notify = handle_pointer_motion_absolute; - - wl_signal_add(&pointer->events.button, &c_device->button); - c_device->button.notify = handle_pointer_button; - - wl_signal_add(&pointer->events.axis, &c_device->axis); - c_device->axis.notify = handle_pointer_axis; - - wl_signal_add(&pointer->events.frame, &c_device->frame); - c_device->frame.notify = handle_pointer_frame; - - wl_signal_add(&pointer->events.swipe_begin, &c_device->swipe_begin); - c_device->swipe_begin.notify = handle_pointer_swipe_begin; - - wl_signal_add(&pointer->events.swipe_update, &c_device->swipe_update); - c_device->swipe_update.notify = handle_pointer_swipe_update; - - wl_signal_add(&pointer->events.swipe_end, &c_device->swipe_end); - c_device->swipe_end.notify = handle_pointer_swipe_end; - - wl_signal_add(&pointer->events.pinch_begin, &c_device->pinch_begin); - c_device->pinch_begin.notify = handle_pointer_pinch_begin; - - wl_signal_add(&pointer->events.pinch_update, &c_device->pinch_update); - c_device->pinch_update.notify = handle_pointer_pinch_update; - - wl_signal_add(&pointer->events.pinch_end, &c_device->pinch_end); - c_device->pinch_end.notify = handle_pointer_pinch_end; - - wl_signal_add(&pointer->events.hold_begin, &c_device->hold_begin); - c_device->hold_begin.notify = handle_pointer_hold_begin; - - wl_signal_add(&pointer->events.hold_end, &c_device->hold_end); - c_device->hold_end.notify = handle_pointer_hold_end; - } else if (device->type == WLR_INPUT_DEVICE_TOUCH) { - struct wlr_touch *touch = wlr_touch_from_input_device(device); - - wl_signal_add(&touch->events.motion, &c_device->touch_motion); - c_device->touch_motion.notify = handle_touch_motion; - - wl_signal_add(&touch->events.down, &c_device->touch_down); - c_device->touch_down.notify = handle_touch_down; - - wl_signal_add(&touch->events.up, &c_device->touch_up); - c_device->touch_up.notify = handle_touch_up; - - wl_signal_add(&touch->events.cancel, &c_device->touch_cancel); - c_device->touch_cancel.notify = handle_touch_cancel; - - wl_signal_add(&touch->events.frame, &c_device->touch_frame); - c_device->touch_frame.notify = handle_touch_frame; - } else if (device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { - struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); - - wl_signal_add(&tablet->events.tip, &c_device->tablet_tool_tip); - c_device->tablet_tool_tip.notify = handle_tablet_tool_tip; - - wl_signal_add(&tablet->events.proximity, - &c_device->tablet_tool_proximity); - c_device->tablet_tool_proximity.notify = handle_tablet_tool_proximity; - - wl_signal_add(&tablet->events.axis, &c_device->tablet_tool_axis); - c_device->tablet_tool_axis.notify = handle_tablet_tool_axis; - - wl_signal_add(&tablet->events.button, &c_device->tablet_tool_button); - c_device->tablet_tool_button.notify = handle_tablet_tool_button; - } - - wl_list_insert(&cursor->state->devices, &c_device->link); - - return c_device; -} - -void wlr_cursor_attach_input_device(struct wlr_cursor *cur, - struct wlr_input_device *dev) { - if (dev->type != WLR_INPUT_DEVICE_POINTER && - dev->type != WLR_INPUT_DEVICE_TOUCH && - dev->type != WLR_INPUT_DEVICE_TABLET_TOOL) { - wlr_log(WLR_ERROR, "only device types of pointer, touch or tablet tool" - "are supported"); - return; - } - - // make sure it is not already attached - struct wlr_cursor_device *_dev; - wl_list_for_each(_dev, &cur->state->devices, link) { - if (_dev->device == dev) { - return; - } - } - - cursor_device_create(cur, dev); -} - -void wlr_cursor_detach_input_device(struct wlr_cursor *cur, - struct wlr_input_device *dev) { - struct wlr_cursor_device *c_device, *tmp = NULL; - wl_list_for_each_safe(c_device, tmp, &cur->state->devices, link) { - if (c_device->device == dev) { - cursor_device_destroy(c_device); - } - } -} - -static void handle_layout_destroy(struct wl_listener *listener, void *data) { - struct wlr_cursor_state *state = - wl_container_of(listener, state, layout_destroy); - cursor_detach_output_layout(state->cursor); -} - -static void handle_layout_output_destroy(struct wl_listener *listener, - void *data) { - struct wlr_cursor_output_cursor *output_cursor = - wl_container_of(listener, output_cursor, layout_output_destroy); - //struct wlr_output_layout_output *l_output = data; - output_cursor_destroy(output_cursor); -} - -static void layout_add(struct wlr_cursor_state *state, - struct wlr_output_layout_output *l_output) { - struct wlr_cursor_output_cursor *output_cursor; - wl_list_for_each(output_cursor, &state->output_cursors, link) { - if (output_cursor->output_cursor->output == l_output->output) { - return; // already added - } - } - - output_cursor = calloc(1, sizeof(struct wlr_cursor_output_cursor)); - if (output_cursor == NULL) { - wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_output_cursor"); - return; - } - output_cursor->cursor = state->cursor; - - output_cursor->output_cursor = wlr_output_cursor_create(l_output->output); - if (output_cursor->output_cursor == NULL) { - wlr_log(WLR_ERROR, "Failed to create wlr_output_cursor"); - free(output_cursor); - return; - } - - output_cursor->layout_output_destroy.notify = handle_layout_output_destroy; - wl_signal_add(&l_output->events.destroy, - &output_cursor->layout_output_destroy); - - wl_list_insert(&state->output_cursors, &output_cursor->link); -} - -static void handle_layout_add(struct wl_listener *listener, void *data) { - struct wlr_cursor_state *state = - wl_container_of(listener, state, layout_add); - struct wlr_output_layout_output *l_output = data; - layout_add(state, l_output); -} - -static void handle_layout_change(struct wl_listener *listener, void *data) { - struct wlr_cursor_state *state = - wl_container_of(listener, state, layout_change); - struct wlr_output_layout *layout = data; - - if (!wlr_output_layout_contains_point(layout, NULL, state->cursor->x, - state->cursor->y) && !wl_list_empty(&layout->outputs)) { - // the output we were on has gone away so go to the closest boundary - // point (unless the layout is empty; compare warp_closest()) - double x, y; - wlr_output_layout_closest_point(layout, NULL, state->cursor->x, - state->cursor->y, &x, &y); - - cursor_warp_unchecked(state->cursor, x, y); - } -} - -void wlr_cursor_attach_output_layout(struct wlr_cursor *cur, - struct wlr_output_layout *l) { - cursor_detach_output_layout(cur); - - if (l == NULL) { - return; - } - - wl_signal_add(&l->events.add, &cur->state->layout_add); - cur->state->layout_add.notify = handle_layout_add; - wl_signal_add(&l->events.change, &cur->state->layout_change); - cur->state->layout_change.notify = handle_layout_change; - wl_signal_add(&l->events.destroy, &cur->state->layout_destroy); - cur->state->layout_destroy.notify = handle_layout_destroy; - - cur->state->layout = l; - - struct wlr_output_layout_output *l_output; - wl_list_for_each(l_output, &l->outputs, link) { - layout_add(cur->state, l_output); - } -} - -void wlr_cursor_map_to_output(struct wlr_cursor *cur, - struct wlr_output *output) { - cur->state->mapped_output = output; -} - -void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, - struct wlr_input_device *dev, struct wlr_output *output) { - struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); - if (!c_device) { - wlr_log(WLR_ERROR, "Cannot map device \"%s\" to output" - " (not found in this cursor)", dev->name); - return; - } - - c_device->mapped_output = output; -} - -void wlr_cursor_map_to_region(struct wlr_cursor *cur, - const struct wlr_box *box) { - memset(&cur->state->mapped_box, 0, sizeof(cur->state->mapped_box)); - - if (box) { - if (wlr_box_empty(box)) { - wlr_log(WLR_ERROR, "cannot map cursor to an empty region"); - return; - } - cur->state->mapped_box = *box; - } -} - -void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, - struct wlr_input_device *dev, const struct wlr_box *box) { - memset(&cur->state->mapped_box, 0, sizeof(cur->state->mapped_box)); - - struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); - if (!c_device) { - wlr_log(WLR_ERROR, "Cannot map device \"%s\" to geometry (not found in" - "this cursor)", dev->name); - return; - } - - if (box) { - if (wlr_box_empty(box)) { - wlr_log(WLR_ERROR, - "cannot map device \"%s\" input to an empty region", - dev->name); - return; - } - c_device->mapped_box = *box; - } -} diff --git a/types/wlr_input_device.c b/types/wlr_input_device.c index 21916d699..0bc9455fd 100644 --- a/types/wlr_input_device.c +++ b/types/wlr_input_device.c @@ -2,6 +2,7 @@ #include #include +#include #include "interfaces/wlr_input_device.h" void wlr_input_device_init(struct wlr_input_device *dev, @@ -12,6 +13,7 @@ void wlr_input_device_init(struct wlr_input_device *dev, dev->vendor = 0; dev->product = 0; + wlr_addon_set_init(&dev->addons); wl_signal_init(&dev->events.destroy); } @@ -22,6 +24,7 @@ void wlr_input_device_finish(struct wlr_input_device *wlr_device) { wl_signal_emit_mutable(&wlr_device->events.destroy, wlr_device); + wlr_addon_set_finish(&wlr_device->addons); wl_list_remove(&wlr_device->events.destroy.listener_list); free(wlr_device->name); diff --git a/types/wlr_input_mapper.c b/types/wlr_input_mapper.c new file mode 100644 index 000000000..89e73ca46 --- /dev/null +++ b/types/wlr_input_mapper.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void constraint_detach_output(struct wlr_input_constraint *constraint) { + constraint->output = NULL; + wl_list_remove(&constraint->output_destroy.link); + wl_list_init(&constraint->output_destroy.link); +} + +static void constraint_handle_output_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_constraint *constraint = + wl_container_of(listener, constraint, output_destroy); + constraint_detach_output(constraint); +} + +static void constraint_init(struct wlr_input_constraint *constraint) { + memset(constraint, 0, sizeof(*constraint)); + + constraint->output_destroy.notify = constraint_handle_output_destroy; + wl_list_init(&constraint->output_destroy.link); +} + +static void constraint_finish(struct wlr_input_constraint *constraint) { + wl_list_remove(&constraint->output_destroy.link); +} + +static void mapping_destroy(struct wlr_input_mapping *mapping) { + constraint_finish(&mapping->constraint); + wlr_addon_finish(&mapping->addon); + wl_list_remove(&mapping->link); + free(mapping); +} + +static void device_addon_destroy(struct wlr_addon *addon) { + struct wlr_input_mapping *mapping = + wl_container_of(addon, mapping, addon); + mapping_destroy(mapping); +} + +static const struct wlr_addon_interface device_addon_impl = { + .name = "wlr_input_mapping", + .destroy = device_addon_destroy, +}; + +static struct wlr_input_mapping *mapping_create(struct wlr_input_mapper *mapper, + struct wlr_input_device *device) { + struct wlr_input_mapping *mapping = calloc(1, sizeof(*mapping)); + if (mapping == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + constraint_init(&mapping->constraint); + wlr_addon_init(&mapping->addon, &device->addons, mapper, &device_addon_impl); + wl_list_insert(&mapper->mappings, &mapping->link); + + return mapping; +} + +static struct wlr_input_mapping *get_mapping(struct wlr_input_mapper *mapper, + struct wlr_input_device *device) { + struct wlr_input_mapping *mapping = NULL; + struct wlr_addon *addon = wlr_addon_find(&device->addons, + mapper, &device_addon_impl); + if (addon != NULL) { + mapping = wl_container_of(addon, mapping, addon); + } + return mapping; +} + +static void detach_output_layout(struct wlr_input_mapper *mapper) { + mapper->layout = NULL; + wl_list_remove(&mapper->layout_destroy.link); + wl_list_init(&mapper->layout_destroy.link); +} + +static void handle_layout_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_mapper *mapper = + wl_container_of(listener, mapper, layout_destroy); + detach_output_layout(mapper); +} + +static void get_constraint(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, struct wlr_box *box) { + memset(box, 0, sizeof(*box)); + + if (device != NULL) { + struct wlr_input_mapping *mapping = get_mapping(mapper, device); + if (mapping != NULL) { + if (!wlr_box_empty(&mapping->constraint.box)) { + *box = mapping->constraint.box; + } else if (mapper->layout != NULL && + mapping->constraint.output != NULL) { + wlr_output_layout_get_box(mapper->layout, + mapping->constraint.output, box); + assert(!wlr_box_empty(box)); + } + } + } + + if (wlr_box_empty(box)) { + if (!wlr_box_empty(&mapper->global.box)) { + *box = mapper->global.box; + } else if (mapper->layout != NULL && + mapper->global.output != NULL) { + wlr_output_layout_get_box(mapper->layout, + mapper->global.output, box); + assert(!wlr_box_empty(box)); + } + } +} + +struct wlr_input_mapper *wlr_input_mapper_create(void) { + struct wlr_input_mapper *mapper = calloc(1, sizeof(*mapper)); + if (mapper == NULL) { + return NULL; + } + + constraint_init(&mapper->global); + + wl_list_init(&mapper->mappings); + wl_signal_init(&mapper->events.destroy); + + mapper->layout_destroy.notify = handle_layout_destroy; + wl_list_init(&mapper->layout_destroy.link); + + return mapper; +} + +void wlr_input_mapper_destroy(struct wlr_input_mapper *mapper) { + if (mapper == NULL) { + return; + } + + wl_signal_emit_mutable(&mapper->events.destroy, NULL); + + struct wlr_input_mapping *mapping, *tmp; + wl_list_for_each_safe(mapping, tmp, &mapper->mappings, link) { + mapping_destroy(mapping); + } + + constraint_finish(&mapper->global); + + wl_list_remove(&mapper->layout_destroy.link); + free(mapper); +} + +void wlr_input_mapper_attach_output_layout(struct wlr_input_mapper *mapper, + struct wlr_output_layout *layout) { + detach_output_layout(mapper); + mapper->layout = layout; + if (layout != NULL) { + wl_signal_add(&layout->events.destroy, &mapper->layout_destroy); + } +} + +void wlr_input_mapper_absolute_to_layout(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, double x, double y, + double *lx, double *ly) { + struct wlr_box box; + get_constraint(mapper, device, &box); + if (wlr_box_empty(&box) && mapper->layout != NULL) { + wlr_output_layout_get_box(mapper->layout, NULL, &box); + } + + // At this point, if no matching constraint was found and the layout + // is NULL or empty, box is filled with zeroes + *lx = x * box.width + box.x; + *ly = y * box.height + box.y; +} + +void wlr_input_mapper_closest_point(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, double lx, double ly, + double *closest_lx, double *closest_ly) { + struct wlr_box box; + get_constraint(mapper, device, &box); + if (!wlr_box_empty(&box)) { + wlr_box_closest_point(&box, lx, ly, closest_lx, closest_ly); + } else if (mapper->layout != NULL && + !wl_list_empty(&mapper->layout->outputs)) { + wlr_output_layout_closest_point(mapper->layout, NULL, + lx, ly, closest_lx, closest_ly); + } else { + // No constraint was found and the layout is either NULL or + // empty. In this case, all points are equally invalid; + // for convenience, the original point is returned. + *closest_lx = lx; + *closest_ly = ly; + } +} + +void wlr_input_mapper_map_to_output(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, struct wlr_output *output) { + struct wlr_input_constraint *constraint = &mapper->global; + if (device != NULL) { + struct wlr_input_mapping *mapping = get_mapping(mapper, device); + if (mapping == NULL) { + mapping = mapping_create(mapper, device); + if (mapping == NULL) { + return; + } + } + constraint = &mapping->constraint; + } + + constraint_detach_output(constraint); + constraint->output = output; + if (output != NULL) { + wl_signal_add(&output->events.destroy, &constraint->output_destroy); + } +} + +void wlr_input_mapper_map_to_box(struct wlr_input_mapper *mapper, + struct wlr_input_device *device, const struct wlr_box *box) { + struct wlr_input_constraint *constraint = &mapper->global; + if (device != NULL) { + struct wlr_input_mapping *mapping = get_mapping(mapper, device); + if (mapping == NULL) { + mapping = mapping_create(mapper, device); + if (mapping == NULL) { + return; + } + } + constraint = &mapping->constraint; + } + + constraint->box = *box; +}