diff options
40 files changed, 1730 insertions, 498 deletions
diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 40b559e2..af2619ff 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -76,7 +76,7 @@ static void session_signal(struct wl_listener *listener, void *data) { struct wlr_drm_plane *plane = conn->crtc->cursor; drm->iface->crtc_set_cursor(drm, conn->crtc, - plane ? plane->cursor_bo : NULL); + (plane && plane->cursor_enabled) ? plane->cursor_bo : NULL); } } else { wlr_log(L_INFO, "DRM fd paused"); diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 441ba24e..a3594bb0 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -505,8 +505,10 @@ static bool wlr_drm_connector_set_cursor(struct wlr_output *output, if (!buf && update_pixels) { // Hide the cursor + plane->cursor_enabled = false; return drm->iface->crtc_set_cursor(drm, crtc, NULL); } + plane->cursor_enabled = true; // We don't have a real cursor plane, so we make a fake one if (!plane) { diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 4d23f963..a6dd247c 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -33,6 +33,7 @@ struct wlr_drm_plane { float matrix[16]; struct wlr_texture *wlr_tex; struct gbm_bo *cursor_bo; + bool cursor_enabled; union wlr_drm_plane_props props; }; diff --git a/include/rootston/config.h b/include/rootston/config.h index 5fa4890f..ecbdd88b 100644 --- a/include/rootston/config.h +++ b/include/rootston/config.h @@ -26,6 +26,17 @@ struct binding_config { struct wl_list link; }; +struct keyboard_config { + char *name; + uint32_t meta_key; + char *rules; + char *model; + char *layout; + char *variant; + char *options; + struct wl_list link; +}; + struct roots_config { bool xwayland; // TODO: Multiple cursors, multiseat @@ -34,13 +45,10 @@ struct roots_config { struct wlr_box *mapped_box; } cursor; - struct { - uint32_t meta_key; - } keyboard; - struct wl_list outputs; struct wl_list devices; struct wl_list bindings; + struct wl_list keyboards; char *config_path; char *startup_cmd; }; @@ -54,13 +62,20 @@ void roots_config_destroy(struct roots_config *config); * NULL. */ struct output_config *config_get_output(struct roots_config *config, - struct wlr_output *output); + struct wlr_output *output); /** * Get configuration for the device. If the device is not configured, returns * NULL. */ struct device_config *config_get_device(struct roots_config *config, - struct wlr_input_device *device); + struct wlr_input_device *device); + +/** + * Get configuration for the keyboard. If the keyboard is not configured, + * returns NULL. A NULL device returns the default config for keyboards. + */ +struct keyboard_config *config_get_keyboard(struct roots_config *config, + struct wlr_input_device *device); #endif diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index c3859afb..376412fb 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -37,11 +37,13 @@ struct roots_desktop { struct wlr_xdg_shell_v6 *xdg_shell_v6; struct wlr_gamma_control_manager *gamma_control_manager; struct wlr_screenshooter *screenshooter; + struct wlr_server_decoration_manager *server_decoration_manager; struct wl_listener output_add; struct wl_listener output_remove; struct wl_listener xdg_shell_v6_surface; struct wl_listener wl_shell_surface; + struct wl_listener decoration_new; #ifdef HAS_XWAYLAND struct wlr_xwayland *xwayland; diff --git a/include/rootston/input.h b/include/rootston/input.h index b55288c9..5cd7509e 100644 --- a/include/rootston/input.h +++ b/include/rootston/input.h @@ -65,6 +65,17 @@ struct roots_input_event { struct wlr_input_device *device; }; +struct roots_drag_icon { + struct wlr_surface *surface; + struct wl_list link; // roots_input::drag_icons + + int32_t sx; + int32_t sy; + + struct wl_listener surface_destroy; + struct wl_listener surface_commit; +}; + struct roots_touch_point { struct roots_touch *device; int32_t slot; @@ -78,13 +89,13 @@ struct roots_input { // TODO: multiseat, multicursor struct wlr_cursor *cursor; - struct wlr_xcursor_theme *theme; - struct wlr_xcursor *xcursor; + struct wlr_xcursor_theme *xcursor_theme; struct wlr_seat *wl_seat; + struct wl_list drag_icons; struct wl_client *cursor_client; enum roots_cursor_mode mode; - struct roots_view *active_view, *last_active_view; + struct roots_view *active_view; int offs_x, offs_y; int view_x, view_y, view_width, view_height; float view_rotation; @@ -114,6 +125,7 @@ struct roots_input { struct wl_listener cursor_tool_axis; struct wl_listener cursor_tool_tip; + struct wl_listener pointer_grab_begin; struct wl_list touch_points; struct wl_listener pointer_grab_end; @@ -146,6 +158,12 @@ void view_begin_move(struct roots_input *input, struct wlr_cursor *cursor, void view_begin_resize(struct roots_input *input, struct wlr_cursor *cursor, struct roots_view *view, uint32_t edges); +struct wlr_xcursor *get_default_xcursor(struct wlr_xcursor_theme *theme); +struct wlr_xcursor *get_move_xcursor(struct wlr_xcursor_theme *theme); +struct wlr_xcursor *get_resize_xcursor(struct wlr_xcursor_theme *theme, + uint32_t edges); +struct wlr_xcursor *get_rotate_xcursor(struct wlr_xcursor_theme *theme); + void set_view_focus(struct roots_input *input, struct roots_desktop *desktop, struct roots_view *view); diff --git a/include/rootston/server.h b/include/rootston/server.h index a4eacb7f..8fc6530e 100644 --- a/include/rootston/server.h +++ b/include/rootston/server.h @@ -3,7 +3,7 @@ #include <wayland-server.h> #include <wlr/backend.h> #include <wlr/backend/session.h> -#include <wlr/types/wlr_data_device_manager.h> +#include <wlr/types/wlr_data_device.h> #include <wlr/render.h> #ifdef HAS_XWAYLAND #include <wlr/xwayland.h> diff --git a/include/rootston/view.h b/include/rootston/view.h index 7d297329..9eb7065d 100644 --- a/include/rootston/view.h +++ b/include/rootston/view.h @@ -80,6 +80,7 @@ void view_resize(struct roots_view *view, uint32_t width, uint32_t height); void view_set_position(struct roots_view *view, double x, double y); void view_close(struct roots_view *view); bool view_center(struct roots_view *view); -void view_initialize(struct roots_view *view); +void view_setup(struct roots_view *view); +void view_teardown(struct roots_view *view); #endif diff --git a/include/wlr/interfaces/wlr_data_source.h b/include/wlr/interfaces/wlr_data_source.h deleted file mode 100644 index 821bdea0..00000000 --- a/include/wlr/interfaces/wlr_data_source.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef WLR_INTERFACES_WLR_DATA_SOURCE_H -#define WLR_INTERFACES_WLR_DATA_SOURCE_H - -#include <wlr/types/wlr_data_source.h> - -struct wlr_data_source_impl { - void (*send)(struct wlr_data_source *data_source, const char *type, int fd); - void (*accepted)(struct wlr_data_source *data_source, const char *type); - void (*cancelled)(struct wlr_data_source *data_source); -}; - -bool wlr_data_source_init(struct wlr_data_source *source, - struct wlr_data_source_impl *impl); -void wlr_data_source_finish(struct wlr_data_source *source); - -#endif diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h new file mode 100644 index 00000000..e1e1e516 --- /dev/null +++ b/include/wlr/types/wlr_data_device.h @@ -0,0 +1,89 @@ +#ifndef WLR_TYPES_WLR_DATA_DEVICE_H +#define WLR_TYPES_WLR_DATA_DEVICE_H + +#include <wayland-server.h> +#include <wlr/types/wlr_seat.h> + +extern const struct +wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface; + +extern const struct +wlr_keyboard_grab_interface wlr_data_device_keyboard_drag_interface; + +struct wlr_data_device_manager { + struct wl_global *global; +}; + +struct wlr_data_offer { + struct wl_resource *resource; + struct wlr_data_source *source; + + uint32_t dnd_actions; + enum wl_data_device_manager_dnd_action preferred_dnd_action; + bool in_ask; + + struct wl_listener source_destroy; +}; + +struct wlr_data_source { + struct wl_resource *resource; + struct wlr_data_offer *offer; + struct wlr_seat_handle *seat; + struct wl_array mime_types; + + bool accepted; + + // drag and drop + enum wl_data_device_manager_dnd_action current_dnd_action; + uint32_t dnd_actions; + uint32_t compositor_action; + bool actions_set; + + void (*accept)(struct wlr_data_source *source, uint32_t serial, + const char *mime_type); + void (*send)(struct wlr_data_source *source, const char *mime_type, + int32_t fd); + void (*cancel)(struct wlr_data_source *source); + + struct { + struct wl_signal destroy; + } events; +}; + +struct wlr_drag { + struct wlr_seat_pointer_grab pointer_grab; + struct wlr_seat_keyboard_grab keyboard_grab; + + struct wlr_seat_handle *handle; + struct wlr_seat_handle *focus_handle; + + struct wlr_surface *icon; + struct wlr_surface *focus; + struct wlr_data_source *source; + + struct wl_listener icon_destroy; + struct wl_listener source_destroy; + struct wl_listener handle_unbound; +}; + +/** + * Create a wl data device manager global for this display. + */ +struct wlr_data_device_manager *wlr_data_device_manager_create( + struct wl_display *display); + +/** + * Creates a new wl_data_offer if there is a wl_data_source currently set as the + * seat selection and sends it to the client for this handle, followed by the + * wl_data_device.selection() event. + * If there is no current selection, the wl_data_device.selection() event will + * carry a NULL wl_data_offer. + * If the client does not have a wl_data_device for the seat nothing * will be + * done. + */ +void wlr_seat_handle_send_selection(struct wlr_seat_handle *handle); + +void wlr_seat_set_selection(struct wlr_seat *seat, + struct wlr_data_source *source, uint32_t serial); + +#endif diff --git a/include/wlr/types/wlr_data_device_manager.h b/include/wlr/types/wlr_data_device_manager.h deleted file mode 100644 index 500f8acd..00000000 --- a/include/wlr/types/wlr_data_device_manager.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef WLR_TYPES_WLR_DATA_DEVICE_MANAGER_H -#define WLR_TYPES_WLR_DATA_DEVICE_MANAGER_H - -#include <wayland-server.h> - -struct wlr_data_device_manager { - struct wl_global *global; -}; - -struct wlr_data_device_manager *wlr_data_device_manager_create(struct wl_display *dpy); -void wlr_data_device_manager_destroy(struct wlr_data_device_manager *manager); - -struct wlr_data_device { - struct wlr_seat *seat; - struct wlr_data_source *selection; - struct wl_listener selection_destroyed; - - struct { - struct wl_signal selection_change; - } events; -}; - -void wlr_data_device_set_selection(struct wlr_data_device *manager, - struct wlr_data_source *source); - -#endif diff --git a/include/wlr/types/wlr_data_source.h b/include/wlr/types/wlr_data_source.h deleted file mode 100644 index f54ac0a9..00000000 --- a/include/wlr/types/wlr_data_source.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef WLR_TYPES_WLR_DATA_SOURCE_H -#define WLR_TYPES_WLR_DATA_SOURCE_H - -#include <wayland-server.h> -#include <wlr/types/wlr_list.h> - -struct wlr_data_source_impl; - -struct wlr_data_source { - struct wlr_data_source_impl *impl; - struct wlr_list *types; - void *data; - - struct { - struct wl_signal destroy; - } events; -}; - -void wlr_data_source_send(struct wlr_data_source *src, const char *type, int fd); -void wlr_data_source_accepted(struct wlr_data_source *src, const char *type); -void wlr_data_source_cancelled(struct wlr_data_source *src); - -struct wlr_wl_data_source { - struct wlr_data_source base; - struct wl_resource *resource; -}; - -struct wlr_wl_data_source *wlr_wl_data_source_create( - struct wl_client *client, - uint32_t version, uint32_t id); - -#endif diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 6fda89b9..d241175b 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -74,11 +74,16 @@ struct wlr_seat_pointer_state { struct wlr_seat_handle *focused_handle; struct wlr_surface *focused_surface; - struct wl_listener surface_destroy; - struct wl_listener resource_destroy; - struct wlr_seat_pointer_grab *grab; struct wlr_seat_pointer_grab *default_grab; + + uint32_t button_count; + uint32_t grab_button; + uint32_t grab_serial; + uint32_t grab_time; + + struct wl_listener surface_destroy; + struct wl_listener resource_destroy; }; struct wlr_seat_keyboard_state { @@ -104,11 +109,16 @@ struct wlr_seat { struct wl_list handles; char *name; uint32_t capabilities; - struct wlr_data_device *data_device; + + struct wlr_data_device *data_device; // TODO needed? + struct wlr_data_source *selection_source; + uint32_t selection_serial; struct wlr_seat_pointer_state pointer_state; struct wlr_seat_keyboard_state keyboard_state; + struct wl_listener selection_data_source_destroy; + struct { struct wl_signal client_bound; struct wl_signal client_unbound; @@ -120,6 +130,8 @@ struct wlr_seat { struct wl_signal keyboard_grab_end; struct wl_signal request_set_cursor; + + struct wl_signal selection; } events; void *data; diff --git a/include/wlr/types/wlr_server_decoration.h b/include/wlr/types/wlr_server_decoration.h new file mode 100644 index 00000000..b4cac5b7 --- /dev/null +++ b/include/wlr/types/wlr_server_decoration.h @@ -0,0 +1,44 @@ +#ifndef WLR_TYPES_WLR_SERVER_DECORATION_H +#define WLR_TYPES_WLR_SERVER_DECORATION_H + +#include <wayland-server.h> + +struct wlr_server_decoration_manager { + struct wl_global *wl_global; + struct wl_list wl_resources; + struct wl_list decorations; // wlr_server_decoration::link + + uint32_t default_mode; // enum org_kde_kwin_server_decoration_manager_mode + + struct { + struct wl_signal new_decoration; + } events; + + void *data; +}; + +struct wlr_server_decoration { + struct wl_resource *resource; + struct wlr_surface *surface; + struct wl_list link; + + uint32_t mode; // enum org_kde_kwin_server_decoration_manager_mode + + struct { + struct wl_signal destroy; + struct wl_signal mode; + } events; + + struct wl_listener surface_destroy_listener; + + void *data; +}; + +struct wlr_server_decoration_manager *wlr_server_decoration_manager_create( + struct wl_display *display); +void wlr_server_decoration_manager_set_default_mode( + struct wlr_server_decoration_manager *manager, uint32_t default_mode); +void wlr_server_decoration_manager_destroy( + struct wlr_server_decoration_manager *manager); + +#endif diff --git a/meson.build b/meson.build index adf3a1fa..74bb6dcd 100644 --- a/meson.build +++ b/meson.build @@ -51,6 +51,7 @@ udev = dependency('libudev') pixman = dependency('pixman-1') xcb = dependency('xcb') xcb_composite = dependency('xcb-composite') +xcb_xfixes = dependency('xcb-xfixes') xcb_icccm = dependency('xcb-icccm', required: false) x11_xcb = dependency('x11-xcb') libcap = dependency('libcap', required: false) diff --git a/protocol/meson.build b/protocol/meson.build index 2cbb5b6d..ff54815a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -24,12 +24,14 @@ protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], 'gamma-control.xml', 'screenshooter.xml', + 'server-decoration.xml', ] client_protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], 'gamma-control.xml', 'screenshooter.xml', + 'server-decoration.xml', ] wl_protos_src = [] diff --git a/protocol/server-decoration.xml b/protocol/server-decoration.xml new file mode 100644 index 00000000..45f11284 --- /dev/null +++ b/protocol/server-decoration.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="server_decoration"> + <copyright><![CDATA[ + Copyright (C) 2015 Martin Gräßlin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + ]]></copyright> + <interface name="org_kde_kwin_server_decoration_manager" version="1"> + <description summary="Server side window decoration manager"> + This interface allows to coordinate whether the server should create + a server-side window decoration around a wl_surface representing a + shell surface (wl_shell_surface or similar). By announcing support + for this interface the server indicates that it supports server + side decorations. + </description> + <request name="create"> + <description summary="Create a server-side decoration object for a given surface"> + When a client creates a server-side decoration object it indicates + that it supports the protocol. The client is supposed to tell the + server whether it wants server-side decorations or will provide + client-side decorations. + + If the client does not create a server-side decoration object for + a surface the server interprets this as lack of support for this + protocol and considers it as client-side decorated. Nevertheless a + client-side decorated surface should use this protocol to indicate + to the server that it does not want a server-side deco. + </description> + <arg name="id" type="new_id" interface="org_kde_kwin_server_decoration"/> + <arg name="surface" type="object" interface="wl_surface"/> + </request> + <enum name="mode"> + <description summary="Possible values to use in request_mode and the event mode."/> + <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/> + <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/> + <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/> + </enum> + <event name="default_mode"> + <description summary="The default mode used on the server"> + This event is emitted directly after binding the interface. It contains + the default mode for the decoration. When a new server decoration object + is created this new object will be in the default mode until the first + request_mode is requested. + + The server may change the default mode at any time. + </description> + <arg name="mode" type="uint" summary="The default decoration mode applied to newly created server decorations."/> + </event> + </interface> + <interface name="org_kde_kwin_server_decoration" version="1"> + <request name="release" type="destructor"> + <description summary="release the server decoration object"/> + </request> + <enum name="mode"> + <description summary="Possible values to use in request_mode and the event mode."/> + <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/> + <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/> + <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/> + </enum> + <request name="request_mode"> + <description summary="The decoration mode the surface wants to use."/> + <arg name="mode" type="uint" summary="The mode this surface wants to use."/> + </request> + <event name="mode"> + <description summary="The new decoration mode applied by the server"> + This event is emitted directly after the decoration is created and + represents the base decoration policy by the server. E.g. a server + which wants all surfaces to be client-side decorated will send Client, + a server which wants server-side decoration will send Server. + + The client can request a different mode through the decoration request. + The server will acknowledge this by another event with the same mode. So + even if a server prefers server-side decoration it's possible to force a + client-side decoration. + + The server may emit this event at any time. In this case the client can + again request a different mode. It's the responsibility of the server to + prevent a feedback loop. + </description> + <arg name="mode" type="uint" summary="The decoration mode applied to the surface by the server."/> + </event> + </interface> +</protocol> diff --git a/rootston/config.c b/rootston/config.c index c9c892f9..6f81170b 100644 --- a/rootston/config.c +++ b/rootston/config.c @@ -147,8 +147,46 @@ void add_binding_config(struct wl_list *bindings, const char* combination, } } +static void config_handle_keyboard(struct roots_config *config, + const char *device_name, const char *name, const char *value) { + struct keyboard_config *kc; + bool found = false; + wl_list_for_each(kc, &config->keyboards, link) { + if (strcmp(kc->name, device_name) == 0) { + found = true; + break; + } + } + + if (!found) { + kc = calloc(1, sizeof(struct keyboard_config)); + kc->name = strdup(device_name); + wl_list_insert(&config->keyboards, &kc->link); + } + + if (strcmp(name, "meta-key") == 0) { + kc->meta_key = parse_modifier(value); + if (kc->meta_key == 0) { + wlr_log(L_ERROR, "got unknown meta key: %s", name); + } + } else if (strcmp(name, "rules") == 0) { + kc->rules = strdup(value); + } else if (strcmp(name, "model") == 0) { + kc->model = strdup(value); + } else if (strcmp(name, "layout") == 0) { + kc->layout = strdup(value); + } else if (strcmp(name, "variant") == 0) { + kc->variant = strdup(value); + } else if (strcmp(name, "options") == 0) { + kc->options = strdup(value); + } else { + wlr_log(L_ERROR, "got unknown keyboard config: %s", name); + } +} + static const char *output_prefix = "output:"; static const char *device_prefix = "device:"; +static const char *keyboard_prefix = "keyboard:"; static int config_ini_handler(void *user, const char *section, const char *name, const char *value) { @@ -219,9 +257,9 @@ static int config_ini_handler(void *user, const char *section, const char *name, } } else if (strncmp(device_prefix, section, strlen(device_prefix)) == 0) { const char *device_name = section + strlen(device_prefix); + struct device_config *dc; bool found = false; - wl_list_for_each(dc, &config->devices, link) { if (strcmp(dc->name, device_name) == 0) { found = true; @@ -245,14 +283,10 @@ static int config_ini_handler(void *user, const char *section, const char *name, wlr_log(L_ERROR, "got unknown device config: %s", name); } } else if (strcmp(section, "keyboard") == 0) { - if (strcmp(name, "meta-key") == 0) { - config->keyboard.meta_key = parse_modifier(value); - if (config->keyboard.meta_key == 0) { - wlr_log(L_ERROR, "got unknown meta key: %s", name); - } - } else { - wlr_log(L_ERROR, "got unknown keyboard config: %s", name); - } + config_handle_keyboard(config, "", name, value); + } else if (strncmp(keyboard_prefix, section, strlen(keyboard_prefix)) == 0) { + const char *device_name = section + strlen(keyboard_prefix); + config_handle_keyboard(config, device_name, name, value); } else if (strcmp(section, "bindings") == 0) { add_binding_config(&config->bindings, name, value); } else { @@ -271,6 +305,7 @@ struct roots_config *parse_args(int argc, char *argv[]) { config->xwayland = true; wl_list_init(&config->outputs); wl_list_init(&config->devices); + wl_list_init(&config->keyboards); wl_list_init(&config->bindings); int c; @@ -305,10 +340,12 @@ struct roots_config *parse_args(int argc, char *argv[]) { if (result == -1) { wlr_log(L_DEBUG, "No config file found. Using sensible defaults."); - config->keyboard.meta_key = WLR_MODIFIER_LOGO; add_binding_config(&config->bindings, "Logo+Shift+E", "exit"); add_binding_config(&config->bindings, "Ctrl+q", "close"); add_binding_config(&config->bindings, "Alt+Tab", "next_window"); + struct keyboard_config *kc = calloc(1, sizeof(struct keyboard_config)); + kc->meta_key = WLR_MODIFIER_LOGO; + wl_list_insert(&config->keyboards, &kc->link); } else if (result == -2) { wlr_log(L_ERROR, "Could not allocate memory to parse config file"); exit(1); @@ -335,6 +372,17 @@ void roots_config_destroy(struct roots_config *config) { free(dc); } + struct keyboard_config *kc, *ktmp = NULL; + wl_list_for_each_safe(kc, ktmp, &config->bindings, link) { + free(kc->name); + free(kc->rules); + free(kc->model); + free(kc->layout); + free(kc->variant); + free(kc->options); + free(kc); + } + struct binding_config *bc, *btmp = NULL; wl_list_for_each_safe(bc, btmp, &config->bindings, link) { free(bc->keysyms); @@ -371,3 +419,16 @@ struct device_config *config_get_device(struct roots_config *config, return NULL; } + +struct keyboard_config *config_get_keyboard(struct roots_config *config, + struct wlr_input_device *device) { + struct keyboard_config *kc; + wl_list_for_each(kc, &config->keyboards, link) { + if ((device != NULL && strcmp(kc->name, device->name) == 0) || + (device == NULL && strcmp(kc->name, "") == 0)) { + return kc; + } + } + + return NULL; +} diff --git a/rootston/cursor.c b/rootston/cursor.c index 2a3a7c25..27f97724 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -4,6 +4,7 @@ #include <stdint.h> #include <string.h> #include <math.h> +#include <assert.h> #ifdef __linux__ #include <linux/input-event-codes.h> #elif __FreeBSD__ @@ -12,6 +13,7 @@ #include <wayland-server.h> #include <wlr/types/wlr_cursor.h> #include <wlr/util/log.h> +#include <wlr/types/wlr_data_device.h> #include "rootston/config.h" #include "rootston/input.h" #include "rootston/desktop.h" @@ -29,6 +31,28 @@ const struct roots_input_event *get_input_event(struct roots_input *input, return NULL; } +static void cursor_set_xcursor_image(struct roots_input *input, + struct wlr_xcursor_image *image) { + struct roots_output *output; + wl_list_for_each(output, &input->server->desktop->outputs, link) { + if (!wlr_output_set_cursor(output->wlr_output, image->buffer, + image->width, image->width, image->height, + image->hotspot_x, image->hotspot_y)) { + wlr_log(L_DEBUG, "Failed to set hardware cursor"); + return; + } + } +} + +static void cursor_set_surface(struct roots_input *input, + struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y) { + struct roots_output *output; + wl_list_for_each(output, &input->server->desktop->outputs, link) { + wlr_output_set_cursor_surface(output->wlr_output, surface, + hotspot_x, hotspot_y); + } +} + void view_begin_move(struct roots_input *input, struct wlr_cursor *cursor, struct roots_view *view) { input->mode = ROOTS_CURSOR_MOVE; @@ -37,6 +61,11 @@ void view_begin_move(struct roots_input *input, struct wlr_cursor *cursor, input->view_x = view->x; input->view_y = view->y; wlr_seat_pointer_clear_focus(input->wl_seat); + + struct wlr_xcursor *xcursor = get_move_xcursor(input->xcursor_theme); + if (xcursor != NULL) { + cursor_set_xcursor_image(input, xcursor->images[0]); + } } void view_begin_resize(struct roots_input *input, struct wlr_cursor *cursor, @@ -52,6 +81,11 @@ void view_begin_resize(struct roots_input *input, struct wlr_cursor *cursor, input->view_height = size.height; input->resize_edges = edges; wlr_seat_pointer_clear_focus(input->wl_seat); + + struct wlr_xcursor *xcursor = get_resize_xcursor(input->xcursor_theme, edges); + if (xcursor != NULL) { + cursor_set_xcursor_image(input, xcursor->images[0]); + } } void view_begin_rotate(struct roots_input *input, struct wlr_cursor *cursor, @@ -61,18 +95,10 @@ void view_begin_rotate(struct roots_input *input, struct wlr_cursor *cursor, input->offs_y = cursor->y; input->view_rotation = view->rotation; wlr_seat_pointer_clear_focus(input->wl_seat); -} -static void cursor_set_xcursor_image(struct roots_input *input, - struct wlr_xcursor_image *image) { - struct roots_output *output; - wl_list_for_each(output, &input->server->desktop->outputs, link) { - if (!wlr_output_set_cursor(output->wlr_output, image->buffer, - image->width, image->width, image->height, - image->hotspot_x, image->hotspot_y)) { - wlr_log(L_DEBUG, "Failed to set hardware cursor"); - return; - } + struct wlr_xcursor *xcursor = get_rotate_xcursor(input->xcursor_theme); + if (xcursor != NULL) { + cursor_set_xcursor_image(input, xcursor->images[0]); } } @@ -92,8 +118,8 @@ void cursor_update_position(struct roots_input *input, uint32_t time) { set_compositor_cursor = view_client != input->cursor_client; } if (set_compositor_cursor) { - wlr_log(L_DEBUG, "Switching to compositor cursor"); - cursor_set_xcursor_image(input, input->xcursor->images[0]); + struct wlr_xcursor *xcursor = get_default_xcursor(input->xcursor_theme); + cursor_set_xcursor_image(input, xcursor->images[0]); input->cursor_client = NULL; } if (view) { @@ -134,6 +160,13 @@ void cursor_update_position(struct roots_input *input, uint32_t time) { width += dx; } + if (width < 0) { + width = 0; + } + if (height < 0) { + height = 0; + } + // TODO we might need one configure event for this if (active_x != input->active_view->x || active_y != input->active_view->y) { @@ -170,7 +203,6 @@ void set_view_focus(struct roots_input *input, struct roots_desktop *desktop, if (!view) { return; } - input->last_active_view = view; size_t index = 0; for (size_t i = 0; i < desktop->views->length; ++i) { @@ -213,8 +245,16 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) { event->orientation, event->delta); } -static bool is_meta_pressed(struct roots_input *input) { - uint32_t meta_key = input->server->config->keyboard.meta_key; +static bool is_meta_pressed(struct roots_input *input, + struct wlr_input_device *device) { + uint32_t meta_key = 0; + struct keyboard_config *config; + if ((config = config_get_keyboard(input->server->config, device))) { + meta_key = config->meta_key; + } else if (!meta_key && (config = config_get_keyboard(input->server->config, + NULL))) { + meta_key = config->meta_key; + } if (meta_key == 0) { return false; } @@ -239,20 +279,31 @@ static void do_cursor_button_press(struct roots_input *input, struct roots_view *view = view_at(desktop, input->cursor->x, input->cursor->y, &surface, &sx, &sy); - if (state == WLR_BUTTON_PRESSED && view && is_meta_pressed(input)) { + if (state == WLR_BUTTON_PRESSED && view && is_meta_pressed(input, device)) { set_view_focus(input, desktop, view); + uint32_t edges; switch (button) { case BTN_LEFT: view_begin_move(input, cursor, view); break; case BTN_RIGHT: - view_begin_resize(input, cursor, view, - ROOTS_CURSOR_RESIZE_EDGE_RIGHT | - ROOTS_CURSOR_RESIZE_EDGE_BOTTOM); + edges = 0; + if (sx < view->wlr_surface->current->width/2) { + edges |= ROOTS_CURSOR_RESIZE_EDGE_LEFT; + } else { + edges |= ROOTS_CURSOR_RESIZE_EDGE_RIGHT; + } + if (sy < view->wlr_surface->current->height/2) { + edges |= ROOTS_CURSOR_RESIZE_EDGE_TOP; + } else { + edges |= ROOTS_CURSOR_RESIZE_EDGE_BOTTOM; + } + view_begin_resize(input, cursor, view, edges); break; case BTN_MIDDLE: view_begin_rotate(input, cursor, view); + break; } return; } @@ -264,6 +315,7 @@ static void do_cursor_button_press(struct roots_input *input, switch (state) { case WLR_BUTTON_RELEASED: set_view_focus(input, desktop, NULL); + cursor_update_position(input, time); break; case WLR_BUTTON_PRESSED: i = input->input_events_idx; @@ -355,6 +407,58 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { (uint32_t)(event->time_usec / 1000), BTN_LEFT, event->state); } +static void handle_drag_icon_destroy(struct wl_listener *listener, void *data) { + struct roots_drag_icon *drag_icon = + wl_container_of(listener, drag_icon, surface_destroy); + wl_list_remove(&drag_icon->link); + wl_list_remove(&drag_icon->surface_destroy.link); + wl_list_remove(&drag_icon->surface_commit.link); + free(drag_icon); +} + +static void handle_drag_icon_commit(struct wl_listener *listener, void *data) { + struct roots_drag_icon *drag_icon = + wl_container_of(listener, drag_icon, surface_commit); + // TODO the spec hints at rules that can determine whether the drag icon is + // mapped here, but it is not completely clear so we need to test more + // toolkits to see how we should interpret the surface state here. + drag_icon->sx += drag_icon->surface->current->sx; + drag_icon->sy += drag_icon->surface->current->sy; +} + +static void handle_pointer_grab_begin(struct wl_listener *listener, + void *data) { + struct roots_input *input = + wl_container_of(listener, input, pointer_grab_begin); + struct wlr_seat_pointer_grab *grab = data; + + if (grab->interface == &wlr_data_device_pointer_drag_interface) { + struct wlr_drag *drag = grab->data; + if (drag->icon) { + struct roots_drag_icon *iter_icon; + wl_list_for_each(iter_icon, &input->drag_icons, link) { + if (iter_icon->surface == drag->icon) { + // already in the list + return; + } + } + + struct roots_drag_icon *drag_icon = + calloc(1, sizeof(struct roots_drag_icon)); + drag_icon->surface = drag->icon; + wl_list_insert(&input->drag_icons, &drag_icon->link); + + wl_signal_add(&drag->icon->events.destroy, + &drag_icon->surface_destroy); + drag_icon->surface_destroy.notify = handle_drag_icon_destroy; + + wl_signal_add(&drag->icon->events.commit, + &drag_icon->surface_commit); + drag_icon->surface_commit.notify = handle_drag_icon_commit; + } + } +} + static void handle_pointer_grab_end(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, pointer_grab_end); @@ -375,19 +479,13 @@ static void handle_request_set_cursor(struct wl_listener *listener, wl_resource_get_client(focused_surface->resource); ok = event->client == focused_client; } - if (!ok) { + if (!ok || input->mode != ROOTS_CURSOR_PASSTHROUGH) { wlr_log(L_DEBUG, "Denying request to set cursor from unfocused client"); return; } wlr_log(L_DEBUG, "Setting client cursor"); - - struct roots_output *output; - wl_list_for_each(output, &input->server->desktop->outputs, link) { - wlr_output_set_cursor_surface(output->wlr_output, event->surface, - event->hotspot_x, event->hotspot_y); - } - + cursor_set_surface(input, event->surface, event->hotspot_x, event->hotspot_y); input->cursor_client = event->client; } @@ -428,6 +526,11 @@ void cursor_initialize(struct roots_input *input) { wl_signal_add(&input->wl_seat->events.pointer_grab_end, &input->pointer_grab_end); input->pointer_grab_end.notify = handle_pointer_grab_end; + wl_signal_add(&input->wl_seat->events.pointer_grab_begin, &input->pointer_grab_begin); + input->pointer_grab_begin.notify = handle_pointer_grab_begin; + + wl_list_init(&input->request_set_cursor.link); + wl_signal_add(&input->wl_seat->events.request_set_cursor, &input->request_set_cursor); input->request_set_cursor.notify = handle_request_set_cursor; diff --git a/rootston/desktop.c b/rootston/desktop.c index 40d088b8..a1d8a632 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -7,11 +7,13 @@ #include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_gamma_control.h> +#include <wlr/types/wlr_server_decoration.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_wl_shell.h> #include <wlr/types/wlr_xdg_shell_v6.h> #include <wlr/util/log.h> -#include "rootston/desktop.h" +#include <server-decoration-protocol.h> +#include "rootston/server.h" #include "rootston/server.h" void view_destroy(struct roots_view *view) { @@ -22,9 +24,6 @@ void view_destroy(struct roots_view *view) { input->active_view = NULL; input->mode = ROOTS_CURSOR_PASSTHROUGH; } - if (input->last_active_view == view) { - input->last_active_view = NULL; - } for (size_t i = 0; i < desktop->views->length; ++i) { struct roots_view *_view = desktop->views->items[i]; @@ -107,14 +106,26 @@ bool view_center(struct roots_view *view) { return true; } -void view_initialize(struct roots_view *view) { +void view_setup(struct roots_view *view) { view_center(view); - struct roots_input *input = view->desktop->server->input; + struct roots_input *input = view->desktop->server->input; set_view_focus(input, view->desktop, view); wlr_seat_keyboard_notify_enter(input->wl_seat, view->wlr_surface); } +void view_teardown(struct roots_view *view) { + struct wlr_list *views = view->desktop->views; + if (views->length < 2 || views->items[views->length-1] != view) { + return; + } + + struct roots_view *prev_view = views->items[views->length-2]; + struct roots_input *input = prev_view->desktop->server->input; + set_view_focus(input, prev_view->desktop, prev_view); + wlr_seat_keyboard_notify_enter(input->wl_seat, prev_view->wlr_surface); +} + struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { for (int i = desktop->views->length - 1; i >= 0; --i) { @@ -252,6 +263,11 @@ struct roots_desktop *desktop_create(struct roots_server *server, server->wl_display); desktop->screenshooter = wlr_screenshooter_create(server->wl_display, server->renderer); + desktop->server_decoration_manager = + wlr_server_decoration_manager_create(server->wl_display); + wlr_server_decoration_manager_set_default_mode( + desktop->server_decoration_manager, + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT); return desktop; } diff --git a/rootston/input.c b/rootston/input.c index 5dc7d16d..a6792bdb 100644 --- a/rootston/input.c +++ b/rootston/input.c @@ -81,16 +81,17 @@ struct roots_input *input_create(struct roots_server *server, input->config = config; input->server = server; - input->theme = wlr_xcursor_theme_load("default", 16); - if (input->theme == NULL) { + input->xcursor_theme = wlr_xcursor_theme_load("default", 16); + if (input->xcursor_theme == NULL) { wlr_log(L_ERROR, "Cannot load xcursor theme"); free(input); return NULL; } - input->xcursor = wlr_xcursor_theme_get_cursor(input->theme, "left_ptr"); - if (input->xcursor == NULL) { + + struct wlr_xcursor *xcursor = get_default_xcursor(input->xcursor_theme); + if (xcursor == NULL) { wlr_log(L_ERROR, "Cannot load xcursor from theme"); - wlr_xcursor_theme_destroy(input->theme); + wlr_xcursor_theme_destroy(input->xcursor_theme); free(input); return NULL; } @@ -98,7 +99,7 @@ struct roots_input *input_create(struct roots_server *server, input->wl_seat = wlr_seat_create(server->wl_display, "seat0"); if (input->wl_seat == NULL) { wlr_log(L_ERROR, "Cannot create seat"); - wlr_xcursor_theme_destroy(input->theme); + wlr_xcursor_theme_destroy(input->xcursor_theme); free(input); return NULL; } @@ -117,13 +118,15 @@ struct roots_input *input_create(struct roots_server *server, input->cursor = wlr_cursor_create(); cursor_initialize(input); - wlr_cursor_set_xcursor(input->cursor, input->xcursor); + wlr_cursor_set_xcursor(input->cursor, xcursor); wlr_cursor_attach_output_layout(input->cursor, server->desktop->layout); wlr_cursor_map_to_region(input->cursor, config->cursor.mapped_box); cursor_load_config(config, input->cursor, input, server->desktop); + wl_list_init(&input->drag_icons); + return input; } diff --git a/rootston/keyboard.c b/rootston/keyboard.c index 1d4699a9..a0601d05 100644 --- a/rootston/keyboard.c +++ b/rootston/keyboard.c @@ -29,8 +29,10 @@ static void keyboard_binding_execute(struct roots_keyboard *keyboard, if (strcmp(command, "exit") == 0) { wl_display_terminate(server->wl_display); } else if (strcmp(command, "close") == 0) { - if (keyboard->input->last_active_view != NULL) { - view_close(keyboard->input->last_active_view); + if (server->desktop->views->length > 0) { + struct roots_view *view = + server->desktop->views->items[server->desktop->views->length-1]; + view_close(view); } } else if (strcmp(command, "next_window") == 0) { if (server->desktop->views->length > 0) { @@ -157,6 +159,28 @@ static void keyboard_modifiers_notify(struct wl_listener *listener, void *data) } +static void keyboard_config_merge(struct keyboard_config *config, + struct keyboard_config *fallback) { + if (fallback == NULL) { + return; + } + if (config->rules == NULL) { + config->rules = fallback->rules; + } + if (config->model == NULL) { + config->model = fallback->model; + } + if (config->layout == NULL) { + config->layout = fallback->layout; + } + if (config->variant == NULL) { + config->variant = fallback->variant; + } + if (config->options == NULL) { + config->options = fallback->options; + } +} + void keyboard_add(struct wlr_input_device *device, struct roots_input *input) { struct roots_keyboard *keyboard = calloc(sizeof(struct roots_keyboard), 1); if (keyboard == NULL) { @@ -174,13 +198,27 @@ void keyboard_add(struct wlr_input_device *device, struct roots_input *input) { wl_list_insert(&input->keyboards, &keyboard->link); + struct keyboard_config config; + memset(&config, 0, sizeof(config)); + keyboard_config_merge(&config, config_get_keyboard(input->config, device)); + keyboard_config_merge(&config, config_get_keyboard(input->config, NULL)); + + struct keyboard_config env_config = { + .rules = getenv("XKB_DEFAULT_RULES"), + .model = getenv("XKB_DEFAULT_MODEL"), + .layout = getenv("XKB_DEFAULT_LAYOUT"), + .variant = getenv("XKB_DEFAULT_VARIANT"), + .options = getenv("XKB_DEFAULT_OPTIONS"), + }; + keyboard_config_merge(&config, &env_config); + struct xkb_rule_names rules; memset(&rules, 0, sizeof(rules)); - rules.rules = getenv("XKB_DEFAULT_RULES"); - rules.model = getenv("XKB_DEFAULT_MODEL"); - rules.layout = getenv("XKB_DEFAULT_LAYOUT"); - rules.variant = getenv("XKB_DEFAULT_VARIANT"); - rules.options = getenv("XKB_DEFAULT_OPTIONS"); + rules.rules = config.rules; + rules.model = config.model; + rules.layout = config.layout; + rules.variant = config.variant; + rules.options = config.options; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (context == NULL) { wlr_log(L_ERROR, "Cannot create XKB context"); diff --git a/rootston/main.c b/rootston/main.c index b80c2efd..2a054e6c 100644 --- a/rootston/main.c +++ b/rootston/main.c @@ -20,11 +20,11 @@ int main(int argc, char **argv) { assert(server.backend = wlr_backend_autocreate(server.wl_display)); assert(server.renderer = wlr_gles2_renderer_create(server.backend)); + server.data_device_manager = + wlr_data_device_manager_create(server.wl_display); wl_display_init_shm(server.wl_display); server.desktop = desktop_create(&server, server.config); server.input = input_create(&server, server.config); - server.data_device_manager = wlr_data_device_manager_create( - server.wl_display); const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { diff --git a/rootston/meson.build b/rootston/meson.build index f2621450..d84422d8 100644 --- a/rootston/meson.build +++ b/rootston/meson.build @@ -10,6 +10,7 @@ sources = [ 'pointer.c', 'tablet_tool.c', 'touch.c', + 'xcursor.c', 'xdg_shell_v6.c', 'wl_shell.c', ] @@ -17,5 +18,5 @@ if get_option('enable_xwayland') sources += ['xwayland.c'] endif executable( - 'rootston', sources, dependencies: wlroots + 'rootston', sources, dependencies: [wlroots, wlr_protos] ) diff --git a/rootston/output.c b/rootston/output.c index 99f70bcf..5fcd02a2 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -150,6 +150,15 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { render_view(view, desktop, wlr_output, &now); } + struct roots_drag_icon *drag_icon = NULL; + wl_list_for_each(drag_icon, &server->input->drag_icons, link) { + struct wlr_surface *icon = drag_icon->surface; + struct wlr_cursor *cursor = server->input->cursor; + double icon_x = cursor->x + drag_icon->sx; + double icon_y = cursor->y + drag_icon->sy; + render_surface(icon, desktop, wlr_output, &now, icon_x, icon_y, 0); + } + wlr_renderer_end(server->renderer); wlr_output_swap_buffers(wlr_output); @@ -191,7 +200,8 @@ void output_add_notify(struct wl_listener *listener, void *data) { cursor_load_config(config, input->cursor, input, desktop); - struct wlr_xcursor_image *image = input->xcursor->images[0]; + struct wlr_xcursor *xcursor = get_default_xcursor(input->xcursor_theme); + struct wlr_xcursor_image *image = xcursor->images[0]; // TODO the cursor must be set depending on which surface it is displayed // over which should happen in the compositor. if (!wlr_output_set_cursor(wlr_output, image->buffer, diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index 88397af8..e5366672 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -56,6 +56,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) { struct roots_wl_shell_surface *roots_surface = wl_container_of(listener, roots_surface, destroy); + view_teardown(roots_surface->view); wl_list_remove(&roots_surface->destroy.link); wl_list_remove(&roots_surface->request_move.link); wl_list_remove(&roots_surface->request_resize.link); @@ -111,7 +112,7 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { view->desktop = desktop; roots_surface->view = view; wlr_list_add(desktop->views, view); - view_initialize(view); + view_setup(view); if (surface->state == WLR_WL_SHELL_SURFACE_STATE_TRANSIENT) { // we need to map it relative to the parent diff --git a/rootston/xcursor.c b/rootston/xcursor.c new file mode 100644 index 00000000..43cbfc51 --- /dev/null +++ b/rootston/xcursor.c @@ -0,0 +1,42 @@ +#include <wlr/types/wlr_cursor.h> +#include "rootston/input.h" + +struct wlr_xcursor *get_default_xcursor(struct wlr_xcursor_theme *theme) { + return wlr_xcursor_theme_get_cursor(theme, "left_ptr"); +} + +struct wlr_xcursor *get_move_xcursor(struct wlr_xcursor_theme *theme) { + return wlr_xcursor_theme_get_cursor(theme, "grabbing"); +} + +static const char *get_resize_xcursor_name(uint32_t edges) { + if (edges & ROOTS_CURSOR_RESIZE_EDGE_TOP) { + if (edges & ROOTS_CURSOR_RESIZE_EDGE_RIGHT) { + return "ne-resize"; + } else if (edges & ROOTS_CURSOR_RESIZE_EDGE_LEFT) { + return "nw-resize"; + } + return "n-resize"; + } else if (edges & ROOTS_CURSOR_RESIZE_EDGE_BOTTOM) { + if (edges & ROOTS_CURSOR_RESIZE_EDGE_RIGHT) { + return "se-resize"; + } else if (edges & ROOTS_CURSOR_RESIZE_EDGE_LEFT) { + return "sw-resize"; + } + return "s-resize"; + } else if (edges & ROOTS_CURSOR_RESIZE_EDGE_RIGHT) { + return "e-resize"; + } else if (edges & ROOTS_CURSOR_RESIZE_EDGE_LEFT) { + return "w-resize"; + } + return "se-resize"; // fallback +} + +struct wlr_xcursor *get_resize_xcursor(struct wlr_xcursor_theme *theme, + uint32_t edges) { + return wlr_xcursor_theme_get_cursor(theme, get_resize_xcursor_name(edges)); +} + +struct wlr_xcursor *get_rotate_xcursor(struct wlr_xcursor_theme *theme) { + return wlr_xcursor_theme_get_cursor(theme, "grabbing"); +} diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index 95b20a8b..4f284851 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -29,6 +29,21 @@ static void resize(struct roots_view *view, uint32_t width, uint32_t height) { assert(view->type == ROOTS_XDG_SHELL_V6_VIEW); struct wlr_xdg_surface_v6 *surf = view->xdg_surface_v6; if (surf->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + struct wlr_xdg_toplevel_v6_state *state = + &surf->toplevel_state->current; + if (width < state->min_width) { + width = state->min_width; + } else if (state->max_width > 0 && + width < state->max_width) { + width = state->max_width; + } + if (height < state->min_height) { + height = state->min_height; + } else if (state->max_height > 0 && + height < state->max_height) { + height = state->max_height; + } + wlr_xdg_toplevel_v6_set_size(surf, width, height); } } @@ -74,6 +89,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) { struct roots_xdg_surface_v6 *roots_xdg_surface = wl_container_of(listener, roots_xdg_surface, destroy); + view_teardown(roots_xdg_surface->view); wl_list_remove(&roots_xdg_surface->commit.link); wl_list_remove(&roots_xdg_surface->destroy.link); wl_list_remove(&roots_xdg_surface->request_move.link); @@ -126,5 +142,5 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { roots_surface->view = view; wlr_list_add(desktop->views, view); - view_initialize(view); + view_setup(view); } diff --git a/rootston/xwayland.c b/rootston/xwayland.c index 1149b966..e1b9d227 100644 --- a/rootston/xwayland.c +++ b/rootston/xwayland.c @@ -22,6 +22,24 @@ static void activate(struct roots_view *view, bool active) { static void resize(struct roots_view *view, uint32_t width, uint32_t height) { assert(view->type == ROOTS_XWAYLAND_VIEW); struct wlr_xwayland_surface *xwayland_surface = view->xwayland_surface; + + struct wlr_xwayland_surface_size_hints *size_hints = + xwayland_surface->size_hints; + if (size_hints != NULL) { + if (width < (uint32_t)size_hints->min_width) { + width = size_hints->min_width; + } else if (size_hints->max_width > 0 && + width > (uint32_t)size_hints->max_width) { + width = size_hints->max_width; + } + if (height < (uint32_t)size_hints->min_height) { + height = size_hints->min_height; + } else if (size_hints->max_height > 0 && + height > (uint32_t)size_hints->max_height) { + height = size_hints->max_height; + } + } + wlr_xwayland_surface_configure(view->desktop->xwayland, xwayland_surface, xwayland_surface->x, xwayland_surface->y, width, height); } @@ -43,6 +61,7 @@ static void close(struct roots_view *view) { static void handle_destroy(struct wl_listener *listener, void *data) { struct roots_xwayland_surface *roots_surface = wl_container_of(listener, roots_surface, destroy); + view_teardown(roots_surface->view); wl_list_remove(&roots_surface->destroy.link); view_destroy(roots_surface->view); free(roots_surface); @@ -101,6 +120,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { wlr_list_add(desktop->views, view); if (!surface->override_redirect) { - view_initialize(view); + view_setup(view); } } diff --git a/types/meson.build b/types/meson.build index bd71dac6..2603ecfd 100644 --- a/types/meson.build +++ b/types/meson.build @@ -1,11 +1,10 @@ lib_wlr_types = static_library( 'wlr_types', files( + 'wlr_data_device.c', 'wlr_box.c', 'wlr_compositor.c', 'wlr_cursor.c', - 'wlr_data_device_manager.c', - 'wlr_data_source.c', 'wlr_gamma_control.c', 'wlr_input_device.c', 'wlr_keyboard.c', @@ -16,6 +15,7 @@ lib_wlr_types = static_library( 'wlr_region.c', 'wlr_screenshooter.c', 'wlr_seat.c', + 'wlr_server_decoration.c', 'wlr_surface.c', 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c new file mode 100644 index 00000000..8902c894 --- /dev/null +++ b/types/wlr_data_device.c @@ -0,0 +1,812 @@ +#define _XOPEN_SOURCE 700 +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <assert.h> +#include <unistd.h> +#include <wayland-server.h> +#include <wlr/util/log.h> +#include <wlr/types/wlr_seat.h> +#include <wlr/types/wlr_data_device.h> + +#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ + WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ + WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) + +static uint32_t data_offer_choose_action(struct wlr_data_offer *offer) { + uint32_t available_actions, preferred_action = 0; + uint32_t source_actions, offer_actions; + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + offer_actions = offer->dnd_actions; + preferred_action = offer->preferred_dnd_action; + } else { + offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + if (wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + source_actions = offer->source->dnd_actions; + } else { + source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + available_actions = offer_actions & source_actions; + + if (!available_actions) { + return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + } + + if (offer->source->seat && + offer->source->compositor_action & available_actions) { + return offer->source->compositor_action; + } + + // If the dest side has a preferred DnD action, use it + if ((preferred_action & available_actions) != 0) { + return preferred_action; + } + + // Use the first found action, in bit order + return 1 << (ffs(available_actions) - 1); +} + +static void data_offer_update_action(struct wlr_data_offer *offer) { + uint32_t action; + + if (!offer->source) { + return; + } + + action = data_offer_choose_action(offer); + + if (offer->source->current_dnd_action == action) { + return; + } + + offer->source->current_dnd_action = action; + + if (offer->in_ask) { + return; + } + + if (wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + wl_data_source_send_action(offer->source->resource, action); + } + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + wl_data_offer_send_action(offer->resource, action); + } +} + +static void client_data_source_accept(struct wlr_data_source *source, + uint32_t serial, const char *mime_type) { + wl_data_source_send_target(source->resource, mime_type); +} + +static void client_data_source_send(struct wlr_data_source *source, + const char *mime_type, int32_t fd) { + wl_data_source_send_send(source->resource, mime_type, fd); + close(fd); +} + +static void client_data_source_cancel(struct wlr_data_source *source) { + wl_data_source_send_cancelled(source->resource); +} + + +static void data_offer_accept(struct wl_client *client, + struct wl_resource *resource, uint32_t serial, const char *mime_type) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (!offer->source || offer != offer->source->offer) { + return; + } + + // TODO check that client is currently focused by the input device + + offer->source->accept(offer->source, serial, mime_type); + offer->source->accepted = (mime_type != NULL); +} + +static void data_offer_receive(struct wl_client *client, + struct wl_resource *resource, const char *mime_type, int32_t fd) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (offer->source && offer == offer->source->offer) { + offer->source->send(offer->source, mime_type, fd); + } else { + close(fd); + } +} +static void data_offer_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void data_source_notify_finish(struct wlr_data_source *source) { + if (!source->actions_set) { + return; + } + + if (source->offer->in_ask && wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + wl_data_source_send_action(source->resource, + source->current_dnd_action); + } + + if (wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_dnd_finished(source->resource); + } + + source->offer = NULL; +} + +static void data_offer_finish(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (!offer->source || offer->source->offer != offer) { + return; + } + + data_source_notify_finish(offer->source); +} + +static void data_offer_set_actions(struct wl_client *client, + struct wl_resource *resource, uint32_t dnd_actions, + uint32_t preferred_action) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (dnd_actions & ~ALL_ACTIONS) { + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, + "invalid action mask %x", dnd_actions); + return; + } + + if (preferred_action && (!(preferred_action & dnd_actions) || + __builtin_popcount(preferred_action) > 1)) { + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_ACTION, + "invalid action %x", preferred_action); + return; + } + + offer->dnd_actions = dnd_actions; + offer->preferred_dnd_action = preferred_action; + + data_offer_update_action(offer); +} + +static void data_offer_resource_destroy(struct wl_resource *resource) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (!offer->source) { + goto out; + } + + wl_list_remove(&offer->source_destroy.link); + + if (offer->source->offer != offer) { + goto out; + } + + // If the drag destination has version < 3, wl_data_offer.finish + // won't be called, so do this here as a safety net, because + // we still want the version >=3 drag source to be happy. + if (wl_resource_get_version(offer->resource) < + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + data_source_notify_finish(offer->source); + } else if (offer->source->resource && + wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_cancelled(offer->source->resource); + } + + offer->source->offer = NULL; +out: + free(offer); +} + +static const struct wl_data_offer_interface data_offer_impl = { + .accept = data_offer_accept, + .receive = data_offer_receive, + .destroy = data_offer_destroy, + .finish = data_offer_finish, + .set_actions = data_offer_set_actions, +}; + +static void handle_offer_source_destroyed(struct wl_listener *listener, + void *data) { + struct wlr_data_offer *offer = + wl_container_of(listener, offer, source_destroy); + + offer->source = NULL; +} + +static struct wlr_data_offer *wlr_data_source_send_offer( + struct wlr_data_source *source, + struct wl_resource *target) { + struct wlr_data_offer *offer = calloc(1, sizeof(struct wlr_data_offer)); + + offer->resource = + wl_resource_create(wl_resource_get_client(target), + &wl_data_offer_interface, + wl_resource_get_version(target), 0); + if (offer->resource == NULL) { + free(offer); + return NULL; + } + + wl_resource_set_implementation(offer->resource, &data_offer_impl, offer, + data_offer_resource_destroy); + + offer->source_destroy.notify = handle_offer_source_destroyed; + wl_signal_add(&source->events.destroy, &offer->source_destroy); + + wl_data_device_send_data_offer(target, offer->resource); + char **p; + wl_array_for_each(p, &source->mime_types) { + wl_data_offer_send_offer(offer->resource, *p); + } + + offer->source = source; + source->offer = offer; + source->accepted = false; + + return offer; +} + + +void wlr_seat_handle_send_selection(struct wlr_seat_handle *handle) { + if (!handle->data_device) { + return; + } + + if (handle->wlr_seat->selection_source) { + struct wlr_data_offer *offer = + wlr_data_source_send_offer(handle->wlr_seat->selection_source, + handle->data_device); + wl_data_device_send_selection(handle->data_device, offer->resource); + } else { + wl_data_device_send_selection(handle->data_device, NULL); + } +} + +static void seat_handle_selection_data_source_destroy( + struct wl_listener *listener, void *data) { + struct wlr_seat *seat = + wl_container_of(listener, seat, selection_data_source_destroy); + + if (seat->keyboard_state.focused_handle && + seat->keyboard_state.focused_surface && + seat->keyboard_state.focused_handle->data_device) { + wl_data_device_send_selection( + seat->keyboard_state.focused_handle->data_device, NULL); + } + + seat->selection_source = NULL; + + wl_signal_emit(&seat->events.selection, seat); +} + +void wlr_seat_set_selection(struct wlr_seat *seat, + struct wlr_data_source *source, uint32_t serial) { + if (seat->selection_source && + seat->selection_serial - serial < UINT32_MAX / 2) { + return; + } + + if (seat->selection_source) { + seat->selection_source->cancel(seat->selection_source); + seat->selection_source = NULL; + wl_list_remove(&seat->selection_data_source_destroy.link); + } + + seat->selection_source = source; + seat->selection_serial = serial; + + struct wlr_seat_handle *focused_handle = + seat->keyboard_state.focused_handle; + + if (focused_handle) { + wlr_seat_handle_send_selection(focused_handle); + } + + wl_signal_emit(&seat->events.selection, seat); + + if (source) { + seat->selection_data_source_destroy.notify = + seat_handle_selection_data_source_destroy; + wl_signal_add(&source->events.destroy, + &seat->selection_data_source_destroy); + } +} + +static void data_device_set_selection(struct wl_client *client, + struct wl_resource *seat_resource, struct wl_resource *source_resource, + uint32_t serial) { + if (!source_resource) { + return; + } + + struct wlr_data_source *source = wl_resource_get_user_data(source_resource); + struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); + + // TODO: store serial and check against incoming serial here + wlr_seat_set_selection(handle->wlr_seat, source, serial); +} + +static void data_device_release(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void drag_handle_seat_unbound(struct wl_listener *listener, void *data) { + struct wlr_drag *drag = wl_container_of(listener, drag, handle_unbound); + struct wlr_seat_handle *unbound_handle = data; + + if (drag->focus_handle == unbound_handle) { + drag->focus_handle = NULL; + wl_list_remove(&drag->handle_unbound.link); + } +} + +static void wlr_drag_set_focus(struct wlr_drag *drag, + struct wlr_surface *surface, double sx, double sy) { + if (drag->focus == surface) { + return; + } + + if (drag->focus_handle && drag->focus_handle->data_device) { + wl_list_remove(&drag->handle_unbound.link); + wl_data_device_send_leave(drag->focus_handle->data_device); + drag->focus_handle = NULL; + drag->focus = NULL; + } + + if (!surface || !surface->resource) { + return; + } + + if (!drag->source && + wl_resource_get_client(surface->resource) != + wl_resource_get_client(drag->handle->wl_resource)) { + return; + } + + if (drag->source && drag->source->offer) { + // unlink the offer from the source + wl_list_remove(&drag->source->offer->source_destroy.link); + drag->source->offer->source = NULL; + drag->source->offer = NULL; + } + + struct wlr_seat_handle *focus_handle = + wlr_seat_handle_for_client(drag->handle->wlr_seat, + wl_resource_get_client(surface->resource)); + + if (!focus_handle || !focus_handle->data_device) { + return; + } + + struct wl_resource *offer_resource = NULL; + if (drag->source) { + drag->source->accepted = false; + struct wlr_data_offer *offer = + wlr_data_source_send_offer(drag->source, focus_handle->data_device); + if (offer == NULL) { + return; + } + + data_offer_update_action(offer); + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { + wl_data_offer_send_source_actions(offer->resource, + drag->source->dnd_actions); + } + + offer_resource = offer->resource; + } + + uint32_t serial = wl_display_next_serial(drag->handle->wlr_seat->display); + + wl_data_device_send_enter(focus_handle->data_device, serial, + surface->resource, wl_fixed_from_double(sx), + wl_fixed_from_double(sy), offer_resource); + + drag->focus = surface; + drag->focus_handle = focus_handle; + drag->handle_unbound.notify = drag_handle_seat_unbound; + wl_signal_add(&focus_handle->wlr_seat->events.client_unbound, + &drag->handle_unbound); +} + +static void wlr_drag_end(struct wlr_drag *drag) { + if (drag->icon) { + wl_list_remove(&drag->icon_destroy.link); + } + + if (drag->source) { + wl_list_remove(&drag->source_destroy.link); + } + + wlr_drag_set_focus(drag, NULL, 0, 0); + wlr_seat_pointer_end_grab(drag->pointer_grab.seat); + wlr_seat_keyboard_end_grab(drag->keyboard_grab.seat); + free(drag); +} + +static void pointer_drag_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_drag *drag = grab->data; + wlr_drag_set_focus(drag, surface, sx, sy); +} + +static void pointer_drag_motion(struct wlr_seat_pointer_grab *grab, + uint32_t time, double sx, double sy) { + struct wlr_drag *drag = grab->data; + if (drag->focus && drag->focus_handle && drag->focus_handle->data_device) { + wl_data_device_send_motion(drag->focus_handle->data_device, time, + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + } +} + +static uint32_t pointer_drag_button(struct wlr_seat_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state) { + struct wlr_drag *drag = grab->data; + + if (drag->source && + grab->seat->pointer_state.grab_button == button && + state == WL_POINTER_BUTTON_STATE_RELEASED) { + if (drag->focus_handle && drag->focus_handle->data_device && + drag->source->current_dnd_action && + drag->source->accepted) { + wl_data_device_send_drop(drag->focus_handle->data_device); + if (wl_resource_get_version(drag->source->resource) >= + WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { + wl_data_source_send_dnd_drop_performed( + drag->source->resource); + } + + drag->source->offer->in_ask = + drag->source->current_dnd_action == + WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + } else if (wl_resource_get_version(drag->source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_cancelled(drag->source->resource); + } + } + + if (grab->seat->pointer_state.button_count == 0 && + state == WL_POINTER_BUTTON_STATE_RELEASED) { + wlr_drag_end(drag); + } + + return 0; +} + +static void pointer_drag_axis(struct wlr_seat_pointer_grab *grab, uint32_t time, + enum wlr_axis_orientation orientation, double value) { +} + +static void pointer_drag_cancel(struct wlr_seat_pointer_grab *grab) { + struct wlr_drag *drag = grab->data; + wlr_drag_end(drag); +} + +const struct +wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface = { + .enter = pointer_drag_enter, + .motion = pointer_drag_motion, + .button = pointer_drag_button, + .axis = pointer_drag_axis, + .cancel = pointer_drag_cancel, +}; + +static void keyboard_drag_enter(struct wlr_seat_keyboard_grab *grab, + struct wlr_surface *surface) { + // nothing has keyboard focus during drags +} + +static void keyboard_drag_key(struct wlr_seat_keyboard_grab *grab, + uint32_t time, uint32_t key, uint32_t state) { + // no keyboard input during drags +} + +static void keyboard_drag_modifiers(struct wlr_seat_keyboard_grab *grab, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + // TODO change the dnd action based on what modifier is pressed on the + // keyboard +} + +static void keyboard_drag_cancel(struct wlr_seat_keyboard_grab *grab) { + struct wlr_drag *drag = grab->data; + wlr_drag_end(drag); +} + +const struct +wlr_keyboard_grab_interface wlr_data_device_keyboard_drag_interface = { + .enter = keyboard_drag_enter, + .key = keyboard_drag_key, + .modifiers = keyboard_drag_modifiers, + .cancel = keyboard_drag_cancel, +}; + +static void drag_handle_icon_destroy(struct wl_listener *listener, void *data) { + struct wlr_drag *drag = wl_container_of(listener, drag, icon_destroy); + drag->icon = NULL; +} + +static void drag_handle_drag_source_destroy(struct wl_listener *listener, + void *data) { + struct wlr_drag *drag = wl_container_of(listener, drag, source_destroy); + wlr_drag_end(drag); +} + +static bool seat_handle_start_drag(struct wlr_seat_handle *handle, + struct wlr_data_source *source, struct wlr_surface *icon) { + struct wlr_drag *drag = calloc(1, sizeof(struct wlr_drag)); + if (drag == NULL) { + return false; + } + + struct wlr_seat *seat = handle->wlr_seat; + + if (icon) { + drag->icon = icon; + drag->icon_destroy.notify = drag_handle_icon_destroy; + wl_signal_add(&icon->events.destroy, &drag->icon_destroy); + drag->icon = icon; + } + + if (source) { + drag->source_destroy.notify = drag_handle_drag_source_destroy; + wl_signal_add(&source->events.destroy, &drag->source_destroy); + drag->source = source; + } + + drag->handle = handle; + drag->pointer_grab.data = drag; + drag->pointer_grab.interface = &wlr_data_device_pointer_drag_interface; + + drag->keyboard_grab.data = drag; + drag->keyboard_grab.interface = &wlr_data_device_keyboard_drag_interface; + + wlr_seat_pointer_clear_focus(seat); + + wlr_seat_keyboard_start_grab(seat, &drag->keyboard_grab); + wlr_seat_pointer_start_grab(seat, &drag->pointer_grab); + + // TODO keyboard grab + + return true; +} + +static void data_device_start_drag(struct wl_client *client, + struct wl_resource *handle_resource, + struct wl_resource *source_resource, + struct wl_resource *origin_resource, struct wl_resource *icon_resource, + uint32_t serial) { + struct wlr_seat_handle *handle = wl_resource_get_user_data(handle_resource); + struct wlr_seat *seat = handle->wlr_seat; + struct wlr_surface *origin = wl_resource_get_user_data(origin_resource); + struct wlr_data_source *source = NULL; + struct wlr_surface *icon = NULL; + + bool is_pointer_grab = seat->pointer_state.button_count == 1 && + seat->pointer_state.grab_serial == serial && + seat->pointer_state.focused_surface && + seat->pointer_state.focused_surface == origin; + + if (!is_pointer_grab) { + return; + } + + if (source_resource) { + source = wl_resource_get_user_data(source_resource); + } + + if (icon_resource) { + icon = wl_resource_get_user_data(icon_resource); + } + if (icon) { + if (wlr_surface_set_role(icon, "wl_data_device-icon", + handle_resource, WL_DATA_DEVICE_ERROR_ROLE) < 0) { + return; + } + } + + // TODO touch grab + + if (!seat_handle_start_drag(handle, source, icon)) { + wl_resource_post_no_memory(handle_resource); + } else { + source->seat = handle; + } +} + +static const struct wl_data_device_interface data_device_impl = { + .start_drag = data_device_start_drag, + .set_selection = data_device_set_selection, + .release = data_device_release, +}; + +void data_device_manager_get_data_device(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *seat_resource) { + struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); + + struct wl_resource *resource = + wl_resource_create(client, + &wl_data_device_interface, + wl_resource_get_version(manager_resource), id); + if (resource == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + if (handle->data_device != NULL) { + // XXX this is probably a protocol violation, but it simplfies our code + // and it's stupid to create several data devices for the same seat. + wl_resource_destroy(handle->data_device); + } + + handle->data_device = resource; + + wl_resource_set_implementation(resource, &data_device_impl, + handle, NULL); +} + +static void data_source_resource_destroy(struct wl_resource *resource) { + struct wlr_data_source *source = + wl_resource_get_user_data(resource); + char **p; + + wl_signal_emit(&source->events.destroy, source); + + wl_array_for_each(p, &source->mime_types) { + free(*p); + } + + wl_array_release(&source->mime_types); + + free(source); +} + +static void data_source_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void data_source_set_actions(struct wl_client *client, + struct wl_resource *resource, uint32_t dnd_actions) { + struct wlr_data_source *source = + wl_resource_get_user_data(resource); + + if (source->actions_set) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "cannot set actions more than once"); + return; + } + + if (dnd_actions & ~ALL_ACTIONS) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "invalid action mask %x", dnd_actions); + return; + } + + if (source->seat) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "invalid action change after " + "wl_data_device.start_drag"); + return; + } + + source->dnd_actions = dnd_actions; + source->actions_set = true; +} + +static void data_source_offer(struct wl_client *client, + struct wl_resource *resource, const char *mime_type) { + struct wlr_data_source *source = + wl_resource_get_user_data(resource); + char **p; + + p = wl_array_add(&source->mime_types, sizeof *p); + + if (p) { + *p = strdup(mime_type); + } + if (!p || !*p){ + wl_resource_post_no_memory(resource); + } +} + +static struct wl_data_source_interface data_source_impl = { + .offer = data_source_offer, + .destroy = data_source_destroy, + .set_actions = data_source_set_actions, +}; + +static void data_device_manager_create_data_source(struct wl_client *client, + struct wl_resource *resource, uint32_t id) { + struct wlr_data_source *source = calloc(1, sizeof(struct wlr_data_source)); + if (source == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + source->resource = + wl_resource_create(client, &wl_data_source_interface, + wl_resource_get_version(resource), id); + if (source->resource == NULL) { + free(source); + wl_resource_post_no_memory(resource); + return; + } + + source->accept = client_data_source_accept; + source->send = client_data_source_send; + source->cancel = client_data_source_cancel; + + wl_array_init(&source->mime_types); + wl_signal_init(&source->events.destroy); + + wl_resource_set_implementation(source->resource, &data_source_impl, + source, data_source_resource_destroy); +} + +static const struct wl_data_device_manager_interface +data_device_manager_impl = { + .create_data_source = data_device_manager_create_data_source, + .get_data_device = data_device_manager_get_data_device, +}; + +static void data_device_manager_bind(struct wl_client *client, + void *data, uint32_t version, uint32_t id) { + struct wl_resource *resource; + + resource = wl_resource_create(client, + &wl_data_device_manager_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, + &data_device_manager_impl, NULL, NULL); +} + +struct wlr_data_device_manager *wlr_data_device_manager_create( + struct wl_display *display) { + struct wlr_data_device_manager *manager = + calloc(1, sizeof(struct wlr_data_device_manager)); + if (manager == NULL) { + wlr_log(L_ERROR, "could not create data device manager"); + return NULL; + } + + manager->global = + wl_global_create(display, &wl_data_device_manager_interface, + 3, NULL, data_device_manager_bind); + + if (!manager->global) { + wlr_log(L_ERROR, "could not create data device manager wl global"); + free(manager); + return NULL; + } + + return manager; +} diff --git a/types/wlr_data_device_manager.c b/types/wlr_data_device_manager.c deleted file mode 100644 index a390e5a2..00000000 --- a/types/wlr_data_device_manager.c +++ /dev/null @@ -1,193 +0,0 @@ -#include <assert.h> -#include <stdlib.h> -#include <string.h> -#include <wayland-server.h> -#include <wlr/util/log.h> -#include <wlr/types/wlr_data_device_manager.h> -#include <wlr/types/wlr_data_source.h> -#include <wlr/types/wlr_seat.h> - -static void resource_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void data_device_start_drag(struct wl_client *client, - struct wl_resource *res, struct wl_resource *source_resource, - struct wl_resource *origin_res, struct wl_resource *icon_res, - uint32_t serial) { - wlr_log(L_DEBUG, "TODO: implement data_device:start_drag"); - - // Will probably look like this: - // struct wlr_seat_handle *handle = wl_resource_get_user_data(res); - // struct wlr_data_device *device = handle->wlr_seat->data_device; - // struct wlr_data_source *src = wl_resource_get_user_data(src_res); - // struct wlr_surface *origin = wl_resource_get_user_data(origin_res); - // struct wlr_surface *icon; - // if (icon_res) - // icon = wl_resource_get_user_data(icon_res); - // wlr_seat_start_drag(serial, device->seat, src, - // origin, icon); // will set surface roles and emit signal for user -} - -static void data_device_set_selection(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *source_resource, - uint32_t serial) { - if (!source_resource) { - return; - } - - // TODO: serial validation - struct wlr_seat_handle *handle = wl_resource_get_user_data(resource); - struct wlr_data_device *device = handle->wlr_seat->data_device; - struct wlr_data_source *source = wl_resource_get_user_data(source_resource); - wlr_data_device_set_selection(device, source); -} - -static struct wl_data_device_interface data_device_impl = { - .start_drag = data_device_start_drag, - .set_selection = data_device_set_selection, - .release = resource_destroy -}; - -static void data_device_selection_destroy(struct wl_listener *listener, - void *data) { - struct wlr_data_device *device = - wl_container_of(listener, device, selection_destroyed); - assert(data == device->selection); - device->selection = NULL; // make sure no cancel is sent - wlr_data_device_set_selection(device, NULL); -} - -static struct wlr_data_device *seat_ensure_data_device( - struct wlr_data_device_manager *manager, struct wlr_seat *seat) { - if (seat->data_device) { - return seat->data_device; - } - - seat->data_device = calloc(1, sizeof(*seat->data_device)); - if (!seat->data_device) { - wlr_log(L_ERROR, "Failed to allocate wlr_data_device"); - return NULL; - } - - seat->data_device->seat = seat; - wl_signal_init(&seat->data_device->events.selection_change); - seat->data_device->selection_destroyed.notify = - data_device_selection_destroy; - return seat->data_device; -} - -static void data_device_destroy(struct wl_resource *resource) { - struct wlr_seat_handle *handle = wl_resource_get_user_data(resource); - handle->data_device = NULL; -} - -static void data_device_manager_create_data_source(struct wl_client *client, - struct wl_resource *resource, uint32_t id) { - uint32_t version = wl_resource_get_version(resource); - if (!wlr_wl_data_source_create(client, version, id)) { - wlr_log(L_ERROR, "Failed to create wlr_wl_data_source"); - wl_resource_post_no_memory(resource); - return; - } -} - -static void data_device_manager_get_data_device(struct wl_client *client, - struct wl_resource *resource, uint32_t id, - struct wl_resource *seat_resource) { - struct wlr_data_device_manager *manager = - wl_resource_get_user_data(resource); - struct wlr_seat_handle *seat_handle = - wl_resource_get_user_data(seat_resource); - struct wlr_data_device *device; - if (!(device = seat_ensure_data_device(manager, seat_handle->wlr_seat))) { - wl_resource_post_no_memory(resource); - return; - } - - if (seat_handle->data_device) { - // TODO: implement resource lists for seat related handles - // this is a protocol violation, see the todos in wlr_seat.c - wl_resource_destroy(seat_handle->data_device); - } - - seat_handle->data_device = wl_resource_create(client, - &wl_data_device_interface, wl_resource_get_version(resource), id); - if (!seat_handle->data_device) { - wlr_log(L_ERROR, "Failed to create wl_data_device resource"); - wl_resource_post_no_memory(resource); - return; - } - - wl_resource_set_implementation(seat_handle->data_device, &data_device_impl, - seat_handle, data_device_destroy); -} - -struct wl_data_device_manager_interface data_device_manager_impl = { - .create_data_source = data_device_manager_create_data_source, - .get_data_device = data_device_manager_get_data_device -}; - -static void data_device_manager_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) { - struct wlr_data_device_manager *manager = data; - assert(client && manager); - - struct wl_resource *resource = wl_resource_create( - client, &wl_data_device_manager_interface, version, id); - if (!resource) { - wlr_log(L_ERROR, "Failed to allocate wl_data_device_manager resource"); - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &data_device_manager_impl, - manager, NULL); -} - -struct wlr_data_device_manager *wlr_data_device_manager_create( - struct wl_display *display) { - struct wlr_data_device_manager *manager = calloc(1, sizeof(*manager)); - if (!manager) { - wlr_log(L_ERROR, "Failed to allocated wlr_data_device_manager"); - return NULL; - } - - manager->global = - wl_global_create(display, &wl_data_device_manager_interface, 3, manager, - data_device_manager_bind); - - if (!manager->global) { - wlr_log(L_ERROR, "Failed to create global for wlr_data_device_manager"); - free(manager); - return NULL; - } - - return manager; -} - -void wlr_data_device_manager_destroy(struct wlr_data_device_manager *manager) { - if (manager) { - wl_global_destroy(manager->global); - free(manager); - } -} - -void wlr_data_device_set_selection(struct wlr_data_device *device, - struct wlr_data_source *source) { - if (device->selection == source) { - return; - } - - if (device->selection) { - wl_list_remove(&device->selection_destroyed.link); - wlr_data_source_cancelled(device->selection); - } - - device->selection = source; - wl_signal_emit(&device->events.selection_change, device); - - if (source) { - wl_signal_add(&source->events.destroy, &device->selection_destroyed); - } -} diff --git a/types/wlr_data_source.c b/types/wlr_data_source.c deleted file mode 100644 index 98de4f97..00000000 --- a/types/wlr_data_source.c +++ /dev/null @@ -1,140 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include <assert.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <wayland-server.h> -#include <wlr/types/wlr_list.h> -#include <wlr/util/log.h> -#include <wlr/types/wlr_data_source.h> -#include <wlr/types/wlr_data_device_manager.h> -#include <wlr/interfaces/wlr_data_source.h> - -bool wlr_data_source_init(struct wlr_data_source *source, - struct wlr_data_source_impl *impl) { - source->impl = impl; - wl_signal_init(&source->events.destroy); - return (source->types = wlr_list_create()); -} - -void wlr_data_source_finish(struct wlr_data_source *source) { - if (source) { - wl_signal_emit(&source->events.destroy, source); - if (source->types) { - wlr_list_foreach(source->types, free); - } - wlr_list_free(source->types); - } -} - -void wlr_data_source_send(struct wlr_data_source *src, const char *type, - int fd) { - assert(src && src->impl && src->impl->send); - src->impl->send(src, type, fd); -} - -void wlr_data_source_accepted(struct wlr_data_source *src, const char *type) { - assert(src && src->impl); - if (src->impl->accepted) { - src->impl->accepted(src, type); - } -} - -void wlr_data_source_cancelled(struct wlr_data_source *src) { - assert(src && src->impl); - if (src->impl->cancelled) { - src->impl->cancelled(src); - } -} - -static void data_source_send(struct wlr_data_source *src, - const char *type, int fd) { - struct wlr_wl_data_source *wl_src = (struct wlr_wl_data_source *) src; - wl_data_source_send_send(wl_src->resource, type, fd); - close(fd); -} - -static void data_source_accepted(struct wlr_data_source *src, - const char *type) { - struct wlr_wl_data_source *wl_src = (struct wlr_wl_data_source *) src; - wl_data_source_send_target(wl_src->resource, type); -} - -static void data_source_cancelled(struct wlr_data_source *src) { - struct wlr_wl_data_source *wl_src = (struct wlr_wl_data_source *) src; - wl_data_source_send_cancelled(wl_src->resource); -} - -static struct wlr_data_source_impl data_source_wl_impl = { - .send = data_source_send, - .accepted = data_source_accepted, - .cancelled = data_source_cancelled, -}; - -static void data_source_offer(struct wl_client *client, - struct wl_resource *resource, - const char *type) { - struct wlr_wl_data_source *src = wl_resource_get_user_data(resource); - char *dtype = strdup(type); - if (!dtype) { - wl_resource_post_no_memory(resource); - return; - } - - wlr_list_add(src->base.types, dtype); -} - -static void data_source_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void data_source_set_actions(struct wl_client *client, - struct wl_resource *resource, uint32_t dnd_actions) { - wlr_log(L_DEBUG, "TODO: data source set actions"); -} - -static struct wl_data_source_interface wl_data_source_impl = { - .offer = data_source_offer, - .destroy = data_source_destroy, - .set_actions = data_source_set_actions, -}; - -static void destroy_wl_data_source(struct wl_resource *resource) { - struct wlr_wl_data_source *src = wl_resource_get_user_data(resource); - wlr_data_source_finish(&src->base); - free(src); -} - -struct wlr_wl_data_source *wlr_wl_data_source_create( - struct wl_client *client, - uint32_t version, uint32_t id) { - struct wlr_wl_data_source *src = calloc(1, sizeof(*src)); - if (!src) { - wlr_log(L_ERROR, "Failed to allocator wlr_wl_data_source"); - wl_client_post_no_memory(client); - return NULL; - } - - if (!wlr_data_source_init(&src->base, &data_source_wl_impl)) { - wlr_log(L_ERROR, "Failed to init wlr_wl_data_source"); - wl_client_post_no_memory(client); - goto err; - } - - if (!(src->resource = wl_resource_create(client, &wl_data_source_interface, - version, id))) { - wlr_log(L_ERROR, "Failed to create wl_resource for wlr_wl_data_source"); - wl_client_post_no_memory(client); - goto err; - } - - wl_resource_set_implementation(src->resource, &wl_data_source_impl, src, - destroy_wl_data_source); - return src; - -err: - wlr_data_source_finish(&src->base); - free(src); - return NULL; -} diff --git a/types/wlr_gamma_control.c b/types/wlr_gamma_control.c index 9d74e749..e765c2a5 100644 --- a/types/wlr_gamma_control.c +++ b/types/wlr_gamma_control.c @@ -15,6 +15,7 @@ static void gamma_control_destroy(struct wlr_gamma_control *gamma_control) { wl_signal_emit(&gamma_control->events.destroy, gamma_control); wl_list_remove(&gamma_control->output_destroy_listener.link); wl_resource_set_user_data(gamma_control->resource, NULL); + wl_list_remove(&gamma_control->link); free(gamma_control); } diff --git a/types/wlr_seat.c b/types/wlr_seat.c index b2c8a758..a825a37f 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -6,6 +6,7 @@ #include <wlr/types/wlr_seat.h> #include <wlr/types/wlr_input_device.h> #include <wlr/util/log.h> +#include <wlr/types/wlr_data_device.h> static void resource_destroy(struct wl_client *client, struct wl_resource *resource) { @@ -162,6 +163,8 @@ static void wl_seat_get_touch(struct wl_client *client, static void wlr_seat_handle_resource_destroy(struct wl_resource *resource) { struct wlr_seat_handle *handle = wl_resource_get_user_data(resource); + wl_signal_emit(&handle->wlr_seat->events.client_unbound, handle); + if (handle == handle->wlr_seat->pointer_state.focused_handle) { handle->wlr_seat->pointer_state.focused_handle = NULL; } @@ -181,7 +184,6 @@ static void wlr_seat_handle_resource_destroy(struct wl_resource *resource) { if (handle->data_device) { wl_resource_destroy(handle->data_device); } - wl_signal_emit(&handle->wlr_seat->events.client_unbound, handle); wl_list_remove(&handle->link); free(handle); } @@ -323,7 +325,9 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_signal_init(&wlr_seat->events.client_bound); wl_signal_init(&wlr_seat->events.client_unbound); + wl_signal_init(&wlr_seat->events.request_set_cursor); + wl_signal_init(&wlr_seat->events.selection); wl_signal_init(&wlr_seat->events.pointer_grab_begin); wl_signal_init(&wlr_seat->events.pointer_grab_end); @@ -550,8 +554,24 @@ void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t button, uint32_t state) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (wlr_seat->pointer_state.button_count == 0) { + wlr_seat->pointer_state.grab_button = button; + wlr_seat->pointer_state.grab_time = time; + } + wlr_seat->pointer_state.button_count++; + } else { + wlr_seat->pointer_state.button_count--; + } + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; - return grab->interface->button(grab, time, button, state); + uint32_t serial = grab->interface->button(grab, time, button, state); + + if (wlr_seat->pointer_state.button_count == 1) { + wlr_seat->pointer_state.grab_serial = serial; + } + + return serial; } void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, @@ -631,9 +651,11 @@ void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat) { struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab; - wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab; - wl_signal_emit(&wlr_seat->events.keyboard_grab_end, grab); + if (grab != wlr_seat->keyboard_state.default_grab) { + wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab; + wl_signal_emit(&wlr_seat->events.keyboard_grab_end, grab); + } } static void keyboard_surface_destroy_notify(struct wl_listener *listener, @@ -706,6 +728,7 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, uint32_t serial = wl_display_next_serial(wlr_seat->display); wl_keyboard_send_enter(handle->keyboard, serial, surface->resource, &keys); + wlr_seat_handle_send_selection(handle); } // reinitialize the focus destroy events diff --git a/types/wlr_server_decoration.c b/types/wlr_server_decoration.c new file mode 100644 index 00000000..0664edfb --- /dev/null +++ b/types/wlr_server_decoration.c @@ -0,0 +1,180 @@ +#include <assert.h> +#include <stdlib.h> +#include <server-decoration-protocol.h> +#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_server_decoration.h> +#include <wlr/util/log.h> + +static void server_decoration_handle_release(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void server_decoration_handle_request_mode(struct wl_client *client, + struct wl_resource *resource, uint32_t mode) { + struct wlr_server_decoration *decoration = + wl_resource_get_user_data(resource); + if (decoration->mode == mode) { + return; + } + decoration->mode = mode; + wl_signal_emit(&decoration->events.mode, decoration); + org_kde_kwin_server_decoration_send_mode(decoration->resource, + decoration->mode); +} + +static void server_decoration_destroy( + struct wlr_server_decoration *decoration) { + wl_signal_emit(&decoration->events.destroy, decoration); + wl_list_remove(&decoration->surface_destroy_listener.link); + wl_resource_set_user_data(decoration->resource, NULL); + wl_list_remove(&decoration->link); + free(decoration); +} + +static void server_decoration_destroy_resource(struct wl_resource *resource) { + struct wlr_server_decoration *decoration = + wl_resource_get_user_data(resource); + if (decoration != NULL) { + server_decoration_destroy(decoration); + } +} + +static void server_decoration_handle_surface_destroy( + struct wl_listener *listener, void *data) { + struct wlr_server_decoration *decoration = + wl_container_of(listener, decoration, surface_destroy_listener); + server_decoration_destroy(decoration); +} + +static const struct org_kde_kwin_server_decoration_interface +server_decoration_impl = { + .release = server_decoration_handle_release, + .request_mode = server_decoration_handle_request_mode, +}; + +static void server_decoration_manager_handle_create(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_server_decoration_manager *manager = + wl_resource_get_user_data(manager_resource); + struct wlr_surface *surface = wl_resource_get_user_data(surface_resource); + + struct wlr_server_decoration *decoration = + calloc(1, sizeof(struct wlr_server_decoration)); + if (decoration == NULL) { + wl_client_post_no_memory(client); + return; + } + decoration->surface = surface; + decoration->mode = manager->default_mode; + + int version = wl_resource_get_version(manager_resource); + decoration->resource = wl_resource_create(client, + &org_kde_kwin_server_decoration_interface, version, id); + if (decoration->resource == NULL) { + wl_client_post_no_memory(client); + free(decoration); + return; + } + wl_resource_set_implementation(decoration->resource, + &server_decoration_impl, decoration, + server_decoration_destroy_resource); + + wlr_log(L_DEBUG, "new server_decoration %p (res %p)", decoration, + decoration->resource); + + wl_signal_init(&decoration->events.destroy); + wl_signal_init(&decoration->events.mode); + + wl_signal_add(&surface->events.destroy, + &decoration->surface_destroy_listener); + decoration->surface_destroy_listener.notify = + server_decoration_handle_surface_destroy; + + wl_list_insert(&manager->decorations, &decoration->link); + + org_kde_kwin_server_decoration_send_mode(decoration->resource, + decoration->mode); + + wl_signal_emit(&manager->events.new_decoration, decoration); +} + +static const struct org_kde_kwin_server_decoration_manager_interface +server_decoration_manager_impl = { + .create = server_decoration_manager_handle_create, +}; + +void wlr_server_decoration_manager_set_default_mode( + struct wlr_server_decoration_manager *manager, uint32_t default_mode) { + manager->default_mode = default_mode; + + struct wl_resource *resource; + wl_resource_for_each(resource, &manager->wl_resources) { + org_kde_kwin_server_decoration_manager_send_default_mode(resource, + manager->default_mode); + } +} + +void server_decoration_manager_destroy_resource(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void server_decoration_manager_bind(struct wl_client *client, + void *_manager, uint32_t version, uint32_t id) { + struct wlr_server_decoration_manager *manager = _manager; + assert(client && manager); + + struct wl_resource *resource = wl_resource_create(client, + &org_kde_kwin_server_decoration_manager_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &server_decoration_manager_impl, + manager, server_decoration_manager_destroy_resource); + + wl_list_insert(&manager->wl_resources, wl_resource_get_link(resource)); + + org_kde_kwin_server_decoration_manager_send_default_mode(resource, + manager->default_mode); +} + +struct wlr_server_decoration_manager *wlr_server_decoration_manager_create( + struct wl_display *display) { + struct wlr_server_decoration_manager *manager = + calloc(1, sizeof(struct wlr_server_decoration_manager)); + if (manager == NULL) { + return NULL; + } + manager->wl_global = wl_global_create(display, + &org_kde_kwin_server_decoration_manager_interface, 1, manager, + server_decoration_manager_bind); + if (manager->wl_global == NULL) { + free(manager); + return NULL; + } + manager->default_mode = ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE; + wl_list_init(&manager->wl_resources); + wl_list_init(&manager->decorations); + wl_signal_init(&manager->events.new_decoration); + return manager; +} + +void wlr_server_decoration_manager_destroy( + struct wlr_server_decoration_manager *manager) { + if (manager == NULL) { + return; + } + struct wlr_server_decoration *decoration, *tmp_decoration; + wl_list_for_each_safe(decoration, tmp_decoration, &manager->decorations, + link) { + server_decoration_destroy(decoration); + } + struct wl_resource *resource, *tmp_resource; + wl_resource_for_each_safe(resource, tmp_resource, &manager->wl_resources) { + server_decoration_manager_destroy_resource(resource); + } + wl_global_destroy(manager->wl_global); + free(manager); +} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index f8689d8e..72996157 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -269,6 +269,8 @@ static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surfa wlr_surface_state_release_buffer(state); wlr_surface_state_set_buffer(state, next->buffer); wlr_surface_state_reset_buffer(next); + state->sx = next->sx; + state->sy = next->sy; update_size = true; } if (update_size) { @@ -406,8 +408,7 @@ static void wlr_surface_commit_pending(struct wlr_surface *surface) { wlr_surface_move_state(surface, surface->pending, surface->current); if (null_buffer_commit) { - wlr_texture_destroy(surface->texture); - surface->texture = NULL; + surface->texture->valid = false; } bool reupload_buffer = oldw != surface->current->buffer_width || diff --git a/xwayland/meson.build b/xwayland/meson.build index d989f6dd..a05ae584 100644 --- a/xwayland/meson.build +++ b/xwayland/meson.build @@ -6,5 +6,12 @@ lib_wlr_xwayland = static_library( 'xwm.c', ), include_directories: wlr_inc, - dependencies: [wayland_server, xcb, xcb_composite, xcb_icccm, pixman], + dependencies: [ + wayland_server, + xcb, + xcb_composite, + xcb_xfixes, + xcb_icccm, + pixman, + ], ) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index f58acb73..499d0bbf 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -3,6 +3,7 @@ #endif #include <stdlib.h> #include <xcb/composite.h> +#include <xcb/xfixes.h> #include "wlr/util/log.h" #include "wlr/types/wlr_surface.h" #include "wlr/xwayland.h" @@ -15,8 +16,8 @@ const char *atom_map[ATOM_LAST] = { "WL_SURFACE_ID", "WM_DELETE_WINDOW", - "WM_HINTS", "WM_PROTOCOLS", + "WM_HINTS", "WM_NORMAL_HINTS", "WM_SIZE_HINTS", "_MOTIF_WM_HINTS", @@ -825,9 +826,30 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) { // TODO more xcb init // xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_composite_id); + xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_xfixes_id); + xcb_get_resources(xwm); xcb_init_wm(xwm); + xwm->xfixes = xcb_get_extension_data(xwm->xcb_conn, &xcb_xfixes_id); + + if (!xwm->xfixes || !xwm->xfixes->present) { + wlr_log(L_DEBUG, "xfixes not available"); + } + + xcb_xfixes_query_version_cookie_t xfixes_cookie; + xcb_xfixes_query_version_reply_t *xfixes_reply; + xfixes_cookie = + xcb_xfixes_query_version(xwm->xcb_conn, XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION); + xfixes_reply = + xcb_xfixes_query_version_reply(xwm->xcb_conn, xfixes_cookie, NULL); + + wlr_log(L_DEBUG, "xfixes version: %d.%d", + xfixes_reply->major_version, xfixes_reply->minor_version); + + free(xfixes_reply); + xwm->surface_create_listener.notify = create_surface_handler; wl_signal_add(&wlr_xwayland->compositor->events.create_surface, &xwm->surface_create_listener); diff --git a/xwayland/xwm.h b/xwayland/xwm.h index be710a1b..a04b1065 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -85,6 +85,8 @@ struct wlr_xwm { struct wl_list new_surfaces; struct wl_list unpaired_surfaces; + + const xcb_query_extension_reply_t *xfixes; }; void xwm_destroy(struct wlr_xwm *xwm); |