diff --git a/include/sway/commands.h b/include/sway/commands.h index d39ac56ca..8d5e7e742 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -111,6 +111,7 @@ sway_cmd cmd_for_window; sway_cmd cmd_force_focus_wrapping; sway_cmd cmd_fullscreen; sway_cmd cmd_gaps; +sway_cmd cmd_hide_cursor; sway_cmd cmd_hide_edge_borders; sway_cmd cmd_include; sway_cmd cmd_input; diff --git a/include/sway/config.h b/include/sway/config.h index f77c3b503..bc2b31c58 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -341,6 +341,8 @@ struct sway_config { int floating_border_thickness; enum edge_border_types hide_edge_borders; + int hide_cursor_timeout; + // border colors struct { struct border_colors focused; diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 20c1c9038..e8f977b21 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -1,6 +1,8 @@ #ifndef _SWAY_INPUT_CURSOR_H #define _SWAY_INPUT_CURSOR_H #include +#include +#include #include "sway/input/seat.h" struct sway_cursor { @@ -9,6 +11,8 @@ struct sway_cursor { struct wlr_xcursor_manager *xcursor_manager; struct wl_client *image_client; + struct wlr_surface *image_surface; + int hotspot_x, hotspot_y; struct wl_listener motion; struct wl_listener motion_absolute; @@ -25,6 +29,9 @@ struct sway_cursor { uint32_t tool_buttons; struct wl_listener request_set_cursor; + + struct wl_event_source *hide_source; + bool hidden; }; void sway_cursor_destroy(struct sway_cursor *cursor); diff --git a/sway/commands.c b/sway/commands.c index 60c647768..6c799d398 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -109,6 +109,7 @@ static struct cmd_handler handlers[] = { { "font", cmd_font }, { "for_window", cmd_for_window }, { "fullscreen", cmd_fullscreen }, + { "hide_cursor", cmd_hide_cursor }, { "hide_edge_borders", cmd_hide_edge_borders }, { "include", cmd_include }, { "input", cmd_input }, diff --git a/sway/commands/hide_cursor.c b/sway/commands/hide_cursor.c new file mode 100644 index 000000000..9d136840d --- /dev/null +++ b/sway/commands/hide_cursor.c @@ -0,0 +1,29 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *cmd_hide_cursor(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "hide_cursor", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + char *end; + config->hide_cursor_timeout = strtol(argv[0], &end, 10); + if (*end) { + config->hide_cursor_timeout = 0; + return cmd_results_new(CMD_INVALID, "hide_cursor", + "Expected an integer timeout."); + } + if (config->hide_cursor_timeout < 100 && config->hide_cursor_timeout != 0) { + config->hide_cursor_timeout = 100; + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config.c b/sway/config.c index 34c8a2802..c38dc6d25 100644 --- a/sway/config.c +++ b/sway/config.c @@ -208,6 +208,8 @@ static void config_defaults(struct sway_config *config) { config->floating_border_thickness = 2; config->hide_edge_borders = E_NONE; + config->hide_cursor_timeout = 0; + // border colors set_color(config->border_colors.focused.border, 0x4C7899); set_color(config->border_colors.focused.border, 0x4C7899); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 9259c4756..a3e737394 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -155,8 +155,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) client = wl_resource_get_client(surface->resource); } if (client != cursor->image_client) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); + if (!cursor->hidden) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + } + cursor->image_surface = NULL; cursor->image_client = client; } @@ -171,23 +174,53 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) } } +static void apply_cursor_image(struct sway_cursor *cursor) { + if (cursor->hidden) { + return; + } + + if (cursor->image_surface) { + wlr_cursor_set_surface(cursor->cursor, cursor->image_surface, + cursor->hotspot_x, cursor->hotspot_y); + } else { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + } +} + +static int hide_notify(void *data) { + struct sway_cursor *cursor = data; + wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + cursor->hidden = true; + return 1; +} + +static void handle_activity(struct sway_cursor *cursor) { + wl_event_source_timer_update(cursor->hide_source, + config->hide_cursor_timeout); + wlr_idle_notify_activity( + cursor->seat->input->server->idle, cursor->seat->wlr_seat); + cursor->hidden = false; + apply_cursor_image(cursor); +} + static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_pointer_motion *event = data; wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y); cursor_send_pointer_motion(cursor, event->time_msec); + handle_activity(cursor); } static void handle_cursor_motion_absolute( struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion_absolute); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); cursor_send_pointer_motion(cursor, event->time_msec); + handle_activity(cursor); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -234,23 +267,24 @@ void dispatch_cursor_button(struct sway_cursor *cursor, static void handle_cursor_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, button); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_pointer_button *event = data; dispatch_cursor_button(cursor, event->time_msec, event->button, event->state); + handle_activity(cursor); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_pointer_axis *event = data; wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source); + handle_activity(cursor); } static void handle_touch_down(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); + wlr_idle_notify_activity( + cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_touch_down *event = data; struct wlr_seat *seat = cursor->seat->wlr_seat; @@ -271,13 +305,15 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { wlr_seat_touch_notify_down(seat, surface, event->time_msec, event->touch_id, sx, sy); cursor->image_client = NULL; + cursor->image_surface = NULL; wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); } } static void handle_touch_up(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); + wlr_idle_notify_activity( + cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_touch_up *event = data; struct wlr_seat *seat = cursor->seat->wlr_seat; // TODO: fall back to cursor simulation if client has not bound to touch @@ -287,7 +323,8 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { static void handle_touch_motion(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_motion); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); + wlr_idle_notify_activity( + cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_touch_motion *event = data; struct wlr_seat *seat = cursor->seat->wlr_seat; @@ -339,7 +376,8 @@ static void apply_mapping_from_region(struct wlr_input_device *device, static void handle_tool_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); + wlr_idle_notify_activity( + cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_axis *event = data; struct sway_input_device *input_device = event->device->data; @@ -362,7 +400,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { static void handle_tool_tip(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); + wlr_idle_notify_activity( + cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_tip *event = data; dispatch_cursor_button(cursor, event->time_msec, BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? @@ -371,7 +410,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { static void handle_tool_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); - wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); + wlr_idle_notify_activity( + cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_button *event = data; // TODO: the user may want to configure which tool buttons are mapped to // which simulated pointer buttons @@ -413,9 +453,11 @@ static void handle_request_set_cursor(struct wl_listener *listener, return; } - wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, - event->hotspot_y); cursor->image_client = focused_client; + cursor->image_surface = event->surface; + cursor->hotspot_x = event->hotspot_x; + cursor->hotspot_y = event->hotspot_y; + apply_cursor_image(cursor); } void sway_cursor_destroy(struct sway_cursor *cursor) { @@ -444,6 +486,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wlr_cursor_attach_output_layout(wlr_cursor, root_container.sway_root->output_layout); + cursor->hide_source = wl_event_loop_add_timer( + seat->input->server->wl_event_loop, hide_notify, cursor); + // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); cursor->motion.notify = handle_cursor_motion; diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c07557db8..e6dd2d944 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -331,7 +331,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; struct wlr_input_device *wlr_device = keyboard->seat_device->input_device->wlr_device; - wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); + wlr_idle_notify_activity( + keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); struct wlr_event_keyboard_key *event = data; xkb_keycode_t keycode = event->keycode + 8; diff --git a/sway/input/seat.c b/sway/input/seat.c index 9ac3e6a89..4b9be5b10 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -244,6 +244,7 @@ struct sway_seat *seat_create(struct sway_input_manager *input, if (!seat) { return NULL; } + seat->input = input; seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name); if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { @@ -268,7 +269,6 @@ struct sway_seat *seat_create(struct sway_input_manager *input, &seat->new_container); seat->new_container.notify = handle_new_container; - seat->input = input; wl_list_init(&seat->devices); wlr_seat_set_capabilities(seat->wlr_seat, diff --git a/sway/meson.build b/sway/meson.build index 570d47835..33d006722 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -42,6 +42,7 @@ sway_sources = files( 'commands/font.c', 'commands/for_window.c', 'commands/fullscreen.c', + 'commands/hide_cursor.c', 'commands/hide_edge_borders.c', 'commands/kill.c', 'commands/opacity.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 2ebdb8ed4..bc7204967 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -342,6 +342,11 @@ The default colors are: workspace (or current workspace), and _current_ changes gaps for the current view or workspace. +*hide\_cursor* + Hides the cursor image after the specified _timeout_ (in milliseconds) has + elapsed with no activity on that cursor. A timeout of 0 disables hiding + the cursor. + *hide\_edge\_borders* none|vertical|horizontal|both|smart Hides window borders adjacent to the screen edges. Default is _none_.