mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-11-03 09:01:40 -05:00 
			
		
		
		
	Remove rootston
This commit is contained in:
		
							parent
							
								
									913cac1835
								
							
						
					
					
						commit
						58b2584863
					
				
					 41 changed files with 0 additions and 9724 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -7,4 +7,3 @@ build/
 | 
				
			||||||
build-*/
 | 
					build-*/
 | 
				
			||||||
wayland-*-protocol.*
 | 
					wayland-*-protocol.*
 | 
				
			||||||
wlr-example.ini
 | 
					wlr-example.ini
 | 
				
			||||||
rootston.ini
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_BINDINGS_H
 | 
					 | 
				
			||||||
#define ROOTSTON_BINDINGS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void execute_binding_command(struct roots_seat *seat, struct roots_input *input, const char *command);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif //ROOTSTON_BINDINGS_H
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,133 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_CONFIG_H
 | 
					 | 
				
			||||||
#define ROOTSTON_CONFIG_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <xf86drmMode.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_input_device.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_switch.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output_layout.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ROOTS_CONFIG_DEFAULT_SEAT_NAME "seat0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_output_mode_config {
 | 
					 | 
				
			||||||
	drmModeModeInfo info;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_output_config {
 | 
					 | 
				
			||||||
	char *name;
 | 
					 | 
				
			||||||
	bool enable;
 | 
					 | 
				
			||||||
	enum wl_output_transform transform;
 | 
					 | 
				
			||||||
	int x, y;
 | 
					 | 
				
			||||||
	float scale;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		int width, height;
 | 
					 | 
				
			||||||
		float refresh_rate;
 | 
					 | 
				
			||||||
	} mode;
 | 
					 | 
				
			||||||
	struct wl_list modes;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_device_config {
 | 
					 | 
				
			||||||
	char *name;
 | 
					 | 
				
			||||||
	char *seat;
 | 
					 | 
				
			||||||
	char *mapped_output;
 | 
					 | 
				
			||||||
	bool tap_enabled;
 | 
					 | 
				
			||||||
	struct wlr_box *mapped_box;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_binding_config {
 | 
					 | 
				
			||||||
	uint32_t modifiers;
 | 
					 | 
				
			||||||
	xkb_keysym_t *keysyms;
 | 
					 | 
				
			||||||
	size_t keysyms_len;
 | 
					 | 
				
			||||||
	char *command;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_keyboard_config {
 | 
					 | 
				
			||||||
	char *name;
 | 
					 | 
				
			||||||
	char *seat;
 | 
					 | 
				
			||||||
	uint32_t meta_key;
 | 
					 | 
				
			||||||
	char *rules;
 | 
					 | 
				
			||||||
	char *model;
 | 
					 | 
				
			||||||
	char *layout;
 | 
					 | 
				
			||||||
	char *variant;
 | 
					 | 
				
			||||||
	char *options;
 | 
					 | 
				
			||||||
	int repeat_rate, repeat_delay;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_cursor_config {
 | 
					 | 
				
			||||||
	char *seat;
 | 
					 | 
				
			||||||
	char *mapped_output;
 | 
					 | 
				
			||||||
	struct wlr_box *mapped_box;
 | 
					 | 
				
			||||||
	char *theme;
 | 
					 | 
				
			||||||
	char *default_image;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_switch_config {
 | 
					 | 
				
			||||||
	char *name;
 | 
					 | 
				
			||||||
	enum wlr_switch_type switch_type;
 | 
					 | 
				
			||||||
	enum wlr_switch_state switch_state;
 | 
					 | 
				
			||||||
	char *command;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_config {
 | 
					 | 
				
			||||||
	bool xwayland;
 | 
					 | 
				
			||||||
	bool xwayland_lazy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list outputs;
 | 
					 | 
				
			||||||
	struct wl_list devices;
 | 
					 | 
				
			||||||
	struct wl_list bindings;
 | 
					 | 
				
			||||||
	struct wl_list keyboards;
 | 
					 | 
				
			||||||
	struct wl_list cursors;
 | 
					 | 
				
			||||||
	struct wl_list switches;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	char *config_path;
 | 
					 | 
				
			||||||
	char *startup_cmd;
 | 
					 | 
				
			||||||
	bool debug_damage_tracking;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Create a roots config from the given command line arguments. Command line
 | 
					 | 
				
			||||||
 * arguments can specify the location of the config file. If it is not
 | 
					 | 
				
			||||||
 * specified, the default location will be used.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct roots_config *roots_config_create_from_args(int argc, char *argv[]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Destroy the config and free its resources.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void roots_config_destroy(struct roots_config *config);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Get configuration for the output. If the output is not configured, returns
 | 
					 | 
				
			||||||
 * NULL.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct roots_output_config *roots_config_get_output(struct roots_config *config,
 | 
					 | 
				
			||||||
	struct wlr_output *output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Get configuration for the device. If the device is not configured, returns
 | 
					 | 
				
			||||||
 * NULL.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct roots_device_config *roots_config_get_device(struct roots_config *config,
 | 
					 | 
				
			||||||
	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 roots_keyboard_config *roots_config_get_keyboard(
 | 
					 | 
				
			||||||
	struct roots_config *config, struct wlr_input_device *device);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Get configuration for the cursor. If the cursor is not configured, returns
 | 
					 | 
				
			||||||
 * NULL. A NULL seat_name returns the default config for cursors.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct roots_cursor_config *roots_config_get_cursor(struct roots_config *config,
 | 
					 | 
				
			||||||
	const char *seat_name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,114 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_CURSOR_H
 | 
					 | 
				
			||||||
#define ROOTSTON_CURSOR_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_pointer_constraints_v1.h>
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum roots_cursor_mode {
 | 
					 | 
				
			||||||
	ROOTS_CURSOR_PASSTHROUGH = 0,
 | 
					 | 
				
			||||||
	ROOTS_CURSOR_MOVE = 1,
 | 
					 | 
				
			||||||
	ROOTS_CURSOR_RESIZE = 2,
 | 
					 | 
				
			||||||
	ROOTS_CURSOR_ROTATE = 3,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_cursor {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_cursor *cursor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_pointer_constraint_v1 *active_constraint;
 | 
					 | 
				
			||||||
	pixman_region32_t confine; // invalid if active_constraint == NULL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const char *default_xcursor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum roots_cursor_mode mode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// state from input (review if this is necessary)
 | 
					 | 
				
			||||||
	struct wlr_xcursor_manager *xcursor_manager;
 | 
					 | 
				
			||||||
	struct wlr_seat *wl_seat;
 | 
					 | 
				
			||||||
	struct wl_client *cursor_client;
 | 
					 | 
				
			||||||
	int offs_x, offs_y;
 | 
					 | 
				
			||||||
	int view_x, view_y, view_width, view_height;
 | 
					 | 
				
			||||||
	float view_rotation;
 | 
					 | 
				
			||||||
	uint32_t resize_edges;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat_view *pointer_view;
 | 
					 | 
				
			||||||
	struct wlr_surface *wlr_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener motion;
 | 
					 | 
				
			||||||
	struct wl_listener motion_absolute;
 | 
					 | 
				
			||||||
	struct wl_listener button;
 | 
					 | 
				
			||||||
	struct wl_listener axis;
 | 
					 | 
				
			||||||
	struct wl_listener frame;
 | 
					 | 
				
			||||||
	struct wl_listener swipe_begin;
 | 
					 | 
				
			||||||
	struct wl_listener swipe_update;
 | 
					 | 
				
			||||||
	struct wl_listener swipe_end;
 | 
					 | 
				
			||||||
	struct wl_listener pinch_begin;
 | 
					 | 
				
			||||||
	struct wl_listener pinch_update;
 | 
					 | 
				
			||||||
	struct wl_listener pinch_end;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener touch_down;
 | 
					 | 
				
			||||||
	struct wl_listener touch_up;
 | 
					 | 
				
			||||||
	struct wl_listener touch_motion;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener tool_axis;
 | 
					 | 
				
			||||||
	struct wl_listener tool_tip;
 | 
					 | 
				
			||||||
	struct wl_listener tool_proximity;
 | 
					 | 
				
			||||||
	struct wl_listener tool_button;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener request_set_cursor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener focus_change;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener constraint_commit;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_cursor *roots_cursor_create(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_destroy(struct roots_cursor *cursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_motion(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_pointer_motion *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_pointer_motion_absolute *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_button(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_pointer_button *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_axis(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_pointer_axis *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_frame(struct roots_cursor *cursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_touch_down(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_touch_down *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_touch_up(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_touch_up *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_touch_motion *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_tablet_tool_axis *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_tool_tip(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_event_tablet_tool_tip *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_seat_pointer_request_set_cursor_event *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_focus_change(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_seat_pointer_focus_change_event *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_update_position(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	uint32_t time);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_update_focus(struct roots_cursor *cursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_constrain(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
	struct wlr_pointer_constraint_v1 *constraint, double sx, double sy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,106 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_DESKTOP_H
 | 
					 | 
				
			||||||
#define ROOTSTON_DESKTOP_H
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_compositor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_gamma_control_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_gtk_primary_selection.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_idle.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_input_inhibitor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_input_method_v2.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_layer_shell_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_list.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output_layout.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output_management_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_pointer_gestures_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_presentation_time.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_relative_pointer_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_screencopy_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_text_input_v3.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_virtual_keyboard_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xcursor_manager.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell_v6.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell.h>
 | 
					 | 
				
			||||||
#include "rootston/config.h"
 | 
					 | 
				
			||||||
#include "rootston/output.h"
 | 
					 | 
				
			||||||
#include "rootston/view.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_desktop {
 | 
					 | 
				
			||||||
	struct wl_list views; // roots_view::link
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list outputs; // roots_output::link
 | 
					 | 
				
			||||||
	struct timespec last_frame;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_server *server;
 | 
					 | 
				
			||||||
	struct roots_config *config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output_layout *layout;
 | 
					 | 
				
			||||||
	struct wlr_xcursor_manager *xcursor_manager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_compositor *compositor;
 | 
					 | 
				
			||||||
	struct wlr_xdg_shell_v6 *xdg_shell_v6;
 | 
					 | 
				
			||||||
	struct wlr_xdg_shell *xdg_shell;
 | 
					 | 
				
			||||||
	struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;
 | 
					 | 
				
			||||||
	struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1;
 | 
					 | 
				
			||||||
	struct wlr_server_decoration_manager *server_decoration_manager;
 | 
					 | 
				
			||||||
	struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager;
 | 
					 | 
				
			||||||
	struct wlr_gtk_primary_selection_device_manager *primary_selection_device_manager;
 | 
					 | 
				
			||||||
	struct wlr_idle *idle;
 | 
					 | 
				
			||||||
	struct wlr_idle_inhibit_manager_v1 *idle_inhibit;
 | 
					 | 
				
			||||||
	struct wlr_input_inhibit_manager *input_inhibit;
 | 
					 | 
				
			||||||
	struct wlr_layer_shell_v1 *layer_shell;
 | 
					 | 
				
			||||||
	struct wlr_input_method_manager_v2 *input_method;
 | 
					 | 
				
			||||||
	struct wlr_text_input_manager_v3 *text_input;
 | 
					 | 
				
			||||||
	struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;
 | 
					 | 
				
			||||||
	struct wlr_screencopy_manager_v1 *screencopy;
 | 
					 | 
				
			||||||
	struct wlr_tablet_manager_v2 *tablet_v2;
 | 
					 | 
				
			||||||
	struct wlr_pointer_constraints_v1 *pointer_constraints;
 | 
					 | 
				
			||||||
	struct wlr_presentation *presentation;
 | 
					 | 
				
			||||||
	struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager_v1;
 | 
					 | 
				
			||||||
	struct wlr_relative_pointer_manager_v1 *relative_pointer_manager;
 | 
					 | 
				
			||||||
	struct wlr_pointer_gestures_v1 *pointer_gestures;
 | 
					 | 
				
			||||||
	struct wlr_output_manager_v1 *output_manager_v1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener new_output;
 | 
					 | 
				
			||||||
	struct wl_listener layout_change;
 | 
					 | 
				
			||||||
	struct wl_listener xdg_shell_v6_surface;
 | 
					 | 
				
			||||||
	struct wl_listener xdg_shell_surface;
 | 
					 | 
				
			||||||
	struct wl_listener layer_shell_surface;
 | 
					 | 
				
			||||||
	struct wl_listener xdg_toplevel_decoration;
 | 
					 | 
				
			||||||
	struct wl_listener input_inhibit_activate;
 | 
					 | 
				
			||||||
	struct wl_listener input_inhibit_deactivate;
 | 
					 | 
				
			||||||
	struct wl_listener virtual_keyboard_new;
 | 
					 | 
				
			||||||
	struct wl_listener pointer_constraint;
 | 
					 | 
				
			||||||
	struct wl_listener output_manager_apply;
 | 
					 | 
				
			||||||
	struct wl_listener output_manager_test;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	struct wlr_xwayland *xwayland;
 | 
					 | 
				
			||||||
	struct wl_listener xwayland_surface;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_desktop *desktop_create(struct roots_server *server,
 | 
					 | 
				
			||||||
	struct roots_config *config);
 | 
					 | 
				
			||||||
void desktop_destroy(struct roots_desktop *desktop);
 | 
					 | 
				
			||||||
struct roots_output *desktop_output_from_wlr_output(
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop, struct wlr_output *output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct wlr_surface *desktop_surface_at(struct roots_desktop *desktop,
 | 
					 | 
				
			||||||
		double lx, double ly, double *sx, double *sy,
 | 
					 | 
				
			||||||
		struct roots_view **view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
void handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
void handle_layer_shell_surface(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
void handle_xwayland_surface(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,93 +0,0 @@
 | 
				
			||||||
/* inih -- simple .INI file parser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
 | 
					 | 
				
			||||||
home page for more info:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
https://github.com/benhoyt/inih
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef __INI_H__
 | 
					 | 
				
			||||||
#define __INI_H__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Make this header file easier to include in C++ code */
 | 
					 | 
				
			||||||
#ifdef __cplusplus
 | 
					 | 
				
			||||||
extern "C" {
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Typedef for prototype of handler function. */
 | 
					 | 
				
			||||||
typedef int (*ini_handler)(void* user, const char* section,
 | 
					 | 
				
			||||||
                           const char* name, const char* value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Typedef for prototype of fgets-style reader function. */
 | 
					 | 
				
			||||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Parse given INI-style file. May have [section]s, name=value pairs
 | 
					 | 
				
			||||||
   (whitespace stripped), and comments starting with ';' (semicolon). Section
 | 
					 | 
				
			||||||
   is "" if name=value pair parsed before any section heading. name:value
 | 
					 | 
				
			||||||
   pairs are also supported as a concession to Python's configparser.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   For each name=value pair parsed, call handler function with given user
 | 
					 | 
				
			||||||
   pointer as well as section, name, and value (data only valid for duration
 | 
					 | 
				
			||||||
   of handler call). Handler should return nonzero on success, zero on error.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   Returns 0 on success, line number of first error on parse error (doesn't
 | 
					 | 
				
			||||||
   stop on first error), -1 on file open error, or -2 on memory allocation
 | 
					 | 
				
			||||||
   error (only when INI_USE_STACK is zero).
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
int ini_parse(const char* filename, ini_handler handler, void* user);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
 | 
					 | 
				
			||||||
   close the file when it's finished -- the caller must do that. */
 | 
					 | 
				
			||||||
int ini_parse_file(FILE* file, ini_handler handler, void* user);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
 | 
					 | 
				
			||||||
   filename. Used for implementing custom or string-based I/O. */
 | 
					 | 
				
			||||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
 | 
					 | 
				
			||||||
                     void* user);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
 | 
					 | 
				
			||||||
   configparser. If allowed, ini_parse() will call the handler with the same
 | 
					 | 
				
			||||||
   name for each subsequent line parsed. */
 | 
					 | 
				
			||||||
#ifndef INI_ALLOW_MULTILINE
 | 
					 | 
				
			||||||
#define INI_ALLOW_MULTILINE 1
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
 | 
					 | 
				
			||||||
   the file. See http://code.google.com/p/inih/issues/detail?id=21 */
 | 
					 | 
				
			||||||
#ifndef INI_ALLOW_BOM
 | 
					 | 
				
			||||||
#define INI_ALLOW_BOM 1
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Nonzero to allow inline comments (with valid inline comment characters
 | 
					 | 
				
			||||||
   specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
 | 
					 | 
				
			||||||
   Python 3.2+ configparser behaviour. */
 | 
					 | 
				
			||||||
#ifndef INI_ALLOW_INLINE_COMMENTS
 | 
					 | 
				
			||||||
#define INI_ALLOW_INLINE_COMMENTS 1
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifndef INI_INLINE_COMMENT_PREFIXES
 | 
					 | 
				
			||||||
#define INI_INLINE_COMMENT_PREFIXES ";"
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Nonzero to use stack, zero to use heap (malloc/free). */
 | 
					 | 
				
			||||||
#ifndef INI_USE_STACK
 | 
					 | 
				
			||||||
#define INI_USE_STACK 1
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Stop parsing on first error (default is to keep parsing). */
 | 
					 | 
				
			||||||
#ifndef INI_STOP_ON_FIRST_ERROR
 | 
					 | 
				
			||||||
#define INI_STOP_ON_FIRST_ERROR 0
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Maximum line length for any line in INI file. */
 | 
					 | 
				
			||||||
#ifndef INI_MAX_LINE
 | 
					 | 
				
			||||||
#define INI_MAX_LINE 2000
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __cplusplus
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* __INI_H__ */
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,37 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_INPUT_H
 | 
					 | 
				
			||||||
#define ROOTSTON_INPUT_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_cursor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_input_device.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_seat.h>
 | 
					 | 
				
			||||||
#include "rootston/config.h"
 | 
					 | 
				
			||||||
#include "rootston/cursor.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
#include "rootston/view.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_input {
 | 
					 | 
				
			||||||
	struct roots_config *config;
 | 
					 | 
				
			||||||
	struct roots_server *server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener new_input;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list seats; // roots_seat::link
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_input *input_create(struct roots_server *server,
 | 
					 | 
				
			||||||
		struct roots_config *config);
 | 
					 | 
				
			||||||
void input_destroy(struct roots_input *input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat *input_seat_from_wlr_seat(struct roots_input *input,
 | 
					 | 
				
			||||||
		struct wlr_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool input_view_has_focus(struct roots_input *input, struct roots_view *view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat *input_get_seat(struct roots_input *input, char *name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat *input_last_active_seat(struct roots_input *input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void input_update_cursor_focus(struct roots_input *input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,34 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_KEYBOARD_H
 | 
					 | 
				
			||||||
#define ROOTSTON_KEYBOARD_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <xkbcommon/xkbcommon.h>
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ROOTS_KEYBOARD_PRESSED_KEYSYMS_CAP 32
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_keyboard {
 | 
					 | 
				
			||||||
	struct roots_input *input;
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_input_device *device;
 | 
					 | 
				
			||||||
	struct roots_keyboard_config *config;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener device_destroy;
 | 
					 | 
				
			||||||
	struct wl_listener keyboard_key;
 | 
					 | 
				
			||||||
	struct wl_listener keyboard_modifiers;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	xkb_keysym_t pressed_keysyms_translated[ROOTS_KEYBOARD_PRESSED_KEYSYMS_CAP];
 | 
					 | 
				
			||||||
	xkb_keysym_t pressed_keysyms_raw[ROOTS_KEYBOARD_PRESSED_KEYSYMS_CAP];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_keyboard *roots_keyboard_create(struct wlr_input_device *device,
 | 
					 | 
				
			||||||
		struct roots_input *input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_keyboard_destroy(struct roots_keyboard *keyboard);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_keyboard_handle_key(struct roots_keyboard *keyboard,
 | 
					 | 
				
			||||||
		struct wlr_event_keyboard_key *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_keyboard_handle_modifiers(struct roots_keyboard *r_keyboard);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,35 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_LAYERS_H
 | 
					 | 
				
			||||||
#define ROOTSTON_LAYERS_H
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_surface.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_layer_shell_v1.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_layer_surface {
 | 
					 | 
				
			||||||
	struct wlr_layer_surface_v1 *layer_surface;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener surface_commit;
 | 
					 | 
				
			||||||
	struct wl_listener output_destroy;
 | 
					 | 
				
			||||||
	struct wl_listener new_popup;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool configured;
 | 
					 | 
				
			||||||
	struct wlr_box geo;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_layer_popup {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *parent;
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *wlr_popup;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener commit;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_output;
 | 
					 | 
				
			||||||
void arrange_layers(struct roots_output *output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,88 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_OUTPUT_H
 | 
					 | 
				
			||||||
#define ROOTSTON_OUTPUT_H
 | 
					 | 
				
			||||||
#include <pixman.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output_damage.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_desktop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_output {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output;
 | 
					 | 
				
			||||||
	struct wl_list link; // roots_desktop:outputs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *fullscreen_view;
 | 
					 | 
				
			||||||
	struct wl_list layers[4]; // layer_surface::link
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct timespec last_frame;
 | 
					 | 
				
			||||||
	struct wlr_output_damage *damage;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box usable_area;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener enable;
 | 
					 | 
				
			||||||
	struct wl_listener mode;
 | 
					 | 
				
			||||||
	struct wl_listener transform;
 | 
					 | 
				
			||||||
	struct wl_listener present;
 | 
					 | 
				
			||||||
	struct wl_listener damage_frame;
 | 
					 | 
				
			||||||
	struct wl_listener damage_destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef void (*roots_surface_iterator_func_t)(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct wlr_surface *surface, struct wlr_box *box, float rotation,
 | 
					 | 
				
			||||||
	void *user_data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void rotate_child_position(double *sx, double *sy, double sw, double sh,
 | 
					 | 
				
			||||||
	double pw, double ph, float rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_input;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_surface_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct wlr_surface *surface, double ox, double oy,
 | 
					 | 
				
			||||||
	roots_surface_iterator_func_t iterator, void *user_data);
 | 
					 | 
				
			||||||
void output_view_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct roots_view *view, roots_surface_iterator_func_t iterator,
 | 
					 | 
				
			||||||
	void *user_data);
 | 
					 | 
				
			||||||
void output_drag_icons_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct roots_input *input, roots_surface_iterator_func_t iterator,
 | 
					 | 
				
			||||||
	void *user_data);
 | 
					 | 
				
			||||||
void output_layer_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct wl_list *layer_surfaces, roots_surface_iterator_func_t iterator,
 | 
					 | 
				
			||||||
	void *user_data);
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
struct wlr_xwayland_surface;
 | 
					 | 
				
			||||||
void output_xwayland_children_for_each_surface(
 | 
					 | 
				
			||||||
	struct roots_output *output, struct wlr_xwayland_surface *surface,
 | 
					 | 
				
			||||||
	roots_surface_iterator_func_t iterator, void *user_data);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
void output_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
	roots_surface_iterator_func_t iterator, void *user_data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_new_output(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
void handle_output_manager_apply(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
void handle_output_manager_test(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view;
 | 
					 | 
				
			||||||
struct roots_drag_icon;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_damage_whole(struct roots_output *output);
 | 
					 | 
				
			||||||
void output_damage_whole_view(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct roots_view *view);
 | 
					 | 
				
			||||||
void output_damage_from_view(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct roots_view *view);
 | 
					 | 
				
			||||||
void output_damage_whole_drag_icon(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct roots_drag_icon *icon);
 | 
					 | 
				
			||||||
void output_damage_from_local_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct wlr_surface *surface, double ox, double oy);
 | 
					 | 
				
			||||||
void output_damage_whole_local_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
	struct wlr_surface *surface, double ox, double oy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_render(struct roots_output *output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void scale_box(struct wlr_box *box, float scale);
 | 
					 | 
				
			||||||
void get_decoration_box(struct roots_view *view,
 | 
					 | 
				
			||||||
	struct roots_output *output, struct wlr_box *box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,183 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_SEAT_H
 | 
					 | 
				
			||||||
#define ROOTSTON_SEAT_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
#include "rootston/keyboard.h"
 | 
					 | 
				
			||||||
#include "rootston/layers.h"
 | 
					 | 
				
			||||||
#include "rootston/switch.h"
 | 
					 | 
				
			||||||
#include "rootston/text_input.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat {
 | 
					 | 
				
			||||||
	struct roots_input *input;
 | 
					 | 
				
			||||||
	struct wlr_seat *seat;
 | 
					 | 
				
			||||||
	struct roots_cursor *cursor;
 | 
					 | 
				
			||||||
	struct wl_list link; // roots_input::seats
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// coordinates of the first touch point if it exists
 | 
					 | 
				
			||||||
	int32_t touch_id;
 | 
					 | 
				
			||||||
	double touch_x, touch_y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// If the focused layer is set, views cannot receive keyboard focus
 | 
					 | 
				
			||||||
	struct wlr_layer_surface_v1 *focused_layer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_input_method_relay im_relay;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// If non-null, only this client can receive input events
 | 
					 | 
				
			||||||
	struct wl_client *exclusive_client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list views; // roots_seat_view::link
 | 
					 | 
				
			||||||
	bool has_focus;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_drag_icon *drag_icon; // can be NULL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list keyboards;
 | 
					 | 
				
			||||||
	struct wl_list pointers;
 | 
					 | 
				
			||||||
	struct wl_list switches;
 | 
					 | 
				
			||||||
	struct wl_list touch;
 | 
					 | 
				
			||||||
	struct wl_list tablets;
 | 
					 | 
				
			||||||
	struct wl_list tablet_pads;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener request_set_selection;
 | 
					 | 
				
			||||||
	struct wl_listener request_set_primary_selection;
 | 
					 | 
				
			||||||
	struct wl_listener request_start_drag;
 | 
					 | 
				
			||||||
	struct wl_listener start_drag;
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat_view {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool has_button_grab;
 | 
					 | 
				
			||||||
	double grab_sx;
 | 
					 | 
				
			||||||
	double grab_sy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list link; // roots_seat::views
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener view_unmap;
 | 
					 | 
				
			||||||
	struct wl_listener view_destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_drag_icon {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_drag_icon *wlr_drag_icon;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double x, y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener surface_commit;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_pointer {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_input_device *device;
 | 
					 | 
				
			||||||
	struct wl_listener device_destroy;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_touch {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_input_device *device;
 | 
					 | 
				
			||||||
	struct wl_listener device_destroy;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_tablet {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_input_device *device;
 | 
					 | 
				
			||||||
	struct wlr_tablet_v2_tablet *tablet_v2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener device_destroy;
 | 
					 | 
				
			||||||
	struct wl_listener axis;
 | 
					 | 
				
			||||||
	struct wl_listener proximity;
 | 
					 | 
				
			||||||
	struct wl_listener tip;
 | 
					 | 
				
			||||||
	struct wl_listener button;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_tablet_pad {
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
	struct wlr_tablet_v2_tablet_pad *tablet_v2_pad;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_input_device *device;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener device_destroy;
 | 
					 | 
				
			||||||
	struct wl_listener attach;
 | 
					 | 
				
			||||||
	struct wl_listener button;
 | 
					 | 
				
			||||||
	struct wl_listener ring;
 | 
					 | 
				
			||||||
	struct wl_listener strip;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_tablet *tablet;
 | 
					 | 
				
			||||||
	struct wl_listener tablet_destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_tablet_tool {
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
	struct wl_list tool_link;
 | 
					 | 
				
			||||||
	struct wlr_tablet_v2_tablet_tool *tablet_v2_tool;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	double tilt_x, tilt_y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener set_cursor;
 | 
					 | 
				
			||||||
	struct wl_listener tool_destroy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_tablet *current_tablet;
 | 
					 | 
				
			||||||
	struct wl_listener tablet_destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_pointer_constraint {
 | 
					 | 
				
			||||||
	struct wlr_pointer_constraint_v1 *constraint;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat *roots_seat_create(struct roots_input *input, char *name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_destroy(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_add_device(struct roots_seat *seat,
 | 
					 | 
				
			||||||
		struct wlr_input_device *device);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_configure_cursor(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_configure_xcursor(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool roots_seat_has_meta_pressed(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view *roots_seat_get_focus(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_set_focus_layer(struct roots_seat *seat,
 | 
					 | 
				
			||||||
		struct wlr_layer_surface_v1 *layer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_cycle_focus(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_begin_move(struct roots_seat *seat, struct roots_view *view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_begin_resize(struct roots_seat *seat, struct roots_view *view,
 | 
					 | 
				
			||||||
		uint32_t edges);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_begin_rotate(struct roots_seat *seat, struct roots_view *view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_end_compositor_grab(struct roots_seat *seat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat_view *roots_seat_view_from_view( struct roots_seat *seat,
 | 
					 | 
				
			||||||
	struct roots_view *view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_drag_icon_update_position(struct roots_drag_icon *icon);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_drag_icon_damage_whole(struct roots_drag_icon *icon);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_seat_set_exclusive_client(struct roots_seat *seat,
 | 
					 | 
				
			||||||
		struct wl_client *client);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool roots_seat_allow_input(struct roots_seat *seat,
 | 
					 | 
				
			||||||
		struct wl_resource *resource);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,37 +0,0 @@
 | 
				
			||||||
#ifndef _ROOTSTON_SERVER_H
 | 
					 | 
				
			||||||
#define _ROOTSTON_SERVER_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/backend.h>
 | 
					 | 
				
			||||||
#include <wlr/backend/session.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/render/wlr_renderer.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_data_device.h>
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
#include <wlr/xwayland.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#include "rootston/config.h"
 | 
					 | 
				
			||||||
#include "rootston/desktop.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_server {
 | 
					 | 
				
			||||||
	/* Rootston resources */
 | 
					 | 
				
			||||||
	struct roots_config *config;
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop;
 | 
					 | 
				
			||||||
	struct roots_input *input;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Wayland resources */
 | 
					 | 
				
			||||||
	struct wl_display *wl_display;
 | 
					 | 
				
			||||||
	struct wl_event_loop *wl_event_loop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* WLR tools */
 | 
					 | 
				
			||||||
	struct wlr_backend *backend;
 | 
					 | 
				
			||||||
	struct wlr_renderer *renderer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Global resources */
 | 
					 | 
				
			||||||
	struct wlr_data_device_manager *data_device_manager;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern struct roots_server server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,18 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_SWITCH_H
 | 
					 | 
				
			||||||
#define ROOTSTON_SWITCH_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_switch {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	struct wlr_input_device *device;
 | 
					 | 
				
			||||||
	struct wl_listener device_destroy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener toggle;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_switch_handle_toggle(struct roots_switch *switch_device,
 | 
					 | 
				
			||||||
		struct wlr_event_switch_toggle *event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,63 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_TEXT_INPUT_H
 | 
					 | 
				
			||||||
#define ROOTSTON_TEXT_INPUT_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_text_input_v3.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_input_method_v2.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_surface.h>
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * The relay structure manages the relationship between text-input and
 | 
					 | 
				
			||||||
 * input_method interfaces on a given seat. Multiple text-input interfaces may
 | 
					 | 
				
			||||||
 * be bound to a relay, but at most one will be focused (reveiving events) at
 | 
					 | 
				
			||||||
 * a time. At most one input-method interface may be bound to the seat. The
 | 
					 | 
				
			||||||
 * relay manages life cycle of both sides. When both sides are present and
 | 
					 | 
				
			||||||
 * focused, the relay passes messages between them.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Text input focus is a subset of keyboard focus - if the text-input is
 | 
					 | 
				
			||||||
 * in the focused state, wl_keyboard sent an enter as well. However, having
 | 
					 | 
				
			||||||
 * wl_keyboard focused doesn't mean that text-input will be focused.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct roots_input_method_relay {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list text_inputs; // roots_text_input::link
 | 
					 | 
				
			||||||
	struct wlr_input_method_v2 *input_method; // doesn't have to be present
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener text_input_new;
 | 
					 | 
				
			||||||
	struct wl_listener text_input_enable;
 | 
					 | 
				
			||||||
	struct wl_listener text_input_commit;
 | 
					 | 
				
			||||||
	struct wl_listener text_input_disable;
 | 
					 | 
				
			||||||
	struct wl_listener text_input_destroy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener input_method_new;
 | 
					 | 
				
			||||||
	struct wl_listener input_method_commit;
 | 
					 | 
				
			||||||
	struct wl_listener input_method_destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_text_input {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_text_input_v3 *input;
 | 
					 | 
				
			||||||
	// The surface getting seat's focus. Stored for when text-input cannot
 | 
					 | 
				
			||||||
	// be sent an enter event immediately after getting focus, e.g. when
 | 
					 | 
				
			||||||
	// there's no input method available. Cleared once text-input is entered.
 | 
					 | 
				
			||||||
	struct wlr_surface *pending_focused_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener pending_focused_surface_destroy;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_input_method_relay_init(struct roots_seat *seat,
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Updates currently focused surface. Surface must belong to the same seat.
 | 
					 | 
				
			||||||
void roots_input_method_relay_set_focus(struct roots_input_method_relay *relay,
 | 
					 | 
				
			||||||
	struct wlr_surface *surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_text_input *roots_text_input_create(
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay,
 | 
					 | 
				
			||||||
	struct wlr_text_input_v3 *text_input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,257 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_VIEW_H
 | 
					 | 
				
			||||||
#define ROOTSTON_VIEW_H
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_surface.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell_v6.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view_interface {
 | 
					 | 
				
			||||||
	void (*activate)(struct roots_view *view, bool active);
 | 
					 | 
				
			||||||
	void (*move)(struct roots_view *view, double x, double y);
 | 
					 | 
				
			||||||
	void (*resize)(struct roots_view *view, uint32_t width, uint32_t height);
 | 
					 | 
				
			||||||
	void (*move_resize)(struct roots_view *view, double x, double y,
 | 
					 | 
				
			||||||
		uint32_t width, uint32_t height);
 | 
					 | 
				
			||||||
	void (*maximize)(struct roots_view *view, bool maximized);
 | 
					 | 
				
			||||||
	void (*set_fullscreen)(struct roots_view *view, bool fullscreen);
 | 
					 | 
				
			||||||
	void (*close)(struct roots_view *view);
 | 
					 | 
				
			||||||
	void (*for_each_surface)(struct roots_view *view,
 | 
					 | 
				
			||||||
		wlr_surface_iterator_func_t iterator, void *user_data);
 | 
					 | 
				
			||||||
	void (*destroy)(struct roots_view *view);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum roots_view_type {
 | 
					 | 
				
			||||||
	ROOTS_XDG_SHELL_V6_VIEW,
 | 
					 | 
				
			||||||
	ROOTS_XDG_SHELL_VIEW,
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	ROOTS_XWAYLAND_VIEW,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view {
 | 
					 | 
				
			||||||
	enum roots_view_type type;
 | 
					 | 
				
			||||||
	const struct roots_view_interface *impl;
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop;
 | 
					 | 
				
			||||||
	struct wl_list link; // roots_desktop::views
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	float rotation;
 | 
					 | 
				
			||||||
	float alpha;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool decorated;
 | 
					 | 
				
			||||||
	int border_width;
 | 
					 | 
				
			||||||
	int titlebar_height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool maximized;
 | 
					 | 
				
			||||||
	struct roots_output *fullscreen_output;
 | 
					 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		double x, y;
 | 
					 | 
				
			||||||
		uint32_t width, height;
 | 
					 | 
				
			||||||
		float rotation;
 | 
					 | 
				
			||||||
	} saved;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		bool update_x, update_y;
 | 
					 | 
				
			||||||
		double x, y;
 | 
					 | 
				
			||||||
		uint32_t width, height;
 | 
					 | 
				
			||||||
	} pending_move_resize;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_surface *wlr_surface;
 | 
					 | 
				
			||||||
	struct wl_list children; // roots_view_child::link
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_foreign_toplevel_handle_v1 *toplevel_handle;
 | 
					 | 
				
			||||||
	struct wl_listener toplevel_handle_request_maximize;
 | 
					 | 
				
			||||||
	struct wl_listener toplevel_handle_request_activate;
 | 
					 | 
				
			||||||
	struct wl_listener toplevel_handle_request_fullscreen;
 | 
					 | 
				
			||||||
	struct wl_listener toplevel_handle_request_close;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener new_subsurface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		struct wl_signal unmap;
 | 
					 | 
				
			||||||
		struct wl_signal destroy;
 | 
					 | 
				
			||||||
	} events;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_surface_v6 {
 | 
					 | 
				
			||||||
	struct roots_view view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *xdg_surface_v6;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener new_popup;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener request_move;
 | 
					 | 
				
			||||||
	struct wl_listener request_resize;
 | 
					 | 
				
			||||||
	struct wl_listener request_maximize;
 | 
					 | 
				
			||||||
	struct wl_listener request_fullscreen;
 | 
					 | 
				
			||||||
	struct wl_listener set_title;
 | 
					 | 
				
			||||||
	struct wl_listener set_app_id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener surface_commit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t pending_move_resize_configure_serial;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_toplevel_decoration;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_surface {
 | 
					 | 
				
			||||||
	struct roots_view view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener new_popup;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener request_move;
 | 
					 | 
				
			||||||
	struct wl_listener request_resize;
 | 
					 | 
				
			||||||
	struct wl_listener request_maximize;
 | 
					 | 
				
			||||||
	struct wl_listener request_fullscreen;
 | 
					 | 
				
			||||||
	struct wl_listener set_title;
 | 
					 | 
				
			||||||
	struct wl_listener set_app_id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener surface_commit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t pending_move_resize_configure_serial;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_xdg_toplevel_decoration *xdg_toplevel_decoration;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
struct roots_xwayland_surface {
 | 
					 | 
				
			||||||
	struct roots_view view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener request_configure;
 | 
					 | 
				
			||||||
	struct wl_listener request_move;
 | 
					 | 
				
			||||||
	struct wl_listener request_resize;
 | 
					 | 
				
			||||||
	struct wl_listener request_maximize;
 | 
					 | 
				
			||||||
	struct wl_listener request_fullscreen;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener set_title;
 | 
					 | 
				
			||||||
	struct wl_listener set_class;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener surface_commit;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view_child;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view_child_interface {
 | 
					 | 
				
			||||||
	void (*destroy)(struct roots_view_child *child);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_view_child {
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
	const struct roots_view_child_interface *impl;
 | 
					 | 
				
			||||||
	struct wlr_surface *wlr_surface;
 | 
					 | 
				
			||||||
	struct wl_list link;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_listener commit;
 | 
					 | 
				
			||||||
	struct wl_listener new_subsurface;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_subsurface {
 | 
					 | 
				
			||||||
	struct roots_view_child view_child;
 | 
					 | 
				
			||||||
	struct wlr_subsurface *wlr_subsurface;
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_popup_v6 {
 | 
					 | 
				
			||||||
	struct roots_view_child view_child;
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup_v6 *wlr_popup;
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener new_popup;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_popup {
 | 
					 | 
				
			||||||
	struct roots_view_child view_child;
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *wlr_popup;
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener map;
 | 
					 | 
				
			||||||
	struct wl_listener unmap;
 | 
					 | 
				
			||||||
	struct wl_listener new_popup;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_toplevel_decoration {
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration;
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *surface;
 | 
					 | 
				
			||||||
	struct wl_listener destroy;
 | 
					 | 
				
			||||||
	struct wl_listener request_mode;
 | 
					 | 
				
			||||||
	struct wl_listener surface_commit;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_init(struct roots_view *view, const struct roots_view_interface *impl,
 | 
					 | 
				
			||||||
	enum roots_view_type type, struct roots_desktop *desktop);
 | 
					 | 
				
			||||||
void view_destroy(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_apply_damage(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_damage_whole(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_update_position(struct roots_view *view, int x, int y);
 | 
					 | 
				
			||||||
void view_update_size(struct roots_view *view, int width, int height);
 | 
					 | 
				
			||||||
void view_update_decorated(struct roots_view *view, bool decorated);
 | 
					 | 
				
			||||||
void view_initial_focus(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_map(struct roots_view *view, struct wlr_surface *surface);
 | 
					 | 
				
			||||||
void view_unmap(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_arrange_maximized(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_get_box(const struct roots_view *view, struct wlr_box *box);
 | 
					 | 
				
			||||||
void view_activate(struct roots_view *view, bool active);
 | 
					 | 
				
			||||||
void view_move(struct roots_view *view, double x, double y);
 | 
					 | 
				
			||||||
void view_resize(struct roots_view *view, uint32_t width, uint32_t height);
 | 
					 | 
				
			||||||
void view_move_resize(struct roots_view *view, double x, double y,
 | 
					 | 
				
			||||||
	uint32_t width, uint32_t height);
 | 
					 | 
				
			||||||
void view_maximize(struct roots_view *view, bool maximized);
 | 
					 | 
				
			||||||
void view_set_fullscreen(struct roots_view *view, bool fullscreen,
 | 
					 | 
				
			||||||
	struct wlr_output *output);
 | 
					 | 
				
			||||||
void view_rotate(struct roots_view *view, float rotation);
 | 
					 | 
				
			||||||
void view_cycle_alpha(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_close(struct roots_view *view);
 | 
					 | 
				
			||||||
bool view_center(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_setup(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_teardown(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_set_title(struct roots_view *view, const char *title);
 | 
					 | 
				
			||||||
void view_set_app_id(struct roots_view *view, const char *app_id);
 | 
					 | 
				
			||||||
void view_create_foreign_toplevel_handle(struct roots_view *view);
 | 
					 | 
				
			||||||
void view_get_deco_box(const struct roots_view *view, struct wlr_box *box);
 | 
					 | 
				
			||||||
void view_for_each_surface(struct roots_view *view,
 | 
					 | 
				
			||||||
	wlr_surface_iterator_func_t iterator, void *user_data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_surface *roots_xdg_surface_from_view(struct roots_view *view);
 | 
					 | 
				
			||||||
struct roots_xdg_surface_v6 *roots_xdg_surface_v6_from_view(
 | 
					 | 
				
			||||||
	struct roots_view *view);
 | 
					 | 
				
			||||||
struct roots_xwayland_surface *roots_xwayland_surface_from_view(
 | 
					 | 
				
			||||||
	struct roots_view *view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum roots_deco_part {
 | 
					 | 
				
			||||||
	ROOTS_DECO_PART_NONE = 0,
 | 
					 | 
				
			||||||
	ROOTS_DECO_PART_TOP_BORDER = (1 << 0),
 | 
					 | 
				
			||||||
	ROOTS_DECO_PART_BOTTOM_BORDER = (1 << 1),
 | 
					 | 
				
			||||||
	ROOTS_DECO_PART_LEFT_BORDER = (1 << 2),
 | 
					 | 
				
			||||||
	ROOTS_DECO_PART_RIGHT_BORDER = (1 << 3),
 | 
					 | 
				
			||||||
	ROOTS_DECO_PART_TITLEBAR = (1 << 4),
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum roots_deco_part view_get_deco_part(struct roots_view *view, double sx, double sy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_child_init(struct roots_view_child *child,
 | 
					 | 
				
			||||||
	const struct roots_view_child_interface *impl, struct roots_view *view,
 | 
					 | 
				
			||||||
	struct wlr_surface *wlr_surface);
 | 
					 | 
				
			||||||
void view_child_destroy(struct roots_view_child *child);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_subsurface *subsurface_create(struct roots_view *view,
 | 
					 | 
				
			||||||
	struct wlr_subsurface *wlr_subsurface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_VIRTUAL_KEYBOARD_H
 | 
					 | 
				
			||||||
#define ROOTSTON_VIRTUAL_KEYBOARD_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_virtual_keyboard(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,12 +0,0 @@
 | 
				
			||||||
#ifndef ROOTSTON_XCURSOR_H
 | 
					 | 
				
			||||||
#define ROOTSTON_XCURSOR_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ROOTS_XCURSOR_SIZE 24
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ROOTS_XCURSOR_DEFAULT "left_ptr"
 | 
					 | 
				
			||||||
#define ROOTS_XCURSOR_MOVE "grabbing"
 | 
					 | 
				
			||||||
#define ROOTS_XCURSOR_ROTATE "grabbing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -202,7 +202,6 @@ summary = [
 | 
				
			||||||
message('\n'.join(summary))
 | 
					message('\n'.join(summary))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
subdir('examples')
 | 
					subdir('examples')
 | 
				
			||||||
subdir('rootston')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
pkgconfig = import('pkgconfig')
 | 
					pkgconfig = import('pkgconfig')
 | 
				
			||||||
pkgconfig.generate(
 | 
					pkgconfig.generate(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,5 +6,4 @@ option('xcb-errors', type: 'feature', value: 'auto', description: 'Use xcb-error
 | 
				
			||||||
option('xcb-icccm', type: 'feature', value: 'auto', description: 'Use xcb-icccm util library')
 | 
					option('xcb-icccm', type: 'feature', value: 'auto', description: 'Use xcb-icccm util library')
 | 
				
			||||||
option('xwayland', type: 'feature', value: 'auto', yield: true, description: 'Enable support for X11 applications')
 | 
					option('xwayland', type: 'feature', value: 'auto', yield: true, description: 'Enable support for X11 applications')
 | 
				
			||||||
option('x11-backend', type: 'feature', value: 'auto', description: 'Enable X11 backend')
 | 
					option('x11-backend', type: 'feature', value: 'auto', description: 'Enable X11 backend')
 | 
				
			||||||
option('rootston', type: 'boolean', value: true, description: 'Build the rootston example compositor')
 | 
					 | 
				
			||||||
option('examples', type: 'boolean', value: true, description: 'Build example applications')
 | 
					option('examples', type: 'boolean', value: true, description: 'Build example applications')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,18 +0,0 @@
 | 
				
			||||||
# rootston
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Rootston is the "big" wlroots test compositor. It implements basically every
 | 
					 | 
				
			||||||
feature of wlroots and may be useful as a reference for new compositors.
 | 
					 | 
				
			||||||
However, it's mostly used as a testbed for wlroots development and does not have
 | 
					 | 
				
			||||||
particularly clean code and is not particularly well designed: proceed with a
 | 
					 | 
				
			||||||
grain of salt. It is not designed for end-users.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Running rootston
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you followed the build instructions in `../README.md`, the rootston
 | 
					 | 
				
			||||||
executable can be found at `build/rootston/rootston`. To use it, refer to the
 | 
					 | 
				
			||||||
example config at [rootston/rootston.ini.example][rootston.ini] and place a
 | 
					 | 
				
			||||||
config file of your own at `rootston.ini` in the working directory (or in an
 | 
					 | 
				
			||||||
arbitrary location via `rootston -C`). Other options are available, refer to
 | 
					 | 
				
			||||||
`rootston -h`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[rootston.ini]: https://github.com/swaywm/wlroots/blob/master/rootston/rootston.ini.example
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,110 +0,0 @@
 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
#include <sys/types.h>
 | 
					 | 
				
			||||||
#include <sys/wait.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/bindings.h"
 | 
					 | 
				
			||||||
#include "rootston/view.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool outputs_enabled = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const char exec_prefix[] = "exec ";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void double_fork_shell_cmd(const char *shell_cmd) {
 | 
					 | 
				
			||||||
	pid_t pid = fork();
 | 
					 | 
				
			||||||
	if (pid < 0) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "cannot execute binding command: fork() failed");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (pid == 0) {
 | 
					 | 
				
			||||||
		pid = fork();
 | 
					 | 
				
			||||||
		if (pid == 0) {
 | 
					 | 
				
			||||||
			execl("/bin/sh", "/bin/sh", "-c", shell_cmd, NULL);
 | 
					 | 
				
			||||||
			_exit(EXIT_FAILURE);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			_exit(pid == -1);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int status;
 | 
					 | 
				
			||||||
	while (waitpid(pid, &status, 0) < 0) {
 | 
					 | 
				
			||||||
		if (errno == EINTR) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		wlr_log_errno(WLR_ERROR, "waitpid() on first child failed");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_ERROR, "first child failed to fork command");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void execute_binding_command(struct roots_seat *seat,
 | 
					 | 
				
			||||||
		struct roots_input *input, const char *command) {
 | 
					 | 
				
			||||||
	if (strcmp(command, "exit") == 0) {
 | 
					 | 
				
			||||||
		wl_display_terminate(input->server->wl_display);
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "close") == 0) {
 | 
					 | 
				
			||||||
		struct roots_view *focus = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (focus != NULL) {
 | 
					 | 
				
			||||||
			view_close(focus);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "fullscreen") == 0) {
 | 
					 | 
				
			||||||
		struct roots_view *focus = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (focus != NULL) {
 | 
					 | 
				
			||||||
			bool is_fullscreen = focus->fullscreen_output != NULL;
 | 
					 | 
				
			||||||
			view_set_fullscreen(focus, !is_fullscreen, NULL);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "next_window") == 0) {
 | 
					 | 
				
			||||||
		roots_seat_cycle_focus(seat);
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "alpha") == 0) {
 | 
					 | 
				
			||||||
		struct roots_view *focus = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (focus != NULL) {
 | 
					 | 
				
			||||||
			view_cycle_alpha(focus);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strncmp(exec_prefix, command, strlen(exec_prefix)) == 0) {
 | 
					 | 
				
			||||||
		const char *shell_cmd = command + strlen(exec_prefix);
 | 
					 | 
				
			||||||
		double_fork_shell_cmd(shell_cmd);
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "maximize") == 0) {
 | 
					 | 
				
			||||||
		struct roots_view *focus = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (focus != NULL) {
 | 
					 | 
				
			||||||
			view_maximize(focus, !focus->maximized);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "nop") == 0) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "nop command");
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "toggle_outputs") == 0) {
 | 
					 | 
				
			||||||
		outputs_enabled = !outputs_enabled;
 | 
					 | 
				
			||||||
		struct roots_output *output;
 | 
					 | 
				
			||||||
		wl_list_for_each(output, &input->server->desktop->outputs, link) {
 | 
					 | 
				
			||||||
			wlr_output_enable(output->wlr_output, outputs_enabled);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "toggle_decoration_mode") == 0) {
 | 
					 | 
				
			||||||
		struct roots_view *focus = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (focus != NULL && focus->type == ROOTS_XDG_SHELL_VIEW) {
 | 
					 | 
				
			||||||
			struct roots_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
				roots_xdg_surface_from_view(focus);
 | 
					 | 
				
			||||||
			struct roots_xdg_toplevel_decoration *decoration =
 | 
					 | 
				
			||||||
				xdg_surface->xdg_toplevel_decoration;
 | 
					 | 
				
			||||||
			if (decoration != NULL) {
 | 
					 | 
				
			||||||
				enum wlr_xdg_toplevel_decoration_v1_mode mode =
 | 
					 | 
				
			||||||
					decoration->wlr_decoration->current_mode;
 | 
					 | 
				
			||||||
				mode = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
 | 
					 | 
				
			||||||
					? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
 | 
					 | 
				
			||||||
					: WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
 | 
					 | 
				
			||||||
				wlr_xdg_toplevel_decoration_v1_set_mode(
 | 
					 | 
				
			||||||
					decoration->wlr_decoration, mode);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strcmp(command, "break_pointer_constraint") == 0) {
 | 
					 | 
				
			||||||
		struct wl_list *list = &input->seats;
 | 
					 | 
				
			||||||
		struct roots_seat *seat;
 | 
					 | 
				
			||||||
		wl_list_for_each(seat, list, link) {
 | 
					 | 
				
			||||||
			roots_cursor_constrain(seat->cursor, NULL, NAN, NAN);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "unknown binding command: %s", command);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,674 +0,0 @@
 | 
				
			||||||
#ifndef _POSIX_C_SOURCE
 | 
					 | 
				
			||||||
#define _POSIX_C_SOURCE 200809L
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <getopt.h>
 | 
					 | 
				
			||||||
#include <limits.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <strings.h>
 | 
					 | 
				
			||||||
#include <sys/param.h>
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/config.h"
 | 
					 | 
				
			||||||
#include "rootston/ini.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
#include "rootston/keyboard.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void usage(const char *name, int ret) {
 | 
					 | 
				
			||||||
	fprintf(stderr,
 | 
					 | 
				
			||||||
		"usage: %s [-C <FILE>] [-E <COMMAND>]\n"
 | 
					 | 
				
			||||||
		"\n"
 | 
					 | 
				
			||||||
		" -C <FILE>      Path to the configuration file\n"
 | 
					 | 
				
			||||||
		"                (default: rootston.ini).\n"
 | 
					 | 
				
			||||||
		"                See `rootston.ini.example` for config\n"
 | 
					 | 
				
			||||||
		"                file documentation.\n"
 | 
					 | 
				
			||||||
		" -E <COMMAND>   Command that will be ran at startup.\n"
 | 
					 | 
				
			||||||
		" -D             Enable damage tracking debugging.\n"
 | 
					 | 
				
			||||||
		" -l <LEVEL>     Set log verbosity, where,\n"
 | 
					 | 
				
			||||||
		"                0:SILENT, 1:ERROR, 2:INFO, 3+:DEBUG\n"
 | 
					 | 
				
			||||||
		"                (default: DEBUG)\n",
 | 
					 | 
				
			||||||
		name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	exit(ret);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct wlr_box *parse_geometry(const char *str) {
 | 
					 | 
				
			||||||
	// format: {width}x{height}+{x}+{y}
 | 
					 | 
				
			||||||
	if (strlen(str) > 255) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "cannot parse geometry string, too long");
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	char *buf = strdup(str);
 | 
					 | 
				
			||||||
	struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool has_width = false;
 | 
					 | 
				
			||||||
	bool has_height = false;
 | 
					 | 
				
			||||||
	bool has_x = false;
 | 
					 | 
				
			||||||
	bool has_y = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	char *pch = strtok(buf, "x+");
 | 
					 | 
				
			||||||
	while (pch != NULL) {
 | 
					 | 
				
			||||||
		errno = 0;
 | 
					 | 
				
			||||||
		char *endptr;
 | 
					 | 
				
			||||||
		long val = strtol(pch, &endptr, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) ||
 | 
					 | 
				
			||||||
				(errno != 0 && val == 0)) {
 | 
					 | 
				
			||||||
			goto invalid_input;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (endptr == pch) {
 | 
					 | 
				
			||||||
			goto invalid_input;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!has_width) {
 | 
					 | 
				
			||||||
			box->width = val;
 | 
					 | 
				
			||||||
			has_width = true;
 | 
					 | 
				
			||||||
		} else if (!has_height) {
 | 
					 | 
				
			||||||
			box->height = val;
 | 
					 | 
				
			||||||
			has_height = true;
 | 
					 | 
				
			||||||
		} else if (!has_x) {
 | 
					 | 
				
			||||||
			box->x = val;
 | 
					 | 
				
			||||||
			has_x = true;
 | 
					 | 
				
			||||||
		} else if (!has_y) {
 | 
					 | 
				
			||||||
			box->y = val;
 | 
					 | 
				
			||||||
			has_y = true;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		pch = strtok(NULL, "x+");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!has_width || !has_height) {
 | 
					 | 
				
			||||||
		goto invalid_input;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	free(buf);
 | 
					 | 
				
			||||||
	return box;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
invalid_input:
 | 
					 | 
				
			||||||
	wlr_log(WLR_ERROR, "could not parse geometry string: %s", str);
 | 
					 | 
				
			||||||
	free(buf);
 | 
					 | 
				
			||||||
	free(box);
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static uint32_t parse_modifier(const char *symname) {
 | 
					 | 
				
			||||||
	if (strcmp(symname, "Shift") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_SHIFT;
 | 
					 | 
				
			||||||
	} else if (strcmp(symname, "Caps") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_CAPS;
 | 
					 | 
				
			||||||
	} else if (strcmp(symname, "Ctrl") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_CTRL;
 | 
					 | 
				
			||||||
	} else if (strcmp(symname, "Alt") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_ALT;
 | 
					 | 
				
			||||||
	} else if (strcmp(symname, "Mod2") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_MOD2;
 | 
					 | 
				
			||||||
	} else if (strcmp(symname, "Mod3") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_MOD3;
 | 
					 | 
				
			||||||
	} else if (strcmp(symname, "Logo") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_LOGO;
 | 
					 | 
				
			||||||
	} else if (strcmp(symname, "Mod5") == 0) {
 | 
					 | 
				
			||||||
		return WLR_MODIFIER_MOD5;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool parse_modeline(const char *s, drmModeModeInfo *mode) {
 | 
					 | 
				
			||||||
	char hsync[16];
 | 
					 | 
				
			||||||
	char vsync[16];
 | 
					 | 
				
			||||||
	float fclock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mode->type = DRM_MODE_TYPE_USERDEF;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (sscanf(s, "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s",
 | 
					 | 
				
			||||||
		   &fclock,
 | 
					 | 
				
			||||||
		   &mode->hdisplay,
 | 
					 | 
				
			||||||
		   &mode->hsync_start,
 | 
					 | 
				
			||||||
		   &mode->hsync_end,
 | 
					 | 
				
			||||||
		   &mode->htotal,
 | 
					 | 
				
			||||||
		   &mode->vdisplay,
 | 
					 | 
				
			||||||
		   &mode->vsync_start,
 | 
					 | 
				
			||||||
		   &mode->vsync_end,
 | 
					 | 
				
			||||||
		   &mode->vtotal, hsync, vsync) != 11) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mode->clock = fclock * 1000;
 | 
					 | 
				
			||||||
	mode->vrefresh = mode->clock * 1000.0 * 1000.0
 | 
					 | 
				
			||||||
		/ mode->htotal / mode->vtotal;
 | 
					 | 
				
			||||||
	if (strcasecmp(hsync, "+hsync") == 0) {
 | 
					 | 
				
			||||||
		mode->flags |= DRM_MODE_FLAG_PHSYNC;
 | 
					 | 
				
			||||||
	} else if (strcasecmp(hsync, "-hsync") == 0) {
 | 
					 | 
				
			||||||
		mode->flags |= DRM_MODE_FLAG_NHSYNC;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strcasecmp(vsync, "+vsync") == 0) {
 | 
					 | 
				
			||||||
		mode->flags |= DRM_MODE_FLAG_PVSYNC;
 | 
					 | 
				
			||||||
	} else if (strcasecmp(vsync, "-vsync") == 0) {
 | 
					 | 
				
			||||||
		mode->flags |= DRM_MODE_FLAG_NVSYNC;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	snprintf(mode->name, sizeof(mode->name), "%dx%d@%d",
 | 
					 | 
				
			||||||
		 mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void add_binding_config(struct wl_list *bindings, const char* combination,
 | 
					 | 
				
			||||||
		const char* command) {
 | 
					 | 
				
			||||||
	struct roots_binding_config *bc =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_binding_config));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	xkb_keysym_t keysyms[ROOTS_KEYBOARD_PRESSED_KEYSYMS_CAP];
 | 
					 | 
				
			||||||
	char *symnames = strdup(combination);
 | 
					 | 
				
			||||||
	char *symname = strtok(symnames, "+");
 | 
					 | 
				
			||||||
	while (symname) {
 | 
					 | 
				
			||||||
		uint32_t modifier = parse_modifier(symname);
 | 
					 | 
				
			||||||
		if (modifier != 0) {
 | 
					 | 
				
			||||||
			bc->modifiers |= modifier;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			xkb_keysym_t sym = xkb_keysym_from_name(symname,
 | 
					 | 
				
			||||||
				XKB_KEYSYM_NO_FLAGS);
 | 
					 | 
				
			||||||
			if (sym == XKB_KEY_NoSymbol) {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "got unknown key binding symbol: %s",
 | 
					 | 
				
			||||||
					symname);
 | 
					 | 
				
			||||||
				free(bc);
 | 
					 | 
				
			||||||
				bc = NULL;
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			keysyms[bc->keysyms_len] = sym;
 | 
					 | 
				
			||||||
			bc->keysyms_len++;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		symname = strtok(NULL, "+");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	free(symnames);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (bc) {
 | 
					 | 
				
			||||||
		wl_list_insert(bindings, &bc->link);
 | 
					 | 
				
			||||||
		bc->command = strdup(command);
 | 
					 | 
				
			||||||
		bc->keysyms = malloc(bc->keysyms_len * sizeof(xkb_keysym_t));
 | 
					 | 
				
			||||||
		memcpy(bc->keysyms, keysyms, bc->keysyms_len * sizeof(xkb_keysym_t));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void add_switch_config(struct wl_list *switches, const char *switch_name,
 | 
					 | 
				
			||||||
		const char *action, const char *command) {
 | 
					 | 
				
			||||||
	struct roots_switch_config *sc =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_switch_config));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strcmp(switch_name, "tablet") == 0) {
 | 
					 | 
				
			||||||
		sc->switch_type = WLR_SWITCH_TYPE_TABLET_MODE;
 | 
					 | 
				
			||||||
	} else if (strcmp(switch_name, "lid") == 0) {
 | 
					 | 
				
			||||||
		sc->switch_type = WLR_SWITCH_TYPE_LID;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		sc->switch_type = -1;
 | 
					 | 
				
			||||||
		sc->name = strdup(switch_name);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strcmp(action, "on") == 0) {
 | 
					 | 
				
			||||||
		sc->switch_state = WLR_SWITCH_STATE_ON;
 | 
					 | 
				
			||||||
	} else if (strcmp(action, "off") == 0) {
 | 
					 | 
				
			||||||
		sc->switch_state = WLR_SWITCH_STATE_OFF;
 | 
					 | 
				
			||||||
	} else if (strcmp(action, "toggle") == 0) {
 | 
					 | 
				
			||||||
		sc->switch_state = WLR_SWITCH_STATE_TOGGLE;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Invalid switch action %s for switch %s:%s",
 | 
					 | 
				
			||||||
			action, switch_name, action);
 | 
					 | 
				
			||||||
		free(sc);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sc->command = strdup(command);
 | 
					 | 
				
			||||||
	wl_list_insert(switches, &sc->link);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void config_handle_cursor(struct roots_config *config,
 | 
					 | 
				
			||||||
		const char *seat_name, const char *name, const char *value) {
 | 
					 | 
				
			||||||
	struct roots_cursor_config *cc;
 | 
					 | 
				
			||||||
	bool found = false;
 | 
					 | 
				
			||||||
	wl_list_for_each(cc, &config->cursors, link) {
 | 
					 | 
				
			||||||
		if (strcmp(cc->seat, seat_name) == 0) {
 | 
					 | 
				
			||||||
			found = true;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!found) {
 | 
					 | 
				
			||||||
		cc = calloc(1, sizeof(struct roots_cursor_config));
 | 
					 | 
				
			||||||
		cc->seat = strdup(seat_name);
 | 
					 | 
				
			||||||
		wl_list_insert(&config->cursors, &cc->link);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strcmp(name, "map-to-output") == 0) {
 | 
					 | 
				
			||||||
		free(cc->mapped_output);
 | 
					 | 
				
			||||||
		cc->mapped_output = strdup(value);
 | 
					 | 
				
			||||||
	} else if (strcmp(name, "geometry") == 0) {
 | 
					 | 
				
			||||||
		free(cc->mapped_box);
 | 
					 | 
				
			||||||
		cc->mapped_box = parse_geometry(value);
 | 
					 | 
				
			||||||
	} else if (strcmp(name, "theme") == 0) {
 | 
					 | 
				
			||||||
		free(cc->theme);
 | 
					 | 
				
			||||||
		cc->theme = strdup(value);
 | 
					 | 
				
			||||||
	} else if (strcmp(name, "default-image") == 0) {
 | 
					 | 
				
			||||||
		free(cc->default_image);
 | 
					 | 
				
			||||||
		cc->default_image = strdup(value);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "got unknown cursor config: %s", name);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void config_handle_keyboard(struct roots_config *config,
 | 
					 | 
				
			||||||
		const char *device_name, const char *name, const char *value) {
 | 
					 | 
				
			||||||
	struct roots_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 roots_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(WLR_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 if (strcmp(name, "repeat-rate") == 0) {
 | 
					 | 
				
			||||||
		kc->repeat_rate = strtol(value, NULL, 10);
 | 
					 | 
				
			||||||
	} else if (strcmp(name, "repeat-delay") == 0) {
 | 
					 | 
				
			||||||
		kc->repeat_delay = strtol(value, NULL, 10);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_log(WLR_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 const char *cursor_prefix = "cursor:";
 | 
					 | 
				
			||||||
static const char *switch_prefix = "switch:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int config_ini_handler(void *user, const char *section, const char *name,
 | 
					 | 
				
			||||||
		const char *value) {
 | 
					 | 
				
			||||||
	struct roots_config *config = user;
 | 
					 | 
				
			||||||
	if (strcmp(section, "core") == 0) {
 | 
					 | 
				
			||||||
		if (strcmp(name, "xwayland") == 0) {
 | 
					 | 
				
			||||||
			if (strcasecmp(value, "true") == 0) {
 | 
					 | 
				
			||||||
				config->xwayland = true;
 | 
					 | 
				
			||||||
			} else if (strcasecmp(value, "immediate") == 0) {
 | 
					 | 
				
			||||||
				config->xwayland = true;
 | 
					 | 
				
			||||||
				config->xwayland_lazy = false;
 | 
					 | 
				
			||||||
			} else if (strcasecmp(value, "false") == 0) {
 | 
					 | 
				
			||||||
				config->xwayland = false;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "got unknown xwayland value: %s", value);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			wlr_log(WLR_ERROR, "got unknown core config: %s", name);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strncmp(output_prefix, section, strlen(output_prefix)) == 0) {
 | 
					 | 
				
			||||||
		const char *output_name = section + strlen(output_prefix);
 | 
					 | 
				
			||||||
		struct roots_output_config *oc;
 | 
					 | 
				
			||||||
		bool found = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		wl_list_for_each(oc, &config->outputs, link) {
 | 
					 | 
				
			||||||
			if (strcmp(oc->name, output_name) == 0) {
 | 
					 | 
				
			||||||
				found = true;
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!found) {
 | 
					 | 
				
			||||||
			oc = calloc(1, sizeof(struct roots_output_config));
 | 
					 | 
				
			||||||
			oc->name = strdup(output_name);
 | 
					 | 
				
			||||||
			oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
 | 
					 | 
				
			||||||
			oc->scale = 1;
 | 
					 | 
				
			||||||
			oc->enable = true;
 | 
					 | 
				
			||||||
			wl_list_init(&oc->modes);
 | 
					 | 
				
			||||||
			wl_list_insert(&config->outputs, &oc->link);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (strcmp(name, "enable") == 0) {
 | 
					 | 
				
			||||||
			if (strcasecmp(value, "true") == 0) {
 | 
					 | 
				
			||||||
				oc->enable = true;
 | 
					 | 
				
			||||||
			} else if (strcasecmp(value, "false") == 0) {
 | 
					 | 
				
			||||||
				oc->enable = false;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "got invalid output enable value: %s", value);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "x") == 0) {
 | 
					 | 
				
			||||||
			oc->x = strtol(value, NULL, 10);
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "y") == 0) {
 | 
					 | 
				
			||||||
			oc->y = strtol(value, NULL, 10);
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "scale") == 0) {
 | 
					 | 
				
			||||||
			oc->scale = strtof(value, NULL);
 | 
					 | 
				
			||||||
			assert(oc->scale > 0);
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "rotate") == 0) {
 | 
					 | 
				
			||||||
			if (strcmp(value, "normal") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
 | 
					 | 
				
			||||||
			} else if (strcmp(value, "90") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_90;
 | 
					 | 
				
			||||||
			} else if (strcmp(value, "180") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_180;
 | 
					 | 
				
			||||||
			} else if (strcmp(value, "270") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_270;
 | 
					 | 
				
			||||||
			} else if (strcmp(value, "flipped") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
 | 
					 | 
				
			||||||
			} else if (strcmp(value, "flipped-90") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
 | 
					 | 
				
			||||||
			} else if (strcmp(value, "flipped-180") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
 | 
					 | 
				
			||||||
			} else if (strcmp(value, "flipped-270") == 0) {
 | 
					 | 
				
			||||||
				oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "got unknown transform value: %s", value);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "mode") == 0) {
 | 
					 | 
				
			||||||
			char *end;
 | 
					 | 
				
			||||||
			oc->mode.width = strtol(value, &end, 10);
 | 
					 | 
				
			||||||
			assert(*end == 'x');
 | 
					 | 
				
			||||||
			++end;
 | 
					 | 
				
			||||||
			oc->mode.height = strtol(end, &end, 10);
 | 
					 | 
				
			||||||
			if (*end) {
 | 
					 | 
				
			||||||
				assert(*end == '@');
 | 
					 | 
				
			||||||
				++end;
 | 
					 | 
				
			||||||
				oc->mode.refresh_rate = strtof(end, &end);
 | 
					 | 
				
			||||||
				assert(strcmp("Hz", end) == 0);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			wlr_log(WLR_DEBUG, "Configured output %s with mode %dx%d@%f",
 | 
					 | 
				
			||||||
					oc->name, oc->mode.width, oc->mode.height,
 | 
					 | 
				
			||||||
					oc->mode.refresh_rate);
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "modeline") == 0) {
 | 
					 | 
				
			||||||
			struct roots_output_mode_config *mode = calloc(1, sizeof(*mode));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (parse_modeline(value, &mode->info)) {
 | 
					 | 
				
			||||||
				wl_list_insert(&oc->modes, &mode->link);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				free(mode);
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "Invalid modeline: %s", value);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strncmp(cursor_prefix, section, strlen(cursor_prefix)) == 0) {
 | 
					 | 
				
			||||||
		const char *seat_name = section + strlen(cursor_prefix);
 | 
					 | 
				
			||||||
		config_handle_cursor(config, seat_name, name, value);
 | 
					 | 
				
			||||||
	} else if (strcmp(section, "cursor") == 0) {
 | 
					 | 
				
			||||||
		config_handle_cursor(config, ROOTS_CONFIG_DEFAULT_SEAT_NAME, name,
 | 
					 | 
				
			||||||
			value);
 | 
					 | 
				
			||||||
	} else if (strncmp(device_prefix, section, strlen(device_prefix)) == 0) {
 | 
					 | 
				
			||||||
		const char *device_name = section + strlen(device_prefix);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct roots_device_config *dc;
 | 
					 | 
				
			||||||
		bool found = false;
 | 
					 | 
				
			||||||
		wl_list_for_each(dc, &config->devices, link) {
 | 
					 | 
				
			||||||
			if (strcmp(dc->name, device_name) == 0) {
 | 
					 | 
				
			||||||
				found = true;
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!found) {
 | 
					 | 
				
			||||||
			dc = calloc(1, sizeof(struct roots_device_config));
 | 
					 | 
				
			||||||
			dc->name = strdup(device_name);
 | 
					 | 
				
			||||||
			dc->seat = strdup(ROOTS_CONFIG_DEFAULT_SEAT_NAME);
 | 
					 | 
				
			||||||
			wl_list_insert(&config->devices, &dc->link);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (strcmp(name, "map-to-output") == 0) {
 | 
					 | 
				
			||||||
			free(dc->mapped_output);
 | 
					 | 
				
			||||||
			dc->mapped_output = strdup(value);
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "geometry") == 0) {
 | 
					 | 
				
			||||||
			free(dc->mapped_box);
 | 
					 | 
				
			||||||
			dc->mapped_box = parse_geometry(value);
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "seat") == 0) {
 | 
					 | 
				
			||||||
			free(dc->seat);
 | 
					 | 
				
			||||||
			dc->seat = strdup(value);
 | 
					 | 
				
			||||||
		} else if (strcmp(name, "tap_enabled") == 0) {
 | 
					 | 
				
			||||||
			if (strcasecmp(value, "true") == 0) {
 | 
					 | 
				
			||||||
				dc->tap_enabled = true;
 | 
					 | 
				
			||||||
			} else if (strcasecmp(value, "false") == 0) {
 | 
					 | 
				
			||||||
				dc->tap_enabled = false;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR,
 | 
					 | 
				
			||||||
					"got unknown tap_enabled value: %s",
 | 
					 | 
				
			||||||
					value);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			wlr_log(WLR_ERROR, "got unknown device config: %s", name);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (strcmp(section, "keyboard") == 0) {
 | 
					 | 
				
			||||||
		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 if (strncmp(switch_prefix, section, strlen(switch_prefix)) == 0) {
 | 
					 | 
				
			||||||
		const char *switch_name = section + strlen(switch_prefix);
 | 
					 | 
				
			||||||
		add_switch_config(&config->switches, switch_name, name, value);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "got unknown config section: %s", section);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_config *roots_config_create_from_args(int argc, char *argv[]) {
 | 
					 | 
				
			||||||
	struct roots_config *config = calloc(1, sizeof(struct roots_config));
 | 
					 | 
				
			||||||
	if (config == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	config->xwayland = true;
 | 
					 | 
				
			||||||
	config->xwayland_lazy = true;
 | 
					 | 
				
			||||||
	wl_list_init(&config->outputs);
 | 
					 | 
				
			||||||
	wl_list_init(&config->devices);
 | 
					 | 
				
			||||||
	wl_list_init(&config->keyboards);
 | 
					 | 
				
			||||||
	wl_list_init(&config->cursors);
 | 
					 | 
				
			||||||
	wl_list_init(&config->bindings);
 | 
					 | 
				
			||||||
	wl_list_init(&config->switches);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int c;
 | 
					 | 
				
			||||||
	unsigned int log_verbosity = WLR_DEBUG;
 | 
					 | 
				
			||||||
	while ((c = getopt(argc, argv, "C:E:hDl:")) != -1) {
 | 
					 | 
				
			||||||
		switch (c) {
 | 
					 | 
				
			||||||
		case 'C':
 | 
					 | 
				
			||||||
			config->config_path = strdup(optarg);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 'E':
 | 
					 | 
				
			||||||
			config->startup_cmd = strdup(optarg);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 'D':
 | 
					 | 
				
			||||||
			config->debug_damage_tracking = true;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 'l':
 | 
					 | 
				
			||||||
			log_verbosity = strtoul(optarg, NULL, 10);
 | 
					 | 
				
			||||||
			if (log_verbosity >= WLR_LOG_IMPORTANCE_LAST) {
 | 
					 | 
				
			||||||
				log_verbosity = WLR_LOG_IMPORTANCE_LAST - 1;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 'h':
 | 
					 | 
				
			||||||
		case '?':
 | 
					 | 
				
			||||||
			usage(argv[0], c != 'h');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_log_init(log_verbosity, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!config->config_path) {
 | 
					 | 
				
			||||||
		// get the config path from the current directory
 | 
					 | 
				
			||||||
		char cwd[MAXPATHLEN];
 | 
					 | 
				
			||||||
		if (getcwd(cwd, sizeof(cwd)) != NULL) {
 | 
					 | 
				
			||||||
			char buf[MAXPATHLEN];
 | 
					 | 
				
			||||||
			if (snprintf(buf, MAXPATHLEN, "%s/%s", cwd, "rootston.ini") >= MAXPATHLEN) {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "config path too long");
 | 
					 | 
				
			||||||
				exit(1);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			config->config_path = strdup(buf);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			wlr_log(WLR_ERROR, "could not get cwd");
 | 
					 | 
				
			||||||
			exit(1);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int result = ini_parse(config->config_path, config_ini_handler, config);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (result == -1) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "No config file found. Using sensible defaults.");
 | 
					 | 
				
			||||||
		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");
 | 
					 | 
				
			||||||
		add_binding_config(&config->bindings, "Logo+Escape", "break_pointer_constraint");
 | 
					 | 
				
			||||||
		struct roots_keyboard_config *kc =
 | 
					 | 
				
			||||||
			calloc(1, sizeof(struct roots_keyboard_config));
 | 
					 | 
				
			||||||
		kc->meta_key = WLR_MODIFIER_LOGO;
 | 
					 | 
				
			||||||
		kc->name = strdup("");
 | 
					 | 
				
			||||||
		wl_list_insert(&config->keyboards, &kc->link);
 | 
					 | 
				
			||||||
	} else if (result == -2) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Could not allocate memory to parse config file");
 | 
					 | 
				
			||||||
		exit(1);
 | 
					 | 
				
			||||||
	} else if (result != 0) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Could not parse config file");
 | 
					 | 
				
			||||||
		exit(1);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return config;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_config_destroy(struct roots_config *config) {
 | 
					 | 
				
			||||||
	struct roots_output_config *oc, *otmp = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each_safe(oc, otmp, &config->outputs, link) {
 | 
					 | 
				
			||||||
		struct roots_output_mode_config *omc, *omctmp = NULL;
 | 
					 | 
				
			||||||
		wl_list_for_each_safe(omc, omctmp, &oc->modes, link) {
 | 
					 | 
				
			||||||
			free(omc);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		free(oc->name);
 | 
					 | 
				
			||||||
		free(oc);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_device_config *dc, *dtmp = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each_safe(dc, dtmp, &config->devices, link) {
 | 
					 | 
				
			||||||
		free(dc->name);
 | 
					 | 
				
			||||||
		free(dc->seat);
 | 
					 | 
				
			||||||
		free(dc->mapped_output);
 | 
					 | 
				
			||||||
		free(dc->mapped_box);
 | 
					 | 
				
			||||||
		free(dc);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_keyboard_config *kc, *ktmp = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each_safe(kc, ktmp, &config->keyboards, link) {
 | 
					 | 
				
			||||||
		free(kc->name);
 | 
					 | 
				
			||||||
		free(kc->rules);
 | 
					 | 
				
			||||||
		free(kc->model);
 | 
					 | 
				
			||||||
		free(kc->layout);
 | 
					 | 
				
			||||||
		free(kc->variant);
 | 
					 | 
				
			||||||
		free(kc->options);
 | 
					 | 
				
			||||||
		free(kc);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_cursor_config *cc, *ctmp = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each_safe(cc, ctmp, &config->cursors, link) {
 | 
					 | 
				
			||||||
		free(cc->seat);
 | 
					 | 
				
			||||||
		free(cc->mapped_output);
 | 
					 | 
				
			||||||
		free(cc->mapped_box);
 | 
					 | 
				
			||||||
		free(cc->theme);
 | 
					 | 
				
			||||||
		free(cc->default_image);
 | 
					 | 
				
			||||||
		free(cc);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_binding_config *bc, *btmp = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each_safe(bc, btmp, &config->bindings, link) {
 | 
					 | 
				
			||||||
		free(bc->keysyms);
 | 
					 | 
				
			||||||
		free(bc->command);
 | 
					 | 
				
			||||||
		free(bc);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	free(config->config_path);
 | 
					 | 
				
			||||||
	free(config);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_output_config *roots_config_get_output(struct roots_config *config,
 | 
					 | 
				
			||||||
		struct wlr_output *output) {
 | 
					 | 
				
			||||||
	char name[88];
 | 
					 | 
				
			||||||
	snprintf(name, sizeof(name), "%s %s %s", output->make, output->model,
 | 
					 | 
				
			||||||
		output->serial);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_output_config *oc;
 | 
					 | 
				
			||||||
	wl_list_for_each(oc, &config->outputs, link) {
 | 
					 | 
				
			||||||
		if (strcmp(oc->name, output->name) == 0 ||
 | 
					 | 
				
			||||||
				strcmp(oc->name, name) == 0) {
 | 
					 | 
				
			||||||
			return oc;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_device_config *roots_config_get_device(struct roots_config *config,
 | 
					 | 
				
			||||||
		struct wlr_input_device *device) {
 | 
					 | 
				
			||||||
	struct roots_device_config *d_config;
 | 
					 | 
				
			||||||
	wl_list_for_each(d_config, &config->devices, link) {
 | 
					 | 
				
			||||||
		if (strcmp(d_config->name, device->name) == 0) {
 | 
					 | 
				
			||||||
			return d_config;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_keyboard_config *roots_config_get_keyboard(
 | 
					 | 
				
			||||||
		struct roots_config *config, struct wlr_input_device *device) {
 | 
					 | 
				
			||||||
	const char *device_name = "";
 | 
					 | 
				
			||||||
	if (device != NULL) {
 | 
					 | 
				
			||||||
		device_name = device->name;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_keyboard_config *kc;
 | 
					 | 
				
			||||||
	wl_list_for_each(kc, &config->keyboards, link) {
 | 
					 | 
				
			||||||
		if (strcmp(kc->name, device_name) == 0) {
 | 
					 | 
				
			||||||
			return kc;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_cursor_config *roots_config_get_cursor(struct roots_config *config,
 | 
					 | 
				
			||||||
		const char *seat_name) {
 | 
					 | 
				
			||||||
	if (seat_name == NULL) {
 | 
					 | 
				
			||||||
		seat_name = ROOTS_CONFIG_DEFAULT_SEAT_NAME;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_cursor_config *cc;
 | 
					 | 
				
			||||||
	wl_list_for_each(cc, &config->cursors, link) {
 | 
					 | 
				
			||||||
		if (strcmp(cc->seat, seat_name) == 0) {
 | 
					 | 
				
			||||||
			return cc;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,632 +0,0 @@
 | 
				
			||||||
#define _XOPEN_SOURCE 700
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <math.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_region.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xcursor_manager.h>
 | 
					 | 
				
			||||||
#include <wlr/util/edges.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include <wlr/util/region.h>
 | 
					 | 
				
			||||||
#include <linux/input-event-codes.h>
 | 
					 | 
				
			||||||
#include "rootston/cursor.h"
 | 
					 | 
				
			||||||
#include "rootston/desktop.h"
 | 
					 | 
				
			||||||
#include "rootston/view.h"
 | 
					 | 
				
			||||||
#include "rootston/xcursor.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_cursor *roots_cursor_create(struct roots_seat *seat) {
 | 
					 | 
				
			||||||
	struct roots_cursor *cursor = calloc(1, sizeof(struct roots_cursor));
 | 
					 | 
				
			||||||
	if (!cursor) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	cursor->cursor = wlr_cursor_create();
 | 
					 | 
				
			||||||
	if (!cursor->cursor) {
 | 
					 | 
				
			||||||
		free(cursor);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	cursor->default_xcursor = ROOTS_XCURSOR_DEFAULT;
 | 
					 | 
				
			||||||
	return cursor;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_destroy(struct roots_cursor *cursor) {
 | 
					 | 
				
			||||||
	// TODO
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void seat_view_deco_motion(struct roots_seat_view *view,
 | 
					 | 
				
			||||||
		double deco_sx, double deco_sy) {
 | 
					 | 
				
			||||||
	struct roots_cursor *cursor = view->seat->cursor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double sx = deco_sx;
 | 
					 | 
				
			||||||
	double sy = deco_sy;
 | 
					 | 
				
			||||||
	if (view->has_button_grab) {
 | 
					 | 
				
			||||||
		sx = view->grab_sx;
 | 
					 | 
				
			||||||
		sy = view->grab_sy;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum roots_deco_part parts = view_get_deco_part(view->view, sx, sy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool is_titlebar = (parts & ROOTS_DECO_PART_TITLEBAR);
 | 
					 | 
				
			||||||
	uint32_t edges = 0;
 | 
					 | 
				
			||||||
	if (parts & ROOTS_DECO_PART_LEFT_BORDER) {
 | 
					 | 
				
			||||||
		edges |= WLR_EDGE_LEFT;
 | 
					 | 
				
			||||||
	} else if (parts & ROOTS_DECO_PART_RIGHT_BORDER) {
 | 
					 | 
				
			||||||
		edges |= WLR_EDGE_RIGHT;
 | 
					 | 
				
			||||||
	} else if (parts & ROOTS_DECO_PART_BOTTOM_BORDER) {
 | 
					 | 
				
			||||||
		edges |= WLR_EDGE_BOTTOM;
 | 
					 | 
				
			||||||
	} else if (parts & ROOTS_DECO_PART_TOP_BORDER) {
 | 
					 | 
				
			||||||
		edges |= WLR_EDGE_TOP;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->has_button_grab) {
 | 
					 | 
				
			||||||
		if (is_titlebar) {
 | 
					 | 
				
			||||||
			roots_seat_begin_move(view->seat, view->view);
 | 
					 | 
				
			||||||
		} else if (edges) {
 | 
					 | 
				
			||||||
			roots_seat_begin_resize(view->seat, view->view, edges);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		view->has_button_grab = false;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if (is_titlebar) {
 | 
					 | 
				
			||||||
			wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
 | 
					 | 
				
			||||||
				cursor->default_xcursor, cursor->cursor);
 | 
					 | 
				
			||||||
		} else if (edges) {
 | 
					 | 
				
			||||||
			const char *resize_name = wlr_xcursor_get_resize_name(edges);
 | 
					 | 
				
			||||||
			wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
 | 
					 | 
				
			||||||
				resize_name, cursor->cursor);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void seat_view_deco_leave(struct roots_seat_view *view) {
 | 
					 | 
				
			||||||
	struct roots_cursor *cursor = view->seat->cursor;
 | 
					 | 
				
			||||||
	wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
 | 
					 | 
				
			||||||
		cursor->default_xcursor, cursor->cursor);
 | 
					 | 
				
			||||||
	view->has_button_grab = false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void seat_view_deco_button(struct roots_seat_view *view, double sx,
 | 
					 | 
				
			||||||
		double sy, uint32_t button, uint32_t state) {
 | 
					 | 
				
			||||||
	if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED) {
 | 
					 | 
				
			||||||
		view->has_button_grab = true;
 | 
					 | 
				
			||||||
		view->grab_sx = sx;
 | 
					 | 
				
			||||||
		view->grab_sy = sy;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		view->has_button_grab = false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum roots_deco_part parts = view_get_deco_part(view->view, sx, sy);
 | 
					 | 
				
			||||||
	if (state == WLR_BUTTON_RELEASED && (parts & ROOTS_DECO_PART_TITLEBAR)) {
 | 
					 | 
				
			||||||
		struct roots_cursor *cursor = view->seat->cursor;
 | 
					 | 
				
			||||||
		wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
 | 
					 | 
				
			||||||
				cursor->default_xcursor, cursor->cursor);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void roots_passthrough_cursor(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		uint32_t time) {
 | 
					 | 
				
			||||||
	double sx, sy;
 | 
					 | 
				
			||||||
	struct roots_view *view = NULL;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = cursor->seat;
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = seat->input->server->desktop;
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = desktop_surface_at(desktop,
 | 
					 | 
				
			||||||
			cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wl_client *client = NULL;
 | 
					 | 
				
			||||||
	if (surface) {
 | 
					 | 
				
			||||||
		client = wl_resource_get_client(surface->resource);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface && !roots_seat_allow_input(seat, surface->resource)) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (cursor->cursor_client != client) {
 | 
					 | 
				
			||||||
		wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
 | 
					 | 
				
			||||||
			cursor->default_xcursor, cursor->cursor);
 | 
					 | 
				
			||||||
		cursor->cursor_client = client;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view) {
 | 
					 | 
				
			||||||
		struct roots_seat_view *seat_view =
 | 
					 | 
				
			||||||
			roots_seat_view_from_view(seat, view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (cursor->pointer_view &&
 | 
					 | 
				
			||||||
				!cursor->wlr_surface && (surface || seat_view != cursor->pointer_view)) {
 | 
					 | 
				
			||||||
			seat_view_deco_leave(cursor->pointer_view);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		cursor->pointer_view = seat_view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!surface) {
 | 
					 | 
				
			||||||
			seat_view_deco_motion(seat_view, sx, sy);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cursor->pointer_view = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cursor->wlr_surface = surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface) {
 | 
					 | 
				
			||||||
		wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy);
 | 
					 | 
				
			||||||
		wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_seat_pointer_clear_focus(seat->seat);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (seat->drag_icon != NULL) {
 | 
					 | 
				
			||||||
		roots_drag_icon_update_position(seat->drag_icon);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline int64_t timespec_to_msec(const struct timespec *a) {
 | 
					 | 
				
			||||||
	return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_update_focus(struct roots_cursor *cursor) {
 | 
					 | 
				
			||||||
	struct timespec now;
 | 
					 | 
				
			||||||
	clock_gettime(CLOCK_MONOTONIC, &now);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_passthrough_cursor(cursor, timespec_to_msec(&now));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_update_position(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		uint32_t time) {
 | 
					 | 
				
			||||||
	struct roots_seat *seat = cursor->seat;
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
	switch (cursor->mode) {
 | 
					 | 
				
			||||||
	case ROOTS_CURSOR_PASSTHROUGH:
 | 
					 | 
				
			||||||
		roots_passthrough_cursor(cursor, time);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case ROOTS_CURSOR_MOVE:
 | 
					 | 
				
			||||||
		view = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (view != NULL) {
 | 
					 | 
				
			||||||
			double dx = cursor->cursor->x - cursor->offs_x;
 | 
					 | 
				
			||||||
			double dy = cursor->cursor->y - cursor->offs_y;
 | 
					 | 
				
			||||||
			view_move(view, cursor->view_x + dx,
 | 
					 | 
				
			||||||
				cursor->view_y + dy);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case ROOTS_CURSOR_RESIZE:
 | 
					 | 
				
			||||||
		view = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (view != NULL) {
 | 
					 | 
				
			||||||
			double dx = cursor->cursor->x - cursor->offs_x;
 | 
					 | 
				
			||||||
			double dy = cursor->cursor->y - cursor->offs_y;
 | 
					 | 
				
			||||||
			double x = view->box.x;
 | 
					 | 
				
			||||||
			double y = view->box.y;
 | 
					 | 
				
			||||||
			int width = cursor->view_width;
 | 
					 | 
				
			||||||
			int height = cursor->view_height;
 | 
					 | 
				
			||||||
			if (cursor->resize_edges & WLR_EDGE_TOP) {
 | 
					 | 
				
			||||||
				y = cursor->view_y + dy;
 | 
					 | 
				
			||||||
				height -= dy;
 | 
					 | 
				
			||||||
				if (height < 1) {
 | 
					 | 
				
			||||||
					y += height;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else if (cursor->resize_edges & WLR_EDGE_BOTTOM) {
 | 
					 | 
				
			||||||
				height += dy;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (cursor->resize_edges & WLR_EDGE_LEFT) {
 | 
					 | 
				
			||||||
				x = cursor->view_x + dx;
 | 
					 | 
				
			||||||
				width -= dx;
 | 
					 | 
				
			||||||
				if (width < 1) {
 | 
					 | 
				
			||||||
					x += width;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else if (cursor->resize_edges & WLR_EDGE_RIGHT) {
 | 
					 | 
				
			||||||
				width += dx;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			view_move_resize(view, x, y,
 | 
					 | 
				
			||||||
					width < 1 ? 1 : width,
 | 
					 | 
				
			||||||
					height < 1 ? 1 : height);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case ROOTS_CURSOR_ROTATE:
 | 
					 | 
				
			||||||
		view = roots_seat_get_focus(seat);
 | 
					 | 
				
			||||||
		if (view != NULL) {
 | 
					 | 
				
			||||||
			int ox = view->box.x + view->wlr_surface->current.width/2,
 | 
					 | 
				
			||||||
				oy = view->box.y + view->wlr_surface->current.height/2;
 | 
					 | 
				
			||||||
			int ux = cursor->offs_x - ox,
 | 
					 | 
				
			||||||
				uy = cursor->offs_y - oy;
 | 
					 | 
				
			||||||
			int vx = cursor->cursor->x - ox,
 | 
					 | 
				
			||||||
				vy = cursor->cursor->y - oy;
 | 
					 | 
				
			||||||
			float angle = atan2(ux*vy - uy*vx, vx*ux + vy*uy);
 | 
					 | 
				
			||||||
			int steps = 12;
 | 
					 | 
				
			||||||
			angle = round(angle/M_PI*steps) / (steps/M_PI);
 | 
					 | 
				
			||||||
			view_rotate(view, cursor->view_rotation + angle);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void roots_cursor_press_button(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_input_device *device, uint32_t time, uint32_t button,
 | 
					 | 
				
			||||||
		uint32_t state, double lx, double ly) {
 | 
					 | 
				
			||||||
	struct roots_seat *seat = cursor->seat;
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = seat->input->server->desktop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool is_touch = device->type == WLR_INPUT_DEVICE_TOUCH;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double sx, sy;
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = desktop_surface_at(desktop,
 | 
					 | 
				
			||||||
			lx, ly, &sx, &sy, &view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (state == WLR_BUTTON_PRESSED && view &&
 | 
					 | 
				
			||||||
			roots_seat_has_meta_pressed(seat)) {
 | 
					 | 
				
			||||||
		roots_seat_set_focus(seat, view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		uint32_t edges;
 | 
					 | 
				
			||||||
		switch (button) {
 | 
					 | 
				
			||||||
		case BTN_LEFT:
 | 
					 | 
				
			||||||
			roots_seat_begin_move(seat, view);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case BTN_RIGHT:
 | 
					 | 
				
			||||||
			edges = 0;
 | 
					 | 
				
			||||||
			if (sx < view->wlr_surface->current.width/2) {
 | 
					 | 
				
			||||||
				edges |= WLR_EDGE_LEFT;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				edges |= WLR_EDGE_RIGHT;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (sy < view->wlr_surface->current.height/2) {
 | 
					 | 
				
			||||||
				edges |= WLR_EDGE_TOP;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				edges |= WLR_EDGE_BOTTOM;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			roots_seat_begin_resize(seat, view, edges);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case BTN_MIDDLE:
 | 
					 | 
				
			||||||
			roots_seat_begin_rotate(seat, view);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if (view && !surface && cursor->pointer_view) {
 | 
					 | 
				
			||||||
			seat_view_deco_button(cursor->pointer_view,
 | 
					 | 
				
			||||||
				sx, sy, button, state);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (state == WLR_BUTTON_RELEASED &&
 | 
					 | 
				
			||||||
				cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
			cursor->mode = ROOTS_CURSOR_PASSTHROUGH;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (state == WLR_BUTTON_PRESSED) {
 | 
					 | 
				
			||||||
			if (view) {
 | 
					 | 
				
			||||||
				roots_seat_set_focus(seat, view);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (surface && wlr_surface_is_layer_surface(surface)) {
 | 
					 | 
				
			||||||
				struct wlr_layer_surface_v1 *layer =
 | 
					 | 
				
			||||||
					wlr_layer_surface_v1_from_wlr_surface(surface);
 | 
					 | 
				
			||||||
				if (layer->current.keyboard_interactive) {
 | 
					 | 
				
			||||||
					roots_seat_set_focus_layer(seat, layer);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!is_touch) {
 | 
					 | 
				
			||||||
		wlr_seat_pointer_notify_button(seat->seat, time, button, state);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_motion(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_pointer_motion *event) {
 | 
					 | 
				
			||||||
	double dx = event->delta_x;
 | 
					 | 
				
			||||||
	double dy = event->delta_y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double dx_unaccel = event->unaccel_dx;
 | 
					 | 
				
			||||||
	double dy_unaccel = event->unaccel_dy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_relative_pointer_manager_v1_send_relative_motion(
 | 
					 | 
				
			||||||
		cursor->seat->input->server->desktop->relative_pointer_manager,
 | 
					 | 
				
			||||||
		cursor->seat->seat, (uint64_t)event->time_msec * 1000, dx, dy,
 | 
					 | 
				
			||||||
		dx_unaccel, dy_unaccel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (cursor->active_constraint) {
 | 
					 | 
				
			||||||
		struct roots_view *view = cursor->pointer_view->view;
 | 
					 | 
				
			||||||
		assert(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// TODO: handle rotated views
 | 
					 | 
				
			||||||
		if (view->rotation == 0.0) {
 | 
					 | 
				
			||||||
			double lx1 = cursor->cursor->x;
 | 
					 | 
				
			||||||
			double ly1 = cursor->cursor->y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			double lx2 = lx1 + dx;
 | 
					 | 
				
			||||||
			double ly2 = ly1 + dy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			double sx1 = lx1 - view->box.x;
 | 
					 | 
				
			||||||
			double sy1 = ly1 - view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			double sx2 = lx2 - view->box.x;
 | 
					 | 
				
			||||||
			double sy2 = ly2 - view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			double sx2_confined, sy2_confined;
 | 
					 | 
				
			||||||
			if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2,
 | 
					 | 
				
			||||||
					&sx2_confined, &sy2_confined)) {
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			dx = sx2_confined - sx1;
 | 
					 | 
				
			||||||
			dy = sy2_confined - sy1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_cursor_move(cursor->cursor, event->device, dx, dy);
 | 
					 | 
				
			||||||
	roots_cursor_update_position(cursor, event->time_msec);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_pointer_motion_absolute *event) {
 | 
					 | 
				
			||||||
	double lx, ly;
 | 
					 | 
				
			||||||
	wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x,
 | 
					 | 
				
			||||||
		event->y, &lx, &ly);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double dx = lx - cursor->cursor->x;
 | 
					 | 
				
			||||||
	double dy = ly - cursor->cursor->y;
 | 
					 | 
				
			||||||
	wlr_relative_pointer_manager_v1_send_relative_motion(
 | 
					 | 
				
			||||||
		cursor->seat->input->server->desktop->relative_pointer_manager,
 | 
					 | 
				
			||||||
		cursor->seat->seat, (uint64_t)event->time_msec * 1000, dx, dy, dx, dy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (cursor->pointer_view) {
 | 
					 | 
				
			||||||
		struct roots_view *view = cursor->pointer_view->view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (cursor->active_constraint &&
 | 
					 | 
				
			||||||
				!pixman_region32_contains_point(&cursor->confine,
 | 
					 | 
				
			||||||
					floor(lx - view->box.x), floor(ly - view->box.y), NULL)) {
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);
 | 
					 | 
				
			||||||
	roots_cursor_update_position(cursor, event->time_msec);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_button(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_pointer_button *event) {
 | 
					 | 
				
			||||||
	roots_cursor_press_button(cursor, event->device, event->time_msec,
 | 
					 | 
				
			||||||
		event->button, event->state, cursor->cursor->x, cursor->cursor->y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_axis(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_pointer_axis *event) {
 | 
					 | 
				
			||||||
	wlr_seat_pointer_notify_axis(cursor->seat->seat, event->time_msec,
 | 
					 | 
				
			||||||
		event->orientation, event->delta, event->delta_discrete, event->source);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_frame(struct roots_cursor *cursor) {
 | 
					 | 
				
			||||||
	wlr_seat_pointer_notify_frame(cursor->seat->seat);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_touch_down(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_touch_down *event) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = cursor->seat->input->server->desktop;
 | 
					 | 
				
			||||||
	double lx, ly;
 | 
					 | 
				
			||||||
	wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
 | 
					 | 
				
			||||||
		event->x, event->y, &lx, &ly);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double sx, sy;
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = desktop_surface_at(
 | 
					 | 
				
			||||||
		desktop, lx, ly, &sx, &sy, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t serial = 0;
 | 
					 | 
				
			||||||
	if (surface && roots_seat_allow_input(cursor->seat, surface->resource)) {
 | 
					 | 
				
			||||||
		serial = wlr_seat_touch_notify_down(cursor->seat->seat, surface,
 | 
					 | 
				
			||||||
			event->time_msec, event->touch_id, sx, sy);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (serial && wlr_seat_touch_num_points(cursor->seat->seat) == 1) {
 | 
					 | 
				
			||||||
		cursor->seat->touch_id = event->touch_id;
 | 
					 | 
				
			||||||
		cursor->seat->touch_x = lx;
 | 
					 | 
				
			||||||
		cursor->seat->touch_y = ly;
 | 
					 | 
				
			||||||
		roots_cursor_press_button(cursor, event->device, event->time_msec,
 | 
					 | 
				
			||||||
			BTN_LEFT, 1, lx, ly);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_touch_up(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_touch_up *event) {
 | 
					 | 
				
			||||||
	struct wlr_touch_point *point =
 | 
					 | 
				
			||||||
		wlr_seat_touch_get_point(cursor->seat->seat, event->touch_id);
 | 
					 | 
				
			||||||
	if (!point) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (wlr_seat_touch_num_points(cursor->seat->seat) == 1) {
 | 
					 | 
				
			||||||
		roots_cursor_press_button(cursor, event->device, event->time_msec,
 | 
					 | 
				
			||||||
			BTN_LEFT, 0, cursor->seat->touch_x, cursor->seat->touch_y);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_seat_touch_notify_up(cursor->seat->seat, event->time_msec,
 | 
					 | 
				
			||||||
		event->touch_id);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_touch_motion *event) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = cursor->seat->input->server->desktop;
 | 
					 | 
				
			||||||
	struct wlr_touch_point *point =
 | 
					 | 
				
			||||||
		wlr_seat_touch_get_point(cursor->seat->seat, event->touch_id);
 | 
					 | 
				
			||||||
	if (!point) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double lx, ly;
 | 
					 | 
				
			||||||
	wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
 | 
					 | 
				
			||||||
		event->x, event->y, &lx, &ly);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double sx, sy;
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = desktop_surface_at(
 | 
					 | 
				
			||||||
			desktop, lx, ly, &sx, &sy, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface && roots_seat_allow_input(cursor->seat, surface->resource)) {
 | 
					 | 
				
			||||||
		wlr_seat_touch_point_focus(cursor->seat->seat, surface,
 | 
					 | 
				
			||||||
			event->time_msec, event->touch_id, sx, sy);
 | 
					 | 
				
			||||||
		wlr_seat_touch_notify_motion(cursor->seat->seat, event->time_msec,
 | 
					 | 
				
			||||||
			event->touch_id, sx, sy);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_seat_touch_point_clear_focus(cursor->seat->seat, event->time_msec,
 | 
					 | 
				
			||||||
			event->touch_id);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (event->touch_id == cursor->seat->touch_id) {
 | 
					 | 
				
			||||||
		cursor->seat->touch_x = lx;
 | 
					 | 
				
			||||||
		cursor->seat->touch_y = ly;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_tablet_tool_axis *event) {
 | 
					 | 
				
			||||||
	double x = NAN, y = NAN;
 | 
					 | 
				
			||||||
	if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
 | 
					 | 
				
			||||||
			(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
 | 
					 | 
				
			||||||
		x = event->x;
 | 
					 | 
				
			||||||
		y = event->y;
 | 
					 | 
				
			||||||
	} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
 | 
					 | 
				
			||||||
		x = event->x;
 | 
					 | 
				
			||||||
	} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
 | 
					 | 
				
			||||||
		y = event->y;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double lx, ly;
 | 
					 | 
				
			||||||
	wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
 | 
					 | 
				
			||||||
		x, y, &lx, &ly);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (cursor->pointer_view) {
 | 
					 | 
				
			||||||
		struct roots_view *view = cursor->pointer_view->view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (cursor->active_constraint &&
 | 
					 | 
				
			||||||
				!pixman_region32_contains_point(&cursor->confine,
 | 
					 | 
				
			||||||
					floor(lx - view->box.x), floor(ly - view->box.y), NULL)) {
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);
 | 
					 | 
				
			||||||
	roots_cursor_update_position(cursor, event->time_msec);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_tool_tip(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_event_tablet_tool_tip *event) {
 | 
					 | 
				
			||||||
	roots_cursor_press_button(cursor, event->device,
 | 
					 | 
				
			||||||
		event->time_msec, BTN_LEFT, event->state, cursor->cursor->x,
 | 
					 | 
				
			||||||
		cursor->cursor->y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_seat_pointer_request_set_cursor_event *event) {
 | 
					 | 
				
			||||||
	struct wlr_surface *focused_surface =
 | 
					 | 
				
			||||||
		event->seat_client->seat->pointer_state.focused_surface;
 | 
					 | 
				
			||||||
	bool has_focused =
 | 
					 | 
				
			||||||
		focused_surface != NULL && focused_surface->resource != NULL;
 | 
					 | 
				
			||||||
	struct wl_client *focused_client = NULL;
 | 
					 | 
				
			||||||
	if (has_focused) {
 | 
					 | 
				
			||||||
		focused_client = wl_resource_get_client(focused_surface->resource);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (event->seat_client->client != focused_client ||
 | 
					 | 
				
			||||||
			cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Denying request to set cursor from unfocused client");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
 | 
					 | 
				
			||||||
		event->hotspot_y);
 | 
					 | 
				
			||||||
	cursor->cursor_client = event->seat_client->client;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_focus_change(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_seat_pointer_focus_change_event *event) {
 | 
					 | 
				
			||||||
	double sx = event->sx;
 | 
					 | 
				
			||||||
	double sy = event->sy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double lx = cursor->cursor->x;
 | 
					 | 
				
			||||||
	double ly = cursor->cursor->y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "entered surface %p, lx: %f, ly: %f, sx: %f, sy: %f",
 | 
					 | 
				
			||||||
		event->new_surface, lx, ly, sx, sy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_cursor_constrain(cursor,
 | 
					 | 
				
			||||||
		wlr_pointer_constraints_v1_constraint_for_surface(
 | 
					 | 
				
			||||||
			cursor->seat->input->server->desktop->pointer_constraints,
 | 
					 | 
				
			||||||
			event->new_surface, cursor->seat->seat),
 | 
					 | 
				
			||||||
		sx, sy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = cursor->seat->input->server->desktop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
	double sx, sy;
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = desktop_surface_at(desktop,
 | 
					 | 
				
			||||||
			cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view);
 | 
					 | 
				
			||||||
	// This should never happen but views move around right when they're
 | 
					 | 
				
			||||||
	// created from (0, 0) to their actual coordinates.
 | 
					 | 
				
			||||||
	if (surface != cursor->active_constraint->surface) {
 | 
					 | 
				
			||||||
		roots_cursor_update_focus(cursor);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		roots_cursor_constrain(cursor, cursor->active_constraint, sx, sy);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_constraint_commit(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_cursor *cursor =
 | 
					 | 
				
			||||||
		wl_container_of(listener, cursor, constraint_commit);
 | 
					 | 
				
			||||||
	assert(cursor->active_constraint->surface == data);
 | 
					 | 
				
			||||||
	roots_cursor_handle_constraint_commit(cursor);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_cursor_constrain(struct roots_cursor *cursor,
 | 
					 | 
				
			||||||
		struct wlr_pointer_constraint_v1 *constraint, double sx, double sy) {
 | 
					 | 
				
			||||||
	if (cursor->active_constraint == constraint) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)",
 | 
					 | 
				
			||||||
		cursor, constraint);
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "cursor->active_constraint: %p",
 | 
					 | 
				
			||||||
		cursor->active_constraint);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_remove(&cursor->constraint_commit.link);
 | 
					 | 
				
			||||||
	wl_list_init(&cursor->constraint_commit.link);
 | 
					 | 
				
			||||||
	if (cursor->active_constraint) {
 | 
					 | 
				
			||||||
		wlr_pointer_constraint_v1_send_deactivated(
 | 
					 | 
				
			||||||
			cursor->active_constraint);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cursor->active_constraint = constraint;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (constraint == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_pointer_constraint_v1_send_activated(constraint);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_remove(&cursor->constraint_commit.link);
 | 
					 | 
				
			||||||
	wl_signal_add(&constraint->surface->events.commit,
 | 
					 | 
				
			||||||
		&cursor->constraint_commit);
 | 
					 | 
				
			||||||
	cursor->constraint_commit.notify = handle_constraint_commit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pixman_region32_clear(&cursor->confine);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pixman_region32_t *region = &constraint->region;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!pixman_region32_contains_point(region, floor(sx), floor(sy), NULL)) {
 | 
					 | 
				
			||||||
		// Warp into region if possible
 | 
					 | 
				
			||||||
		int nboxes;
 | 
					 | 
				
			||||||
		pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes);
 | 
					 | 
				
			||||||
		if (nboxes > 0) {
 | 
					 | 
				
			||||||
			struct roots_view *view = cursor->pointer_view->view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			double sx = (boxes[0].x1 + boxes[0].x2) / 2.;
 | 
					 | 
				
			||||||
			double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			rotate_child_position(&sx, &sy, 0, 0, view->box.width, view->box.height,
 | 
					 | 
				
			||||||
				view->rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			double lx = view->box.x + sx;
 | 
					 | 
				
			||||||
			double ly = view->box.y + sy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			wlr_cursor_warp_closest(cursor->cursor, NULL, lx, ly);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// A locked pointer will result in an empty region, thus disallowing all movement
 | 
					 | 
				
			||||||
	if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) {
 | 
					 | 
				
			||||||
		pixman_region32_copy(&cursor->confine, region);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,465 +0,0 @@
 | 
				
			||||||
#define _POSIX_C_SOURCE 200112L
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <math.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_compositor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_cursor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_data_control_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_export_dmabuf_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_gamma_control_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_gtk_primary_selection.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_idle.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_input_inhibitor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_layer_shell_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output_layout.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_pointer_constraints_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_primary_selection_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_server_decoration.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_tablet_v2.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xcursor_manager.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_output_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_output_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell_v6.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/layers.h"
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
#include "rootston/view.h"
 | 
					 | 
				
			||||||
#include "rootston/virtual_keyboard.h"
 | 
					 | 
				
			||||||
#include "rootston/xcursor.h"
 | 
					 | 
				
			||||||
#include "wlr-layer-shell-unstable-v1-protocol.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool view_at(struct roots_view *view, double lx, double ly,
 | 
					 | 
				
			||||||
		struct wlr_surface **surface, double *sx, double *sy) {
 | 
					 | 
				
			||||||
	if (view->wlr_surface == NULL) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double view_sx = lx - view->box.x;
 | 
					 | 
				
			||||||
	double view_sy = ly - view->box.y;
 | 
					 | 
				
			||||||
	rotate_child_position(&view_sx, &view_sy, 0, 0,
 | 
					 | 
				
			||||||
		view->box.width, view->box.height, -view->rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double _sx, _sy;
 | 
					 | 
				
			||||||
	struct wlr_surface *_surface = NULL;
 | 
					 | 
				
			||||||
	switch (view->type) {
 | 
					 | 
				
			||||||
	case ROOTS_XDG_SHELL_V6_VIEW:;
 | 
					 | 
				
			||||||
		struct roots_xdg_surface_v6 *xdg_surface_v6 =
 | 
					 | 
				
			||||||
			roots_xdg_surface_v6_from_view(view);
 | 
					 | 
				
			||||||
		_surface = wlr_xdg_surface_v6_surface_at(xdg_surface_v6->xdg_surface_v6,
 | 
					 | 
				
			||||||
			view_sx, view_sy, &_sx, &_sy);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case ROOTS_XDG_SHELL_VIEW:;
 | 
					 | 
				
			||||||
		struct roots_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
			roots_xdg_surface_from_view(view);
 | 
					 | 
				
			||||||
		_surface = wlr_xdg_surface_surface_at(xdg_surface->xdg_surface,
 | 
					 | 
				
			||||||
			view_sx, view_sy, &_sx, &_sy);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	case ROOTS_XWAYLAND_VIEW:
 | 
					 | 
				
			||||||
		_surface = wlr_surface_surface_at(view->wlr_surface,
 | 
					 | 
				
			||||||
			view_sx, view_sy, &_sx, &_sy);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (_surface != NULL) {
 | 
					 | 
				
			||||||
		*sx = _sx;
 | 
					 | 
				
			||||||
		*sy = _sy;
 | 
					 | 
				
			||||||
		*surface = _surface;
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view_get_deco_part(view, view_sx, view_sy)) {
 | 
					 | 
				
			||||||
		*sx = view_sx;
 | 
					 | 
				
			||||||
		*sy = view_sy;
 | 
					 | 
				
			||||||
		*surface = NULL;
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_view *desktop_view_at(struct roots_desktop *desktop,
 | 
					 | 
				
			||||||
		double lx, double ly, struct wlr_surface **surface,
 | 
					 | 
				
			||||||
		double *sx, double *sy) {
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
	wl_list_for_each(view, &desktop->views, link) {
 | 
					 | 
				
			||||||
		if (view_at(view, lx, ly, surface, sx, sy)) {
 | 
					 | 
				
			||||||
			return view;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct wlr_surface *layer_surface_at(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *roots_surface;
 | 
					 | 
				
			||||||
	wl_list_for_each_reverse(roots_surface, layer, link) {
 | 
					 | 
				
			||||||
		double _sx = ox - roots_surface->geo.x;
 | 
					 | 
				
			||||||
		double _sy = oy - roots_surface->geo.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
 | 
					 | 
				
			||||||
			roots_surface->layer_surface, _sx, _sy, sx, sy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (sub) {
 | 
					 | 
				
			||||||
			return sub;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct wlr_surface *desktop_surface_at(struct roots_desktop *desktop,
 | 
					 | 
				
			||||||
		double lx, double ly, double *sx, double *sy,
 | 
					 | 
				
			||||||
		struct roots_view **view) {
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = NULL;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output =
 | 
					 | 
				
			||||||
		wlr_output_layout_output_at(desktop->layout, lx, ly);
 | 
					 | 
				
			||||||
	struct roots_output *roots_output = NULL;
 | 
					 | 
				
			||||||
	double ox = lx, oy = ly;
 | 
					 | 
				
			||||||
	if (view) {
 | 
					 | 
				
			||||||
		*view = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (wlr_output) {
 | 
					 | 
				
			||||||
		roots_output = wlr_output->data;
 | 
					 | 
				
			||||||
		wlr_output_layout_output_coords(desktop->layout, wlr_output, &ox, &oy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if ((surface = layer_surface_at(roots_output,
 | 
					 | 
				
			||||||
					&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
 | 
					 | 
				
			||||||
					ox, oy, sx, sy))) {
 | 
					 | 
				
			||||||
			return surface;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct roots_output *output =
 | 
					 | 
				
			||||||
			desktop_output_from_wlr_output(desktop, wlr_output);
 | 
					 | 
				
			||||||
		if (output != NULL && output->fullscreen_view != NULL) {
 | 
					 | 
				
			||||||
			if (view_at(output->fullscreen_view, lx, ly, &surface, sx, sy)) {
 | 
					 | 
				
			||||||
				return surface;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				return NULL;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if ((surface = layer_surface_at(roots_output,
 | 
					 | 
				
			||||||
					&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
 | 
					 | 
				
			||||||
					ox, oy, sx, sy))) {
 | 
					 | 
				
			||||||
			return surface;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *_view;
 | 
					 | 
				
			||||||
	if ((_view = desktop_view_at(desktop, lx, ly, &surface, sx, sy))) {
 | 
					 | 
				
			||||||
		if (view) {
 | 
					 | 
				
			||||||
			*view = _view;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return surface;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (wlr_output) {
 | 
					 | 
				
			||||||
		if ((surface = layer_surface_at(roots_output,
 | 
					 | 
				
			||||||
					&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
 | 
					 | 
				
			||||||
					ox, oy, sx, sy))) {
 | 
					 | 
				
			||||||
			return surface;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if ((surface = layer_surface_at(roots_output,
 | 
					 | 
				
			||||||
					&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
 | 
					 | 
				
			||||||
					ox, oy, sx, sy))) {
 | 
					 | 
				
			||||||
			return surface;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_layout_change(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, layout_change);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output *center_output =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_center_output(desktop->layout);
 | 
					 | 
				
			||||||
	if (center_output == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box *center_output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(desktop->layout, center_output);
 | 
					 | 
				
			||||||
	double center_x = center_output_box->x + center_output_box->width/2;
 | 
					 | 
				
			||||||
	double center_y = center_output_box->y + center_output_box->height/2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
	wl_list_for_each(view, &desktop->views, link) {
 | 
					 | 
				
			||||||
		struct wlr_box box;
 | 
					 | 
				
			||||||
		view_get_box(view, &box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (wlr_output_layout_intersects(desktop->layout, NULL, &box)) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		view_move(view, center_x - box.width/2, center_y - box.height/2);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void input_inhibit_activate(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = wl_container_of(
 | 
					 | 
				
			||||||
			listener, desktop, input_inhibit_activate);
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &desktop->server->input->seats, link) {
 | 
					 | 
				
			||||||
		roots_seat_set_exclusive_client(seat,
 | 
					 | 
				
			||||||
				desktop->input_inhibit->active_client);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void input_inhibit_deactivate(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = wl_container_of(
 | 
					 | 
				
			||||||
			listener, desktop, input_inhibit_deactivate);
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &desktop->server->input->seats, link) {
 | 
					 | 
				
			||||||
		roots_seat_set_exclusive_client(seat, NULL);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_constraint_destroy(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_pointer_constraint *constraint =
 | 
					 | 
				
			||||||
		wl_container_of(listener, constraint, destroy);
 | 
					 | 
				
			||||||
	struct wlr_pointer_constraint_v1 *wlr_constraint = data;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = wlr_constraint->seat->data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_remove(&constraint->destroy.link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (seat->cursor->active_constraint == wlr_constraint) {
 | 
					 | 
				
			||||||
		wl_list_remove(&seat->cursor->constraint_commit.link);
 | 
					 | 
				
			||||||
		wl_list_init(&seat->cursor->constraint_commit.link);
 | 
					 | 
				
			||||||
		seat->cursor->active_constraint = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (wlr_constraint->current.committed &
 | 
					 | 
				
			||||||
				WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT &&
 | 
					 | 
				
			||||||
				seat->cursor->pointer_view) {
 | 
					 | 
				
			||||||
			double sx = wlr_constraint->current.cursor_hint.x;
 | 
					 | 
				
			||||||
			double sy = wlr_constraint->current.cursor_hint.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			struct roots_view *view = seat->cursor->pointer_view->view;
 | 
					 | 
				
			||||||
			rotate_child_position(&sx, &sy, 0, 0, view->box.width, view->box.height,
 | 
					 | 
				
			||||||
				view->rotation);
 | 
					 | 
				
			||||||
			double lx = view->box.x + sx;
 | 
					 | 
				
			||||||
			double ly = view->box.y + sy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			wlr_cursor_warp(seat->cursor->cursor, NULL, lx, ly);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	free(constraint);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_pointer_constraint(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct wlr_pointer_constraint_v1 *wlr_constraint = data;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = wlr_constraint->seat->data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_pointer_constraint *constraint =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_pointer_constraint));
 | 
					 | 
				
			||||||
	constraint->constraint = wlr_constraint;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	constraint->destroy.notify = handle_constraint_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double sx, sy;
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = desktop_surface_at(
 | 
					 | 
				
			||||||
		seat->input->server->desktop,
 | 
					 | 
				
			||||||
		seat->cursor->cursor->x, seat->cursor->cursor->y, &sx, &sy, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface == wlr_constraint->surface) {
 | 
					 | 
				
			||||||
		assert(!seat->cursor->active_constraint);
 | 
					 | 
				
			||||||
		roots_cursor_constrain(seat->cursor, wlr_constraint, sx, sy);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_desktop *desktop_create(struct roots_server *server,
 | 
					 | 
				
			||||||
		struct roots_config *config) {
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "Initializing roots desktop");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = calloc(1, sizeof(struct roots_desktop));
 | 
					 | 
				
			||||||
	if (desktop == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_init(&desktop->views);
 | 
					 | 
				
			||||||
	wl_list_init(&desktop->outputs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->new_output.notify = handle_new_output;
 | 
					 | 
				
			||||||
	wl_signal_add(&server->backend->events.new_output, &desktop->new_output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->server = server;
 | 
					 | 
				
			||||||
	desktop->config = config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->layout = wlr_output_layout_create();
 | 
					 | 
				
			||||||
	wlr_xdg_output_manager_v1_create(server->wl_display, desktop->layout);
 | 
					 | 
				
			||||||
	desktop->layout_change.notify = handle_layout_change;
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->layout->events.change, &desktop->layout_change);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->compositor = wlr_compositor_create(server->wl_display,
 | 
					 | 
				
			||||||
		server->renderer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->xdg_shell_v6->events.new_surface,
 | 
					 | 
				
			||||||
		&desktop->xdg_shell_v6_surface);
 | 
					 | 
				
			||||||
	desktop->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->xdg_shell = wlr_xdg_shell_create(server->wl_display);
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->xdg_shell->events.new_surface,
 | 
					 | 
				
			||||||
		&desktop->xdg_shell_surface);
 | 
					 | 
				
			||||||
	desktop->xdg_shell_surface.notify = handle_xdg_shell_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->layer_shell = wlr_layer_shell_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->layer_shell->events.new_surface,
 | 
					 | 
				
			||||||
		&desktop->layer_shell_surface);
 | 
					 | 
				
			||||||
	desktop->layer_shell_surface.notify = handle_layer_shell_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const char *cursor_theme = NULL;
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	const char *cursor_default = ROOTS_XCURSOR_DEFAULT;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	struct roots_cursor_config *cc =
 | 
					 | 
				
			||||||
		roots_config_get_cursor(config, ROOTS_CONFIG_DEFAULT_SEAT_NAME);
 | 
					 | 
				
			||||||
	if (cc != NULL) {
 | 
					 | 
				
			||||||
		cursor_theme = cc->theme;
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
		if (cc->default_image != NULL) {
 | 
					 | 
				
			||||||
			cursor_default = cc->default_image;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	char cursor_size_fmt[16];
 | 
					 | 
				
			||||||
	snprintf(cursor_size_fmt, sizeof(cursor_size_fmt),
 | 
					 | 
				
			||||||
		"%d", ROOTS_XCURSOR_SIZE);
 | 
					 | 
				
			||||||
	setenv("XCURSOR_SIZE", cursor_size_fmt, 1);
 | 
					 | 
				
			||||||
	if (cursor_theme != NULL) {
 | 
					 | 
				
			||||||
		setenv("XCURSOR_THEME", cursor_theme, 1);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	desktop->xcursor_manager = wlr_xcursor_manager_create(cursor_theme,
 | 
					 | 
				
			||||||
		ROOTS_XCURSOR_SIZE);
 | 
					 | 
				
			||||||
	if (desktop->xcursor_manager == NULL) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Cannot create XCursor manager for theme %s",
 | 
					 | 
				
			||||||
			cursor_theme);
 | 
					 | 
				
			||||||
		free(desktop);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (config->xwayland) {
 | 
					 | 
				
			||||||
		desktop->xwayland = wlr_xwayland_create(server->wl_display,
 | 
					 | 
				
			||||||
			desktop->compositor, config->xwayland_lazy);
 | 
					 | 
				
			||||||
		wl_signal_add(&desktop->xwayland->events.new_surface,
 | 
					 | 
				
			||||||
			&desktop->xwayland_surface);
 | 
					 | 
				
			||||||
		desktop->xwayland_surface.notify = handle_xwayland_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		setenv("DISPLAY", desktop->xwayland->display_name, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (wlr_xcursor_manager_load(desktop->xcursor_manager, 1)) {
 | 
					 | 
				
			||||||
			wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
 | 
					 | 
				
			||||||
			desktop->xcursor_manager, cursor_default, 1);
 | 
					 | 
				
			||||||
		if (xcursor != NULL) {
 | 
					 | 
				
			||||||
			struct wlr_xcursor_image *image = xcursor->images[0];
 | 
					 | 
				
			||||||
			wlr_xwayland_set_cursor(desktop->xwayland, image->buffer,
 | 
					 | 
				
			||||||
				image->width * 4, image->width, image->height, image->hotspot_x,
 | 
					 | 
				
			||||||
				image->hotspot_y);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->gamma_control_manager_v1 = wlr_gamma_control_manager_v1_create(
 | 
					 | 
				
			||||||
		server->wl_display);
 | 
					 | 
				
			||||||
	desktop->export_dmabuf_manager_v1 =
 | 
					 | 
				
			||||||
		wlr_export_dmabuf_manager_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->server_decoration_manager =
 | 
					 | 
				
			||||||
		wlr_server_decoration_manager_create(server->wl_display);
 | 
					 | 
				
			||||||
	wlr_server_decoration_manager_set_default_mode(
 | 
					 | 
				
			||||||
		desktop->server_decoration_manager,
 | 
					 | 
				
			||||||
		WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
 | 
					 | 
				
			||||||
	desktop->idle = wlr_idle_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->idle_inhibit = wlr_idle_inhibit_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->primary_selection_device_manager =
 | 
					 | 
				
			||||||
		wlr_gtk_primary_selection_device_manager_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->input_inhibit =
 | 
					 | 
				
			||||||
		wlr_input_inhibit_manager_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->input_inhibit_activate.notify = input_inhibit_activate;
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->input_inhibit->events.activate,
 | 
					 | 
				
			||||||
		&desktop->input_inhibit_activate);
 | 
					 | 
				
			||||||
	desktop->input_inhibit_deactivate.notify = input_inhibit_deactivate;
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->input_inhibit->events.deactivate,
 | 
					 | 
				
			||||||
		&desktop->input_inhibit_deactivate);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->input_method =
 | 
					 | 
				
			||||||
		wlr_input_method_manager_v2_create(server->wl_display);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->text_input = wlr_text_input_manager_v3_create(server->wl_display);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(
 | 
					 | 
				
			||||||
		server->wl_display);
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->virtual_keyboard->events.new_virtual_keyboard,
 | 
					 | 
				
			||||||
		&desktop->virtual_keyboard_new);
 | 
					 | 
				
			||||||
	desktop->virtual_keyboard_new.notify = handle_virtual_keyboard;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->screencopy = wlr_screencopy_manager_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->xdg_decoration_manager =
 | 
					 | 
				
			||||||
		wlr_xdg_decoration_manager_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->xdg_decoration_manager->events.new_toplevel_decoration,
 | 
					 | 
				
			||||||
		&desktop->xdg_toplevel_decoration);
 | 
					 | 
				
			||||||
	desktop->xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->pointer_constraints =
 | 
					 | 
				
			||||||
		wlr_pointer_constraints_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->pointer_constraint.notify = handle_pointer_constraint;
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->pointer_constraints->events.new_constraint,
 | 
					 | 
				
			||||||
		&desktop->pointer_constraint);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->presentation =
 | 
					 | 
				
			||||||
		wlr_presentation_create(server->wl_display, server->backend);
 | 
					 | 
				
			||||||
	desktop->foreign_toplevel_manager_v1 =
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_manager_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->relative_pointer_manager =
 | 
					 | 
				
			||||||
		wlr_relative_pointer_manager_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->pointer_gestures =
 | 
					 | 
				
			||||||
		wlr_pointer_gestures_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	desktop->output_manager_v1 =
 | 
					 | 
				
			||||||
		wlr_output_manager_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
	desktop->output_manager_apply.notify = handle_output_manager_apply;
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->output_manager_v1->events.apply,
 | 
					 | 
				
			||||||
		&desktop->output_manager_apply);
 | 
					 | 
				
			||||||
	desktop->output_manager_test.notify = handle_output_manager_test;
 | 
					 | 
				
			||||||
	wl_signal_add(&desktop->output_manager_v1->events.test,
 | 
					 | 
				
			||||||
		&desktop->output_manager_test);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_primary_selection_v1_device_manager_create(server->wl_display);
 | 
					 | 
				
			||||||
	wlr_data_control_manager_v1_create(server->wl_display);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return desktop;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void desktop_destroy(struct roots_desktop *desktop) {
 | 
					 | 
				
			||||||
	// TODO
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_output *desktop_output_from_wlr_output(
 | 
					 | 
				
			||||||
		struct roots_desktop *desktop, struct wlr_output *wlr_output) {
 | 
					 | 
				
			||||||
	struct roots_output *output;
 | 
					 | 
				
			||||||
	wl_list_for_each(output, &desktop->outputs, link) {
 | 
					 | 
				
			||||||
		if (output->wlr_output == wlr_output) {
 | 
					 | 
				
			||||||
			return output;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										195
									
								
								rootston/ini.c
									
										
									
									
									
								
							
							
						
						
									
										195
									
								
								rootston/ini.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,195 +0,0 @@
 | 
				
			||||||
/* inih -- simple .INI file parser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
 | 
					 | 
				
			||||||
home page for more info:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
https://github.com/benhoyt/inih
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
 | 
					 | 
				
			||||||
#define _CRT_SECURE_NO_WARNINGS
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
#include <ctype.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "rootston/ini.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if !INI_USE_STACK
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAX_SECTION 50
 | 
					 | 
				
			||||||
#define MAX_NAME 50
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Strip whitespace chars off end of given string, in place. Return s. */
 | 
					 | 
				
			||||||
static char* rstrip(char* s)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    char* p = s + strlen(s);
 | 
					 | 
				
			||||||
    while (p > s && isspace((unsigned char)(*--p)))
 | 
					 | 
				
			||||||
        *p = '\0';
 | 
					 | 
				
			||||||
    return s;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Return pointer to first non-whitespace char in given string. */
 | 
					 | 
				
			||||||
static char* lskip(const char* s)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    while (*s && isspace((unsigned char)(*s)))
 | 
					 | 
				
			||||||
        s++;
 | 
					 | 
				
			||||||
    return (char*)s;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Return pointer to first char (of chars) or inline comment in given string,
 | 
					 | 
				
			||||||
   or pointer to null at end of string if neither found. Inline comment must
 | 
					 | 
				
			||||||
   be prefixed by a whitespace character to register as a comment. */
 | 
					 | 
				
			||||||
static char* find_chars_or_comment(const char* s, const char* chars)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
#if INI_ALLOW_INLINE_COMMENTS
 | 
					 | 
				
			||||||
    int was_space = 0;
 | 
					 | 
				
			||||||
    while (*s && (!chars || !strchr(chars, *s)) &&
 | 
					 | 
				
			||||||
           !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
 | 
					 | 
				
			||||||
        was_space = isspace((unsigned char)(*s));
 | 
					 | 
				
			||||||
        s++;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
    while (*s && (!chars || !strchr(chars, *s))) {
 | 
					 | 
				
			||||||
        s++;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    return (char*)s;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
 | 
					 | 
				
			||||||
static char* strncpy0(char* dest, const char* src, size_t size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    strncpy(dest, src, size-1);
 | 
					 | 
				
			||||||
    dest[size - 1] = '\0';
 | 
					 | 
				
			||||||
    return dest;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* See documentation in header file. */
 | 
					 | 
				
			||||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
 | 
					 | 
				
			||||||
                     void* user)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /* Uses a fair bit of stack (use heap instead if you need to) */
 | 
					 | 
				
			||||||
#if INI_USE_STACK
 | 
					 | 
				
			||||||
    char line[INI_MAX_LINE];
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
    char* line;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    char section[MAX_SECTION] = "";
 | 
					 | 
				
			||||||
    char prev_name[MAX_NAME] = "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    char* start;
 | 
					 | 
				
			||||||
    char* end;
 | 
					 | 
				
			||||||
    char* name;
 | 
					 | 
				
			||||||
    char* value;
 | 
					 | 
				
			||||||
    int lineno = 0;
 | 
					 | 
				
			||||||
    int error = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if !INI_USE_STACK
 | 
					 | 
				
			||||||
    line = (char*)malloc(INI_MAX_LINE);
 | 
					 | 
				
			||||||
    if (!line) {
 | 
					 | 
				
			||||||
        return -2;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Scan through stream line by line */
 | 
					 | 
				
			||||||
    while (reader(line, INI_MAX_LINE, stream) != NULL) {
 | 
					 | 
				
			||||||
        lineno++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        start = line;
 | 
					 | 
				
			||||||
#if INI_ALLOW_BOM
 | 
					 | 
				
			||||||
        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
 | 
					 | 
				
			||||||
                           (unsigned char)start[1] == 0xBB &&
 | 
					 | 
				
			||||||
                           (unsigned char)start[2] == 0xBF) {
 | 
					 | 
				
			||||||
            start += 3;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        start = lskip(rstrip(start));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (*start == ';' || *start == '#') {
 | 
					 | 
				
			||||||
            /* Per Python configparser, allow both ; and # comments at the
 | 
					 | 
				
			||||||
               start of a line */
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
#if INI_ALLOW_MULTILINE
 | 
					 | 
				
			||||||
        else if (*prev_name && *start && start > line) {
 | 
					 | 
				
			||||||
            /* Non-blank line with leading whitespace, treat as continuation
 | 
					 | 
				
			||||||
               of previous name's value (as per Python configparser). */
 | 
					 | 
				
			||||||
            if (!handler(user, section, prev_name, start) && !error)
 | 
					 | 
				
			||||||
                error = lineno;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        else if (*start == '[') {
 | 
					 | 
				
			||||||
            /* A "[section]" line */
 | 
					 | 
				
			||||||
            end = find_chars_or_comment(start + 1, "]");
 | 
					 | 
				
			||||||
            if (*end == ']') {
 | 
					 | 
				
			||||||
                *end = '\0';
 | 
					 | 
				
			||||||
                strncpy0(section, start + 1, sizeof(section));
 | 
					 | 
				
			||||||
                *prev_name = '\0';
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (!error) {
 | 
					 | 
				
			||||||
                /* No ']' found on section line */
 | 
					 | 
				
			||||||
                error = lineno;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (*start) {
 | 
					 | 
				
			||||||
            /* Not a comment, must be a name[=:]value pair */
 | 
					 | 
				
			||||||
            end = find_chars_or_comment(start, "=:");
 | 
					 | 
				
			||||||
            if (*end == '=' || *end == ':') {
 | 
					 | 
				
			||||||
                *end = '\0';
 | 
					 | 
				
			||||||
                name = rstrip(start);
 | 
					 | 
				
			||||||
                value = lskip(end + 1);
 | 
					 | 
				
			||||||
#if INI_ALLOW_INLINE_COMMENTS
 | 
					 | 
				
			||||||
                end = find_chars_or_comment(value, NULL);
 | 
					 | 
				
			||||||
                if (*end)
 | 
					 | 
				
			||||||
                    *end = '\0';
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
                rstrip(value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                /* Valid name[=:]value pair found, call handler */
 | 
					 | 
				
			||||||
                strncpy0(prev_name, name, sizeof(prev_name));
 | 
					 | 
				
			||||||
                if (!handler(user, section, name, value) && !error)
 | 
					 | 
				
			||||||
                    error = lineno;
 | 
					 | 
				
			||||||
				memset(value, 0, strlen(value));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (!error) {
 | 
					 | 
				
			||||||
                /* No '=' or ':' found on name[=:]value line */
 | 
					 | 
				
			||||||
                error = lineno;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if INI_STOP_ON_FIRST_ERROR
 | 
					 | 
				
			||||||
        if (error)
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if !INI_USE_STACK
 | 
					 | 
				
			||||||
    free(line);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return error;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* See documentation in header file. */
 | 
					 | 
				
			||||||
int ini_parse_file(FILE* file, ini_handler handler, void* user)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    return ini_parse_stream((ini_reader)fgets, file, handler, user);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* See documentation in header file. */
 | 
					 | 
				
			||||||
int ini_parse(const char* filename, ini_handler handler, void* user)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    FILE* file;
 | 
					 | 
				
			||||||
    int error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    file = fopen(filename, "r");
 | 
					 | 
				
			||||||
    if (!file)
 | 
					 | 
				
			||||||
        return -1;
 | 
					 | 
				
			||||||
    error = ini_parse_file(file, handler, user);
 | 
					 | 
				
			||||||
    fclose(file);
 | 
					 | 
				
			||||||
    return error;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										146
									
								
								rootston/input.c
									
										
									
									
									
								
							
							
						
						
									
										146
									
								
								rootston/input.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,146 +0,0 @@
 | 
				
			||||||
#define _POSIX_C_SOURCE 200112L
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/backend/libinput.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_cursor.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include <wlr/xcursor.h>
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
#include <wlr/xwayland.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#include "rootston/config.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
#include "rootston/keyboard.h"
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const char *device_type(enum wlr_input_device_type type) {
 | 
					 | 
				
			||||||
	switch (type) {
 | 
					 | 
				
			||||||
	case WLR_INPUT_DEVICE_KEYBOARD:
 | 
					 | 
				
			||||||
		return "keyboard";
 | 
					 | 
				
			||||||
	case WLR_INPUT_DEVICE_POINTER:
 | 
					 | 
				
			||||||
		return "pointer";
 | 
					 | 
				
			||||||
	case WLR_INPUT_DEVICE_SWITCH:
 | 
					 | 
				
			||||||
		return "switch";
 | 
					 | 
				
			||||||
	case WLR_INPUT_DEVICE_TOUCH:
 | 
					 | 
				
			||||||
		return "touch";
 | 
					 | 
				
			||||||
	case WLR_INPUT_DEVICE_TABLET_TOOL:
 | 
					 | 
				
			||||||
		return "tablet tool";
 | 
					 | 
				
			||||||
	case WLR_INPUT_DEVICE_TABLET_PAD:
 | 
					 | 
				
			||||||
		return "tablet pad";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat *input_get_seat(struct roots_input *input, char *name) {
 | 
					 | 
				
			||||||
	struct roots_seat *seat = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		if (strcmp(seat->seat->name, name) == 0) {
 | 
					 | 
				
			||||||
			return seat;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	seat = roots_seat_create(input, name);
 | 
					 | 
				
			||||||
	return seat;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_new_input(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct wlr_input_device *device = data;
 | 
					 | 
				
			||||||
	struct roots_input *input = wl_container_of(listener, input, new_input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	char *seat_name = ROOTS_CONFIG_DEFAULT_SEAT_NAME;
 | 
					 | 
				
			||||||
	struct roots_device_config *dc =
 | 
					 | 
				
			||||||
		roots_config_get_device(input->config, device);
 | 
					 | 
				
			||||||
	if (dc) {
 | 
					 | 
				
			||||||
		seat_name = dc->seat;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat = input_get_seat(input, seat_name);
 | 
					 | 
				
			||||||
	if (!seat) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "could not create roots seat");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "New input device: %s (%d:%d) %s seat:%s", device->name,
 | 
					 | 
				
			||||||
			device->vendor, device->product, device_type(device->type), seat_name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_seat_add_device(seat, device);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (dc && wlr_input_device_is_libinput(device)) {
 | 
					 | 
				
			||||||
		struct libinput_device *libinput_dev =
 | 
					 | 
				
			||||||
			wlr_libinput_get_device_handle(device);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "input has config, tap_enabled: %d\n", dc->tap_enabled);
 | 
					 | 
				
			||||||
		if (dc->tap_enabled) {
 | 
					 | 
				
			||||||
			libinput_device_config_tap_set_enabled(libinput_dev,
 | 
					 | 
				
			||||||
					LIBINPUT_CONFIG_TAP_ENABLED);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_input *input_create(struct roots_server *server,
 | 
					 | 
				
			||||||
		struct roots_config *config) {
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "Initializing roots input");
 | 
					 | 
				
			||||||
	assert(server->desktop);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_input *input = calloc(1, sizeof(struct roots_input));
 | 
					 | 
				
			||||||
	if (input == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	input->config = config;
 | 
					 | 
				
			||||||
	input->server = server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_init(&input->seats);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	input->new_input.notify = handle_new_input;
 | 
					 | 
				
			||||||
	wl_signal_add(&server->backend->events.new_input, &input->new_input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return input;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void input_destroy(struct roots_input *input) {
 | 
					 | 
				
			||||||
	// TODO
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_seat *input_seat_from_wlr_seat(struct roots_input *input,
 | 
					 | 
				
			||||||
		struct wlr_seat *wlr_seat) {
 | 
					 | 
				
			||||||
	struct roots_seat *seat = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		if (seat->seat == wlr_seat) {
 | 
					 | 
				
			||||||
			return seat;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return seat;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool input_view_has_focus(struct roots_input *input, struct roots_view *view) {
 | 
					 | 
				
			||||||
	if (!view) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		if (view == roots_seat_get_focus(seat)) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline int64_t timespec_to_msec(const struct timespec *a) {
 | 
					 | 
				
			||||||
	return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void input_update_cursor_focus(struct roots_input *input) {
 | 
					 | 
				
			||||||
	struct timespec now;
 | 
					 | 
				
			||||||
	clock_gettime(CLOCK_MONOTONIC, &now);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		roots_cursor_update_position(seat->cursor, timespec_to_msec(&now));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,349 +0,0 @@
 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/backend/session.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_input_device.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_pointer_constraints_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_pointer.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include <xkbcommon/xkbcommon.h>
 | 
					 | 
				
			||||||
#include "rootston/bindings.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
#include "rootston/keyboard.h"
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms,
 | 
					 | 
				
			||||||
		xkb_keysym_t keysym) {
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < ROOTS_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
 | 
					 | 
				
			||||||
		if (pressed_keysyms[i] == keysym) {
 | 
					 | 
				
			||||||
			return i;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return -1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) {
 | 
					 | 
				
			||||||
	size_t n = 0;
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < ROOTS_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
 | 
					 | 
				
			||||||
		if (pressed_keysyms[i] != XKB_KEY_NoSymbol) {
 | 
					 | 
				
			||||||
			++n;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return n;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms,
 | 
					 | 
				
			||||||
		xkb_keysym_t keysym) {
 | 
					 | 
				
			||||||
	ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
 | 
					 | 
				
			||||||
	if (i < 0) {
 | 
					 | 
				
			||||||
		i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol);
 | 
					 | 
				
			||||||
		if (i >= 0) {
 | 
					 | 
				
			||||||
			pressed_keysyms[i] = keysym;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms,
 | 
					 | 
				
			||||||
		xkb_keysym_t keysym) {
 | 
					 | 
				
			||||||
	ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
 | 
					 | 
				
			||||||
	if (i >= 0) {
 | 
					 | 
				
			||||||
		pressed_keysyms[i] = XKB_KEY_NoSymbol;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool keysym_is_modifier(xkb_keysym_t keysym) {
 | 
					 | 
				
			||||||
	switch (keysym) {
 | 
					 | 
				
			||||||
	case XKB_KEY_Shift_L: case XKB_KEY_Shift_R:
 | 
					 | 
				
			||||||
	case XKB_KEY_Control_L: case XKB_KEY_Control_R:
 | 
					 | 
				
			||||||
	case XKB_KEY_Caps_Lock:
 | 
					 | 
				
			||||||
	case XKB_KEY_Shift_Lock:
 | 
					 | 
				
			||||||
	case XKB_KEY_Meta_L: case XKB_KEY_Meta_R:
 | 
					 | 
				
			||||||
	case XKB_KEY_Alt_L: case XKB_KEY_Alt_R:
 | 
					 | 
				
			||||||
	case XKB_KEY_Super_L: case XKB_KEY_Super_R:
 | 
					 | 
				
			||||||
	case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R:
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms,
 | 
					 | 
				
			||||||
		const xkb_keysym_t *keysyms, size_t keysyms_len,
 | 
					 | 
				
			||||||
		enum wlr_key_state state) {
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < keysyms_len; ++i) {
 | 
					 | 
				
			||||||
		if (keysym_is_modifier(keysyms[i])) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (state == WLR_KEY_PRESSED) {
 | 
					 | 
				
			||||||
			pressed_keysyms_add(pressed_keysyms, keysyms[i]);
 | 
					 | 
				
			||||||
		} else { // WLR_KEY_RELEASED
 | 
					 | 
				
			||||||
			pressed_keysyms_remove(pressed_keysyms, keysyms[i]);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void keyboard_binding_execute(struct roots_keyboard *keyboard,
 | 
					 | 
				
			||||||
		const char *command) {
 | 
					 | 
				
			||||||
	execute_binding_command(keyboard->seat, keyboard->input, command);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Execute a built-in, hardcoded compositor binding. These are triggered from a
 | 
					 | 
				
			||||||
 * single keysym.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Returns true if the keysym was handled by a binding and false if the event
 | 
					 | 
				
			||||||
 * should be propagated to clients.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static bool keyboard_execute_compositor_binding(struct roots_keyboard *keyboard,
 | 
					 | 
				
			||||||
		xkb_keysym_t keysym) {
 | 
					 | 
				
			||||||
	if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
 | 
					 | 
				
			||||||
			keysym <= XKB_KEY_XF86Switch_VT_12) {
 | 
					 | 
				
			||||||
		struct roots_server *server = keyboard->input->server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct wlr_session *session = wlr_backend_get_session(server->backend);
 | 
					 | 
				
			||||||
		if (session) {
 | 
					 | 
				
			||||||
			unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
 | 
					 | 
				
			||||||
			wlr_session_change_vt(session, vt);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (keysym == XKB_KEY_Escape) {
 | 
					 | 
				
			||||||
		wlr_seat_pointer_end_grab(keyboard->seat->seat);
 | 
					 | 
				
			||||||
		wlr_seat_keyboard_end_grab(keyboard->seat->seat);
 | 
					 | 
				
			||||||
		roots_seat_end_compositor_grab(keyboard->seat);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Execute keyboard bindings. These include compositor bindings and user-defined
 | 
					 | 
				
			||||||
 * bindings.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Returns true if the keysym was handled by a binding and false if the event
 | 
					 | 
				
			||||||
 * should be propagated to clients.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static bool keyboard_execute_binding(struct roots_keyboard *keyboard,
 | 
					 | 
				
			||||||
		xkb_keysym_t *pressed_keysyms, uint32_t modifiers,
 | 
					 | 
				
			||||||
		const xkb_keysym_t *keysyms, size_t keysyms_len) {
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < keysyms_len; ++i) {
 | 
					 | 
				
			||||||
		if (keyboard_execute_compositor_binding(keyboard, keysyms[i])) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// User-defined bindings
 | 
					 | 
				
			||||||
	size_t n = pressed_keysyms_length(pressed_keysyms);
 | 
					 | 
				
			||||||
	struct wl_list *bindings = &keyboard->input->server->config->bindings;
 | 
					 | 
				
			||||||
	struct roots_binding_config *bc;
 | 
					 | 
				
			||||||
	wl_list_for_each(bc, bindings, link) {
 | 
					 | 
				
			||||||
		if (modifiers ^ bc->modifiers || n != bc->keysyms_len) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		bool ok = true;
 | 
					 | 
				
			||||||
		for (size_t i = 0; i < bc->keysyms_len; i++) {
 | 
					 | 
				
			||||||
			ssize_t j = pressed_keysyms_index(pressed_keysyms, bc->keysyms[i]);
 | 
					 | 
				
			||||||
			if (j < 0) {
 | 
					 | 
				
			||||||
				ok = false;
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (ok) {
 | 
					 | 
				
			||||||
			keyboard_binding_execute(keyboard, bc->command);
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * Get keysyms and modifiers from the keyboard as xkb sees them.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This uses the xkb keysyms translation based on pressed modifiers and clears
 | 
					 | 
				
			||||||
 * the consumed modifiers from the list of modifiers passed to keybind
 | 
					 | 
				
			||||||
 * detection.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * On US layout, pressing Alt+Shift+2 will trigger Alt+@.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static size_t keyboard_keysyms_translated(struct roots_keyboard *keyboard,
 | 
					 | 
				
			||||||
		xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
 | 
					 | 
				
			||||||
		uint32_t *modifiers) {
 | 
					 | 
				
			||||||
	*modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard);
 | 
					 | 
				
			||||||
	xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
 | 
					 | 
				
			||||||
		keyboard->device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
 | 
					 | 
				
			||||||
	*modifiers = *modifiers & ~consumed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state,
 | 
					 | 
				
			||||||
		keycode, keysyms);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * Get keysyms and modifiers from the keyboard as if modifiers didn't change
 | 
					 | 
				
			||||||
 * keysyms.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This avoids the xkb keysym translation based on modifiers considered pressed
 | 
					 | 
				
			||||||
 * in the state.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This will trigger keybinds such as Alt+Shift+2.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static size_t keyboard_keysyms_raw(struct roots_keyboard *keyboard,
 | 
					 | 
				
			||||||
		xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
 | 
					 | 
				
			||||||
		uint32_t *modifiers) {
 | 
					 | 
				
			||||||
	*modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	xkb_layout_index_t layout_index = xkb_state_key_get_layout(
 | 
					 | 
				
			||||||
		keyboard->device->keyboard->xkb_state, keycode);
 | 
					 | 
				
			||||||
	return xkb_keymap_key_get_syms_by_level(keyboard->device->keyboard->keymap,
 | 
					 | 
				
			||||||
		keycode, layout_index, 0, keysyms);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_keyboard_handle_key(struct roots_keyboard *keyboard,
 | 
					 | 
				
			||||||
		struct wlr_event_keyboard_key *event) {
 | 
					 | 
				
			||||||
	xkb_keycode_t keycode = event->keycode + 8;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool handled = false;
 | 
					 | 
				
			||||||
	uint32_t modifiers;
 | 
					 | 
				
			||||||
	const xkb_keysym_t *keysyms;
 | 
					 | 
				
			||||||
	size_t keysyms_len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Handle translated keysyms
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	keysyms_len = keyboard_keysyms_translated(keyboard, keycode, &keysyms,
 | 
					 | 
				
			||||||
		&modifiers);
 | 
					 | 
				
			||||||
	pressed_keysyms_update(keyboard->pressed_keysyms_translated, keysyms,
 | 
					 | 
				
			||||||
		keysyms_len, event->state);
 | 
					 | 
				
			||||||
	if (event->state == WLR_KEY_PRESSED) {
 | 
					 | 
				
			||||||
		handled = keyboard_execute_binding(keyboard,
 | 
					 | 
				
			||||||
			keyboard->pressed_keysyms_translated, modifiers, keysyms,
 | 
					 | 
				
			||||||
			keysyms_len);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Handle raw keysyms
 | 
					 | 
				
			||||||
	keysyms_len = keyboard_keysyms_raw(keyboard, keycode, &keysyms, &modifiers);
 | 
					 | 
				
			||||||
	pressed_keysyms_update(keyboard->pressed_keysyms_raw, keysyms, keysyms_len,
 | 
					 | 
				
			||||||
		event->state);
 | 
					 | 
				
			||||||
	if (event->state == WLR_KEY_PRESSED && !handled) {
 | 
					 | 
				
			||||||
		handled = keyboard_execute_binding(keyboard,
 | 
					 | 
				
			||||||
			keyboard->pressed_keysyms_raw, modifiers, keysyms, keysyms_len);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!handled) {
 | 
					 | 
				
			||||||
		wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device);
 | 
					 | 
				
			||||||
		wlr_seat_keyboard_notify_key(keyboard->seat->seat, event->time_msec,
 | 
					 | 
				
			||||||
			event->keycode, event->state);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_keyboard_handle_modifiers(struct roots_keyboard *r_keyboard) {
 | 
					 | 
				
			||||||
	struct wlr_seat *seat = r_keyboard->seat->seat;
 | 
					 | 
				
			||||||
	wlr_seat_set_keyboard(seat, r_keyboard->device);
 | 
					 | 
				
			||||||
	wlr_seat_keyboard_notify_modifiers(seat,
 | 
					 | 
				
			||||||
		&r_keyboard->device->keyboard->modifiers);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void keyboard_config_merge(struct roots_keyboard_config *config,
 | 
					 | 
				
			||||||
		struct roots_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;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (config->meta_key == 0) {
 | 
					 | 
				
			||||||
		config->meta_key = fallback->meta_key;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (config->name == NULL) {
 | 
					 | 
				
			||||||
		config->name = fallback->name;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (config->repeat_rate <= 0) {
 | 
					 | 
				
			||||||
		config->repeat_rate = fallback->repeat_rate;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (config->repeat_delay <= 0) {
 | 
					 | 
				
			||||||
		config->repeat_delay = fallback->repeat_delay;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_keyboard *roots_keyboard_create(struct wlr_input_device *device,
 | 
					 | 
				
			||||||
		struct roots_input *input) {
 | 
					 | 
				
			||||||
	struct roots_keyboard *keyboard = calloc(sizeof(struct roots_keyboard), 1);
 | 
					 | 
				
			||||||
	if (keyboard == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	device->data = keyboard;
 | 
					 | 
				
			||||||
	keyboard->device = device;
 | 
					 | 
				
			||||||
	keyboard->input = input;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_keyboard_config *config =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_keyboard_config));
 | 
					 | 
				
			||||||
	if (config == NULL) {
 | 
					 | 
				
			||||||
		free(keyboard);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	keyboard_config_merge(config, roots_config_get_keyboard(input->config, device));
 | 
					 | 
				
			||||||
	keyboard_config_merge(config, roots_config_get_keyboard(input->config, NULL));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_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);
 | 
					 | 
				
			||||||
	keyboard->config = config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct xkb_rule_names rules = { 0 };
 | 
					 | 
				
			||||||
	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(WLR_ERROR, "Cannot create XKB context");
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
 | 
					 | 
				
			||||||
		XKB_KEYMAP_COMPILE_NO_FLAGS);
 | 
					 | 
				
			||||||
	if (keymap == NULL) {
 | 
					 | 
				
			||||||
		xkb_context_unref(context);
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Cannot create XKB keymap");
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_keyboard_set_keymap(device->keyboard, keymap);
 | 
					 | 
				
			||||||
	xkb_keymap_unref(keymap);
 | 
					 | 
				
			||||||
	xkb_context_unref(context);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int repeat_rate = (config->repeat_rate > 0) ? config->repeat_rate : 25;
 | 
					 | 
				
			||||||
	int repeat_delay = (config->repeat_delay > 0) ? config->repeat_delay : 600;
 | 
					 | 
				
			||||||
	wlr_keyboard_set_repeat_info(device->keyboard, repeat_rate, repeat_delay);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return keyboard;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_keyboard_destroy(struct roots_keyboard *keyboard) {
 | 
					 | 
				
			||||||
	wl_list_remove(&keyboard->link);
 | 
					 | 
				
			||||||
	free(keyboard->config);
 | 
					 | 
				
			||||||
	free(keyboard);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,506 +0,0 @@
 | 
				
			||||||
#ifndef _POSIX_C_SOURCE
 | 
					 | 
				
			||||||
#define _POSIX_C_SOURCE 200112L
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <errno.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_surface.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_layer_shell_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/desktop.h"
 | 
					 | 
				
			||||||
#include "rootston/layers.h"
 | 
					 | 
				
			||||||
#include "rootston/output.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void apply_exclusive(struct wlr_box *usable_area,
 | 
					 | 
				
			||||||
		uint32_t anchor, int32_t exclusive,
 | 
					 | 
				
			||||||
		int32_t margin_top, int32_t margin_right,
 | 
					 | 
				
			||||||
		int32_t margin_bottom, int32_t margin_left) {
 | 
					 | 
				
			||||||
	if (exclusive <= 0) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		uint32_t anchors;
 | 
					 | 
				
			||||||
		int *positive_axis;
 | 
					 | 
				
			||||||
		int *negative_axis;
 | 
					 | 
				
			||||||
		int margin;
 | 
					 | 
				
			||||||
	} edges[] = {
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			.anchors =
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
 | 
					 | 
				
			||||||
			.positive_axis = &usable_area->y,
 | 
					 | 
				
			||||||
			.negative_axis = &usable_area->height,
 | 
					 | 
				
			||||||
			.margin = margin_top,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			.anchors =
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
 | 
					 | 
				
			||||||
			.positive_axis = NULL,
 | 
					 | 
				
			||||||
			.negative_axis = &usable_area->height,
 | 
					 | 
				
			||||||
			.margin = margin_bottom,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			.anchors =
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
 | 
					 | 
				
			||||||
			.positive_axis = &usable_area->x,
 | 
					 | 
				
			||||||
			.negative_axis = &usable_area->width,
 | 
					 | 
				
			||||||
			.margin = margin_left,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			.anchors =
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
 | 
					 | 
				
			||||||
				ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
 | 
					 | 
				
			||||||
			.positive_axis = NULL,
 | 
					 | 
				
			||||||
			.negative_axis = &usable_area->width,
 | 
					 | 
				
			||||||
			.margin = margin_right,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
 | 
					 | 
				
			||||||
		if ((anchor & edges[i].anchors) == edges[i].anchors) {
 | 
					 | 
				
			||||||
			if (edges[i].positive_axis) {
 | 
					 | 
				
			||||||
				*edges[i].positive_axis += exclusive + edges[i].margin;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (edges[i].negative_axis) {
 | 
					 | 
				
			||||||
				*edges[i].negative_axis -= exclusive + edges[i].margin;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void update_cursors(struct roots_layer_surface *roots_surface,
 | 
					 | 
				
			||||||
		struct wl_list *seats /* struct roots_seat */) {
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, seats, link) {
 | 
					 | 
				
			||||||
		double sx, sy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct wlr_surface *surface = desktop_surface_at(
 | 
					 | 
				
			||||||
			seat->input->server->desktop,
 | 
					 | 
				
			||||||
			seat->cursor->cursor->x, seat->cursor->cursor->y, &sx, &sy, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (surface == roots_surface->layer_surface->surface) {
 | 
					 | 
				
			||||||
			struct timespec time;
 | 
					 | 
				
			||||||
			if (clock_gettime(CLOCK_MONOTONIC, &time) == 0) {
 | 
					 | 
				
			||||||
				roots_cursor_update_position(seat->cursor,
 | 
					 | 
				
			||||||
					time.tv_sec * 1000 + time.tv_nsec / 1000000);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "Failed to get time, not updating"
 | 
					 | 
				
			||||||
					"position. Errno: %s\n", strerror(errno));
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void arrange_layer(struct wlr_output *output,
 | 
					 | 
				
			||||||
		struct wl_list *seats /* struct *roots_seat */,
 | 
					 | 
				
			||||||
		struct wl_list *list /* struct *roots_layer_surface */,
 | 
					 | 
				
			||||||
		struct wlr_box *usable_area, bool exclusive) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *roots_surface;
 | 
					 | 
				
			||||||
	struct wlr_box full_area = { 0 };
 | 
					 | 
				
			||||||
	wlr_output_effective_resolution(output,
 | 
					 | 
				
			||||||
			&full_area.width, &full_area.height);
 | 
					 | 
				
			||||||
	wl_list_for_each_reverse(roots_surface, list, link) {
 | 
					 | 
				
			||||||
		struct wlr_layer_surface_v1 *layer = roots_surface->layer_surface;
 | 
					 | 
				
			||||||
		struct wlr_layer_surface_v1_state *state = &layer->current;
 | 
					 | 
				
			||||||
		if (exclusive != (state->exclusive_zone > 0)) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		struct wlr_box bounds;
 | 
					 | 
				
			||||||
		if (state->exclusive_zone == -1) {
 | 
					 | 
				
			||||||
			bounds = full_area;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			bounds = *usable_area;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		struct wlr_box box = {
 | 
					 | 
				
			||||||
			.width = state->desired_width,
 | 
					 | 
				
			||||||
			.height = state->desired_height
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
		// Horizontal axis
 | 
					 | 
				
			||||||
		const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
 | 
					 | 
				
			||||||
			| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
 | 
					 | 
				
			||||||
		if ((state->anchor & both_horiz) && box.width == 0) {
 | 
					 | 
				
			||||||
			box.x = bounds.x;
 | 
					 | 
				
			||||||
			box.width = bounds.width;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
 | 
					 | 
				
			||||||
			box.x = bounds.x;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
 | 
					 | 
				
			||||||
			box.x = bounds.x + (bounds.width - box.width);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// Vertical axis
 | 
					 | 
				
			||||||
		const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
 | 
					 | 
				
			||||||
			| ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
 | 
					 | 
				
			||||||
		if ((state->anchor & both_vert) && box.height == 0) {
 | 
					 | 
				
			||||||
			box.y = bounds.y;
 | 
					 | 
				
			||||||
			box.height = bounds.height;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
 | 
					 | 
				
			||||||
			box.y = bounds.y;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
 | 
					 | 
				
			||||||
			box.y = bounds.y + (bounds.height - box.height);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// Margin
 | 
					 | 
				
			||||||
		if ((state->anchor & both_horiz) == both_horiz) {
 | 
					 | 
				
			||||||
			box.x += state->margin.left;
 | 
					 | 
				
			||||||
			box.width -= state->margin.left + state->margin.right;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
 | 
					 | 
				
			||||||
			box.x += state->margin.left;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
 | 
					 | 
				
			||||||
			box.x -= state->margin.right;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if ((state->anchor & both_vert) == both_vert) {
 | 
					 | 
				
			||||||
			box.y += state->margin.top;
 | 
					 | 
				
			||||||
			box.height -= state->margin.top + state->margin.bottom;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
 | 
					 | 
				
			||||||
			box.y += state->margin.top;
 | 
					 | 
				
			||||||
		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
 | 
					 | 
				
			||||||
			box.y -= state->margin.bottom;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (box.width < 0 || box.height < 0) {
 | 
					 | 
				
			||||||
			// TODO: Bubble up a protocol error?
 | 
					 | 
				
			||||||
			wlr_layer_surface_v1_close(layer);
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Apply
 | 
					 | 
				
			||||||
		struct wlr_box old_geo = roots_surface->geo;
 | 
					 | 
				
			||||||
		roots_surface->geo = box;
 | 
					 | 
				
			||||||
		apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
 | 
					 | 
				
			||||||
				state->margin.top, state->margin.right,
 | 
					 | 
				
			||||||
				state->margin.bottom, state->margin.left);
 | 
					 | 
				
			||||||
		wlr_layer_surface_v1_configure(layer, box.width, box.height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Having a cursor newly end up over the moved layer will not
 | 
					 | 
				
			||||||
		// automatically send a motion event to the surface. The event needs to
 | 
					 | 
				
			||||||
		// be synthesized.
 | 
					 | 
				
			||||||
		// Only update layer surfaces which kept their size (and so buffers) the
 | 
					 | 
				
			||||||
		// same, because those with resized buffers will be handled separately.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (roots_surface->geo.x != old_geo.x
 | 
					 | 
				
			||||||
				|| roots_surface->geo.y != old_geo.y) {
 | 
					 | 
				
			||||||
			update_cursors(roots_surface, seats);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void arrange_layers(struct roots_output *output) {
 | 
					 | 
				
			||||||
	struct wlr_box usable_area = { 0 };
 | 
					 | 
				
			||||||
	wlr_output_effective_resolution(output->wlr_output,
 | 
					 | 
				
			||||||
			&usable_area.width, &usable_area.height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Arrange exclusive surfaces from top->bottom
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
 | 
					 | 
				
			||||||
			&usable_area, true);
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
 | 
					 | 
				
			||||||
			&usable_area, true);
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
 | 
					 | 
				
			||||||
			&usable_area, true);
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
 | 
					 | 
				
			||||||
			&usable_area, true);
 | 
					 | 
				
			||||||
	memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *view;
 | 
					 | 
				
			||||||
	wl_list_for_each(view, &output->desktop->views, link) {
 | 
					 | 
				
			||||||
		if (view->maximized) {
 | 
					 | 
				
			||||||
			view_arrange_maximized(view);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Arrange non-exlusive surfaces from top->bottom
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
 | 
					 | 
				
			||||||
			&usable_area, false);
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
 | 
					 | 
				
			||||||
			&usable_area, false);
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
 | 
					 | 
				
			||||||
			&usable_area, false);
 | 
					 | 
				
			||||||
	arrange_layer(output->wlr_output, &output->desktop->server->input->seats,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
 | 
					 | 
				
			||||||
			&usable_area, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Find topmost keyboard interactive layer, if such a layer exists
 | 
					 | 
				
			||||||
	uint32_t layers_above_shell[] = {
 | 
					 | 
				
			||||||
		ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
 | 
					 | 
				
			||||||
		ZWLR_LAYER_SHELL_V1_LAYER_TOP,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer, *topmost = NULL;
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < nlayers; ++i) {
 | 
					 | 
				
			||||||
		wl_list_for_each_reverse(layer,
 | 
					 | 
				
			||||||
				&output->layers[layers_above_shell[i]], link) {
 | 
					 | 
				
			||||||
			if (layer->layer_surface->current.keyboard_interactive) {
 | 
					 | 
				
			||||||
				topmost = layer;
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (topmost != NULL) {
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_input *input = output->desktop->server->input;
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		roots_seat_set_focus_layer(seat,
 | 
					 | 
				
			||||||
				topmost ? topmost->layer_surface : NULL);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_output_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer =
 | 
					 | 
				
			||||||
		wl_container_of(listener, layer, output_destroy);
 | 
					 | 
				
			||||||
	layer->layer_surface->output = NULL;
 | 
					 | 
				
			||||||
	wl_list_remove(&layer->output_destroy.link);
 | 
					 | 
				
			||||||
	wlr_layer_surface_v1_close(layer->layer_surface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_surface_commit(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer =
 | 
					 | 
				
			||||||
		wl_container_of(listener, layer, surface_commit);
 | 
					 | 
				
			||||||
	struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = layer_surface->output;
 | 
					 | 
				
			||||||
	if (wlr_output != NULL) {
 | 
					 | 
				
			||||||
		struct roots_output *output = wlr_output->data;
 | 
					 | 
				
			||||||
		struct wlr_box old_geo = layer->geo;
 | 
					 | 
				
			||||||
		arrange_layers(output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Cursor changes which happen as a consequence of resizing a layer
 | 
					 | 
				
			||||||
		// surface are applied in arrange_layers. Because the resize happens
 | 
					 | 
				
			||||||
		// before the underlying surface changes, it will only receive a cursor
 | 
					 | 
				
			||||||
		// update if the new cursor position crosses the *old* sized surface in
 | 
					 | 
				
			||||||
		// the *new* layer surface.
 | 
					 | 
				
			||||||
		// Another cursor move event is needed when the surface actually
 | 
					 | 
				
			||||||
		// changes.
 | 
					 | 
				
			||||||
		struct wlr_surface *surface = layer_surface->surface;
 | 
					 | 
				
			||||||
		if (surface->previous.width != surface->current.width ||
 | 
					 | 
				
			||||||
				surface->previous.height != surface->current.height) {
 | 
					 | 
				
			||||||
			update_cursors(layer, &output->desktop->server->input->seats);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) {
 | 
					 | 
				
			||||||
			output_damage_whole_local_surface(output, layer_surface->surface,
 | 
					 | 
				
			||||||
					old_geo.x, old_geo.y);
 | 
					 | 
				
			||||||
			output_damage_whole_local_surface(output, layer_surface->surface,
 | 
					 | 
				
			||||||
					layer->geo.x, layer->geo.y);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			output_damage_from_local_surface(output, layer_surface->surface,
 | 
					 | 
				
			||||||
					layer->geo.x, layer->geo.y);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void unmap(struct wlr_layer_surface_v1 *layer_surface) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer = layer_surface->data;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = layer_surface->output;
 | 
					 | 
				
			||||||
	if (wlr_output != NULL) {
 | 
					 | 
				
			||||||
		struct roots_output *output = wlr_output->data;
 | 
					 | 
				
			||||||
		output_damage_whole_local_surface(output, layer_surface->surface,
 | 
					 | 
				
			||||||
			layer->geo.x, layer->geo.y);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer = wl_container_of(
 | 
					 | 
				
			||||||
			listener, layer, destroy);
 | 
					 | 
				
			||||||
	if (layer->layer_surface->mapped) {
 | 
					 | 
				
			||||||
		unmap(layer->layer_surface);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wl_list_remove(&layer->link);
 | 
					 | 
				
			||||||
	wl_list_remove(&layer->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&layer->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&layer->unmap.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&layer->surface_commit.link);
 | 
					 | 
				
			||||||
	if (layer->layer_surface->output) {
 | 
					 | 
				
			||||||
		wl_list_remove(&layer->output_destroy.link);
 | 
					 | 
				
			||||||
		arrange_layers((struct roots_output *)layer->layer_surface->output->data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	free(layer);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_map(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct wlr_layer_surface_v1 *layer_surface = data;
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer = layer_surface->data;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = layer_surface->output;
 | 
					 | 
				
			||||||
	struct roots_output *output = wlr_output->data;
 | 
					 | 
				
			||||||
	output_damage_whole_local_surface(output, layer_surface->surface,
 | 
					 | 
				
			||||||
		layer->geo.x, layer->geo.y);
 | 
					 | 
				
			||||||
	wlr_surface_send_enter(layer_surface->surface, wlr_output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_unmap(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer = wl_container_of(
 | 
					 | 
				
			||||||
			listener, layer, unmap);
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = layer->layer_surface->output;
 | 
					 | 
				
			||||||
	struct roots_output *output = wlr_output->data;
 | 
					 | 
				
			||||||
	unmap(layer->layer_surface);
 | 
					 | 
				
			||||||
	input_update_cursor_focus(output->desktop->server->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_map(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_popup *popup = wl_container_of(listener, popup, map);
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer = popup->parent;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = layer->layer_surface->output;
 | 
					 | 
				
			||||||
	struct roots_output *output = wlr_output->data;
 | 
					 | 
				
			||||||
	int ox = popup->wlr_popup->geometry.x + layer->geo.x;
 | 
					 | 
				
			||||||
	int oy = popup->wlr_popup->geometry.y + layer->geo.y;
 | 
					 | 
				
			||||||
	output_damage_whole_local_surface(output, popup->wlr_popup->base->surface,
 | 
					 | 
				
			||||||
		ox, oy);
 | 
					 | 
				
			||||||
	input_update_cursor_focus(output->desktop->server->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_unmap(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_popup *popup = wl_container_of(listener, popup, unmap);
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer = popup->parent;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = layer->layer_surface->output;
 | 
					 | 
				
			||||||
	struct roots_output *output = wlr_output->data;
 | 
					 | 
				
			||||||
	int ox = popup->wlr_popup->geometry.x + layer->geo.x;
 | 
					 | 
				
			||||||
	int oy = popup->wlr_popup->geometry.y + layer->geo.y;
 | 
					 | 
				
			||||||
	output_damage_whole_local_surface(output, popup->wlr_popup->base->surface,
 | 
					 | 
				
			||||||
		ox, oy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_commit(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_popup *popup = wl_container_of(listener, popup, commit);
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer = popup->parent;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = layer->layer_surface->output;
 | 
					 | 
				
			||||||
	struct roots_output *output = wlr_output->data;
 | 
					 | 
				
			||||||
	int ox = popup->wlr_popup->geometry.x + layer->geo.x;
 | 
					 | 
				
			||||||
	int oy = popup->wlr_popup->geometry.y + layer->geo.y;
 | 
					 | 
				
			||||||
	output_damage_from_local_surface(output, popup->wlr_popup->base->surface,
 | 
					 | 
				
			||||||
		ox, oy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_popup *popup =
 | 
					 | 
				
			||||||
		wl_container_of(listener, popup, destroy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->unmap.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->commit.link);
 | 
					 | 
				
			||||||
	free(popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_layer_popup *popup_create(struct roots_layer_surface *parent,
 | 
					 | 
				
			||||||
		struct wlr_xdg_popup *wlr_popup) {
 | 
					 | 
				
			||||||
	struct roots_layer_popup *popup =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_layer_popup));
 | 
					 | 
				
			||||||
	if (popup == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	popup->wlr_popup = wlr_popup;
 | 
					 | 
				
			||||||
	popup->parent = parent;
 | 
					 | 
				
			||||||
	popup->map.notify = popup_handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.map, &popup->map);
 | 
					 | 
				
			||||||
	popup->unmap.notify = popup_handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
 | 
					 | 
				
			||||||
	popup->destroy.notify = popup_handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
 | 
					 | 
				
			||||||
	popup->commit.notify = popup_handle_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
 | 
					 | 
				
			||||||
	/* TODO: popups can have popups, see xdg_shell::popup_create */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return popup;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_new_popup(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *roots_layer_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_layer_surface, new_popup);
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *wlr_popup = data;
 | 
					 | 
				
			||||||
	popup_create(roots_layer_surface, wlr_popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct wlr_layer_surface_v1 *layer_surface = data;
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, layer_shell_surface);
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
 | 
					 | 
				
			||||||
			"size %dx%d margin %d,%d,%d,%d",
 | 
					 | 
				
			||||||
		layer_surface->namespace, layer_surface->layer, layer_surface->layer,
 | 
					 | 
				
			||||||
		layer_surface->client_pending.desired_width,
 | 
					 | 
				
			||||||
		layer_surface->client_pending.desired_height,
 | 
					 | 
				
			||||||
		layer_surface->client_pending.margin.top,
 | 
					 | 
				
			||||||
		layer_surface->client_pending.margin.right,
 | 
					 | 
				
			||||||
		layer_surface->client_pending.margin.bottom,
 | 
					 | 
				
			||||||
		layer_surface->client_pending.margin.left);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!layer_surface->output) {
 | 
					 | 
				
			||||||
		struct roots_input *input = desktop->server->input;
 | 
					 | 
				
			||||||
		struct roots_seat *seat = input_last_active_seat(input);
 | 
					 | 
				
			||||||
		assert(seat); // Technically speaking we should handle this case
 | 
					 | 
				
			||||||
		struct wlr_output *output =
 | 
					 | 
				
			||||||
			wlr_output_layout_output_at(desktop->layout,
 | 
					 | 
				
			||||||
					seat->cursor->cursor->x,
 | 
					 | 
				
			||||||
					seat->cursor->cursor->y);
 | 
					 | 
				
			||||||
		if (!output) {
 | 
					 | 
				
			||||||
			wlr_log(WLR_ERROR, "Couldn't find output at (%.0f,%.0f)",
 | 
					 | 
				
			||||||
				seat->cursor->cursor->x,
 | 
					 | 
				
			||||||
				seat->cursor->cursor->y);
 | 
					 | 
				
			||||||
			output = wlr_output_layout_get_center_output(desktop->layout);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (output) {
 | 
					 | 
				
			||||||
			layer_surface->output = output;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			wlr_layer_surface_v1_close(layer_surface);
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_layer_surface *roots_surface =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_layer_surface));
 | 
					 | 
				
			||||||
	if (!roots_surface) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->surface_commit.notify = handle_surface_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&layer_surface->surface->events.commit,
 | 
					 | 
				
			||||||
		&roots_surface->surface_commit);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->output_destroy.notify = handle_output_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&layer_surface->output->events.destroy,
 | 
					 | 
				
			||||||
		&roots_surface->output_destroy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->destroy.notify = handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&layer_surface->events.destroy, &roots_surface->destroy);
 | 
					 | 
				
			||||||
	roots_surface->map.notify = handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&layer_surface->events.map, &roots_surface->map);
 | 
					 | 
				
			||||||
	roots_surface->unmap.notify = handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&layer_surface->events.unmap, &roots_surface->unmap);
 | 
					 | 
				
			||||||
	roots_surface->new_popup.notify = handle_new_popup;
 | 
					 | 
				
			||||||
	wl_signal_add(&layer_surface->events.new_popup, &roots_surface->new_popup);
 | 
					 | 
				
			||||||
	// TODO: Listen for subsurfaces
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->layer_surface = layer_surface;
 | 
					 | 
				
			||||||
	layer_surface->data = roots_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_output *output = layer_surface->output->data;
 | 
					 | 
				
			||||||
	wl_list_insert(&output->layers[layer_surface->layer], &roots_surface->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Temporarily set the layer's current state to client_pending
 | 
					 | 
				
			||||||
	// So that we can easily arrange it
 | 
					 | 
				
			||||||
	struct wlr_layer_surface_v1_state old_state = layer_surface->current;
 | 
					 | 
				
			||||||
	layer_surface->current = layer_surface->client_pending;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	arrange_layers(output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	layer_surface->current = old_state;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,83 +0,0 @@
 | 
				
			||||||
#define _POSIX_C_SOURCE 200112L
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/backend.h>
 | 
					 | 
				
			||||||
#include <wlr/backend/headless.h>
 | 
					 | 
				
			||||||
#include <wlr/backend/multi.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/render/wlr_renderer.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/config.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_server server = { 0 };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char **argv) {
 | 
					 | 
				
			||||||
	wlr_log_init(WLR_DEBUG, NULL);
 | 
					 | 
				
			||||||
	server.config = roots_config_create_from_args(argc, argv);
 | 
					 | 
				
			||||||
	server.wl_display = wl_display_create();
 | 
					 | 
				
			||||||
	server.wl_event_loop = wl_display_get_event_loop(server.wl_display);
 | 
					 | 
				
			||||||
	assert(server.config && server.wl_display && server.wl_event_loop);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	server.backend = wlr_backend_autocreate(server.wl_display, NULL);
 | 
					 | 
				
			||||||
	if (server.backend == NULL) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "could not start backend");
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	server.renderer = wlr_backend_get_renderer(server.backend);
 | 
					 | 
				
			||||||
	assert(server.renderer);
 | 
					 | 
				
			||||||
	server.data_device_manager =
 | 
					 | 
				
			||||||
		wlr_data_device_manager_create(server.wl_display);
 | 
					 | 
				
			||||||
	wlr_renderer_init_wl_display(server.renderer, server.wl_display);
 | 
					 | 
				
			||||||
	server.desktop = desktop_create(&server, server.config);
 | 
					 | 
				
			||||||
	server.input = input_create(&server, server.config);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const char *socket = wl_display_add_socket_auto(server.wl_display);
 | 
					 | 
				
			||||||
	if (!socket) {
 | 
					 | 
				
			||||||
		wlr_log_errno(WLR_ERROR, "Unable to open wayland socket");
 | 
					 | 
				
			||||||
		wlr_backend_destroy(server.backend);
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_INFO, "Running compositor on wayland display '%s'", socket);
 | 
					 | 
				
			||||||
	setenv("_WAYLAND_DISPLAY", socket, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!wlr_backend_start(server.backend)) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Failed to start backend");
 | 
					 | 
				
			||||||
		wlr_backend_destroy(server.backend);
 | 
					 | 
				
			||||||
		wl_display_destroy(server.wl_display);
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setenv("WAYLAND_DISPLAY", socket, true);
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	if (server.desktop->xwayland != NULL) {
 | 
					 | 
				
			||||||
		struct roots_seat *xwayland_seat =
 | 
					 | 
				
			||||||
			input_get_seat(server.input, ROOTS_CONFIG_DEFAULT_SEAT_NAME);
 | 
					 | 
				
			||||||
		wlr_xwayland_set_seat(server.desktop->xwayland, xwayland_seat->seat);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (server.config->startup_cmd != NULL) {
 | 
					 | 
				
			||||||
		const char *cmd = server.config->startup_cmd;
 | 
					 | 
				
			||||||
		pid_t pid = fork();
 | 
					 | 
				
			||||||
		if (pid < 0) {
 | 
					 | 
				
			||||||
			wlr_log(WLR_ERROR, "cannot execute binding command: fork() failed");
 | 
					 | 
				
			||||||
		} else if (pid == 0) {
 | 
					 | 
				
			||||||
			execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_display_run(server.wl_display);
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	// We need to shutdown Xwayland before disconnecting all clients, otherwise
 | 
					 | 
				
			||||||
	// wlroots will restart it automatically.
 | 
					 | 
				
			||||||
	wlr_xwayland_destroy(server.desktop->xwayland);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	wl_display_destroy_clients(server.wl_display);
 | 
					 | 
				
			||||||
	wl_display_destroy(server.wl_display);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,31 +0,0 @@
 | 
				
			||||||
sources = [
 | 
					 | 
				
			||||||
	'bindings.c',
 | 
					 | 
				
			||||||
	'config.c',
 | 
					 | 
				
			||||||
	'cursor.c',
 | 
					 | 
				
			||||||
	'desktop.c',
 | 
					 | 
				
			||||||
	'ini.c',
 | 
					 | 
				
			||||||
	'input.c',
 | 
					 | 
				
			||||||
	'keyboard.c',
 | 
					 | 
				
			||||||
	'layer_shell.c',
 | 
					 | 
				
			||||||
	'main.c',
 | 
					 | 
				
			||||||
	'output.c',
 | 
					 | 
				
			||||||
	'render.c',
 | 
					 | 
				
			||||||
	'seat.c',
 | 
					 | 
				
			||||||
	'switch.c',
 | 
					 | 
				
			||||||
	'text_input.c',
 | 
					 | 
				
			||||||
	'view.c',
 | 
					 | 
				
			||||||
	'virtual_keyboard.c',
 | 
					 | 
				
			||||||
	'xdg_shell_v6.c',
 | 
					 | 
				
			||||||
	'xdg_shell.c',
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if conf_data.get('WLR_HAS_XWAYLAND', 0) == 1
 | 
					 | 
				
			||||||
	sources += 'xwayland.c'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
executable(
 | 
					 | 
				
			||||||
	'rootston',
 | 
					 | 
				
			||||||
	sources,
 | 
					 | 
				
			||||||
	dependencies: [wlroots, wlr_protos, pixman],
 | 
					 | 
				
			||||||
	build_by_default: get_option('rootston'),
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,688 +0,0 @@
 | 
				
			||||||
#define _POSIX_C_SOURCE 200809L
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <wlr/backend/drm.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_compositor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output_layout.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_presentation_time.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell_v6.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include <wlr/util/region.h>
 | 
					 | 
				
			||||||
#include "rootston/config.h"
 | 
					 | 
				
			||||||
#include "rootston/layers.h"
 | 
					 | 
				
			||||||
#include "rootston/output.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Rotate a child's position relative to a parent. The parent size is (pw, ph),
 | 
					 | 
				
			||||||
 * the child position is (*sx, *sy) and its size is (sw, sh).
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void rotate_child_position(double *sx, double *sy, double sw, double sh,
 | 
					 | 
				
			||||||
		double pw, double ph, float rotation) {
 | 
					 | 
				
			||||||
	if (rotation == 0.0) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Coordinates relative to the center of the subsurface
 | 
					 | 
				
			||||||
	double cx = *sx - pw/2 + sw/2,
 | 
					 | 
				
			||||||
		cy = *sy - ph/2 + sh/2;
 | 
					 | 
				
			||||||
	// Rotated coordinates
 | 
					 | 
				
			||||||
	double rx = cos(rotation)*cx - sin(rotation)*cy,
 | 
					 | 
				
			||||||
		ry = cos(rotation)*cy + sin(rotation)*cx;
 | 
					 | 
				
			||||||
	*sx = rx + pw/2 - sw/2;
 | 
					 | 
				
			||||||
	*sy = ry + ph/2 - sh/2;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct surface_iterator_data {
 | 
					 | 
				
			||||||
	roots_surface_iterator_func_t user_iterator;
 | 
					 | 
				
			||||||
	void *user_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_output *output;
 | 
					 | 
				
			||||||
	double ox, oy;
 | 
					 | 
				
			||||||
	int width, height;
 | 
					 | 
				
			||||||
	float rotation;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool get_surface_box(struct surface_iterator_data *data,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, int sx, int sy,
 | 
					 | 
				
			||||||
		struct wlr_box *surface_box) {
 | 
					 | 
				
			||||||
	struct roots_output *output = data->output;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!wlr_surface_has_buffer(surface)) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int sw = surface->current.width;
 | 
					 | 
				
			||||||
	int sh = surface->current.height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double _sx = sx + surface->sx;
 | 
					 | 
				
			||||||
	double _sy = sy + surface->sy;
 | 
					 | 
				
			||||||
	rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height,
 | 
					 | 
				
			||||||
		data->rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box = {
 | 
					 | 
				
			||||||
		.x = data->ox + _sx,
 | 
					 | 
				
			||||||
		.y = data->oy + _sy,
 | 
					 | 
				
			||||||
		.width = sw,
 | 
					 | 
				
			||||||
		.height = sh,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	if (surface_box != NULL) {
 | 
					 | 
				
			||||||
		*surface_box = box;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box rotated_box;
 | 
					 | 
				
			||||||
	wlr_box_rotated_bounds(&rotated_box, &box, data->rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box output_box = {0};
 | 
					 | 
				
			||||||
	wlr_output_effective_resolution(output->wlr_output,
 | 
					 | 
				
			||||||
		&output_box.width, &output_box.height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box intersection;
 | 
					 | 
				
			||||||
	return wlr_box_intersection(&intersection, &output_box, &rotated_box);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_for_each_surface_iterator(struct wlr_surface *surface,
 | 
					 | 
				
			||||||
		int sx, int sy, void *_data) {
 | 
					 | 
				
			||||||
	struct surface_iterator_data *data = _data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	bool intersects = get_surface_box(data, surface, sx, sy, &box);
 | 
					 | 
				
			||||||
	if (!intersects) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	data->user_iterator(data->output, surface, &box, data->rotation,
 | 
					 | 
				
			||||||
		data->user_data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_surface_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, double ox, double oy,
 | 
					 | 
				
			||||||
		roots_surface_iterator_func_t iterator, void *user_data) {
 | 
					 | 
				
			||||||
	struct surface_iterator_data data = {
 | 
					 | 
				
			||||||
		.user_iterator = iterator,
 | 
					 | 
				
			||||||
		.user_data = user_data,
 | 
					 | 
				
			||||||
		.output = output,
 | 
					 | 
				
			||||||
		.ox = ox,
 | 
					 | 
				
			||||||
		.oy = oy,
 | 
					 | 
				
			||||||
		.width = surface->current.width,
 | 
					 | 
				
			||||||
		.height = surface->current.height,
 | 
					 | 
				
			||||||
		.rotation = 0,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_surface_for_each_surface(surface,
 | 
					 | 
				
			||||||
		output_for_each_surface_iterator, &data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_view_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct roots_view *view, roots_surface_iterator_func_t iterator,
 | 
					 | 
				
			||||||
		void *user_data) {
 | 
					 | 
				
			||||||
	struct wlr_box *output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(output->desktop->layout, output->wlr_output);
 | 
					 | 
				
			||||||
	if (!output_box) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct surface_iterator_data data = {
 | 
					 | 
				
			||||||
		.user_iterator = iterator,
 | 
					 | 
				
			||||||
		.user_data = user_data,
 | 
					 | 
				
			||||||
		.output = output,
 | 
					 | 
				
			||||||
		.ox = view->box.x - output_box->x,
 | 
					 | 
				
			||||||
		.oy = view->box.y - output_box->y,
 | 
					 | 
				
			||||||
		.width = view->box.width,
 | 
					 | 
				
			||||||
		.height = view->box.height,
 | 
					 | 
				
			||||||
		.rotation = view->rotation,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_for_each_surface(view, output_for_each_surface_iterator, &data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
void output_xwayland_children_for_each_surface(
 | 
					 | 
				
			||||||
		struct roots_output *output, struct wlr_xwayland_surface *surface,
 | 
					 | 
				
			||||||
		roots_surface_iterator_func_t iterator, void *user_data) {
 | 
					 | 
				
			||||||
	struct wlr_box *output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(output->desktop->layout, output->wlr_output);
 | 
					 | 
				
			||||||
	if (!output_box) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *child;
 | 
					 | 
				
			||||||
	wl_list_for_each(child, &surface->children, parent_link) {
 | 
					 | 
				
			||||||
		if (child->mapped) {
 | 
					 | 
				
			||||||
			double ox = child->x - output_box->x;
 | 
					 | 
				
			||||||
			double oy = child->y - output_box->y;
 | 
					 | 
				
			||||||
			output_surface_for_each_surface(output, child->surface,
 | 
					 | 
				
			||||||
				ox, oy, iterator, user_data);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		output_xwayland_children_for_each_surface(output, child,
 | 
					 | 
				
			||||||
			iterator, user_data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_layer_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wl_list *layer_surfaces, roots_surface_iterator_func_t iterator,
 | 
					 | 
				
			||||||
		void *user_data) {
 | 
					 | 
				
			||||||
	struct roots_layer_surface *layer_surface;
 | 
					 | 
				
			||||||
	wl_list_for_each(layer_surface, layer_surfaces, link) {
 | 
					 | 
				
			||||||
		struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
 | 
					 | 
				
			||||||
			layer_surface->layer_surface;
 | 
					 | 
				
			||||||
		output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
 | 
					 | 
				
			||||||
			layer_surface->geo.x, layer_surface->geo.y, iterator,
 | 
					 | 
				
			||||||
			user_data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct wlr_xdg_popup *state;
 | 
					 | 
				
			||||||
		wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) {
 | 
					 | 
				
			||||||
			struct wlr_xdg_surface *popup = state->base;
 | 
					 | 
				
			||||||
			if (!popup->configured) {
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			double popup_sx, popup_sy;
 | 
					 | 
				
			||||||
			popup_sx = layer_surface->geo.x;
 | 
					 | 
				
			||||||
			popup_sx += popup->popup->geometry.x - popup->geometry.x;
 | 
					 | 
				
			||||||
			popup_sy = layer_surface->geo.y;
 | 
					 | 
				
			||||||
			popup_sy += popup->popup->geometry.y - popup->geometry.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			output_surface_for_each_surface(output, popup->surface,
 | 
					 | 
				
			||||||
				popup_sx, popup_sy, iterator, user_data);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_drag_icons_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct roots_input *input, roots_surface_iterator_func_t iterator,
 | 
					 | 
				
			||||||
		void *user_data) {
 | 
					 | 
				
			||||||
	struct wlr_box *output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(output->desktop->layout, output->wlr_output);
 | 
					 | 
				
			||||||
	if (!output_box) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		struct roots_drag_icon *drag_icon = seat->drag_icon;
 | 
					 | 
				
			||||||
		if (!drag_icon || !drag_icon->wlr_drag_icon->mapped) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		double ox = drag_icon->x - output_box->x;
 | 
					 | 
				
			||||||
		double oy = drag_icon->y - output_box->y;
 | 
					 | 
				
			||||||
		output_surface_for_each_surface(output,
 | 
					 | 
				
			||||||
			drag_icon->wlr_drag_icon->surface, ox, oy, iterator, user_data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_for_each_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
		roots_surface_iterator_func_t iterator, void *user_data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = output->desktop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (output->fullscreen_view != NULL) {
 | 
					 | 
				
			||||||
		struct roots_view *view = output->fullscreen_view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		output_view_for_each_surface(output, view, iterator, user_data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
		if (view->type == ROOTS_XWAYLAND_VIEW) {
 | 
					 | 
				
			||||||
			struct roots_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
				roots_xwayland_surface_from_view(view);
 | 
					 | 
				
			||||||
			output_xwayland_children_for_each_surface(output,
 | 
					 | 
				
			||||||
				xwayland_surface->xwayland_surface, iterator, user_data);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		struct roots_view *view;
 | 
					 | 
				
			||||||
		wl_list_for_each_reverse(view, &desktop->views, link) {
 | 
					 | 
				
			||||||
			output_view_for_each_surface(output, view, iterator, user_data);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	output_drag_icons_for_each_surface(output, desktop->server->input,
 | 
					 | 
				
			||||||
		iterator, user_data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < len; ++i) {
 | 
					 | 
				
			||||||
		output_layer_for_each_surface(output, &output->layers[i],
 | 
					 | 
				
			||||||
			iterator, user_data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int scale_length(int length, int offset, float scale) {
 | 
					 | 
				
			||||||
	return round((offset + length) * scale) - round(offset * scale);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void scale_box(struct wlr_box *box, float scale) {
 | 
					 | 
				
			||||||
	box->width = scale_length(box->width, box->x, scale);
 | 
					 | 
				
			||||||
	box->height = scale_length(box->height, box->y, scale);
 | 
					 | 
				
			||||||
	box->x = round(box->x * scale);
 | 
					 | 
				
			||||||
	box->y = round(box->y * scale);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void get_decoration_box(struct roots_view *view,
 | 
					 | 
				
			||||||
		struct roots_output *output, struct wlr_box *box) {
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = output->wlr_output;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box deco_box;
 | 
					 | 
				
			||||||
	view_get_deco_box(view, &deco_box);
 | 
					 | 
				
			||||||
	double sx = deco_box.x - view->box.x;
 | 
					 | 
				
			||||||
	double sy = deco_box.y - view->box.y;
 | 
					 | 
				
			||||||
	rotate_child_position(&sx, &sy, deco_box.width, deco_box.height,
 | 
					 | 
				
			||||||
		view->wlr_surface->current.width,
 | 
					 | 
				
			||||||
		view->wlr_surface->current.height, view->rotation);
 | 
					 | 
				
			||||||
	double x = sx + view->box.x;
 | 
					 | 
				
			||||||
	double y = sy + view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_output_layout_output_coords(output->desktop->layout, wlr_output, &x, &y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	box->x = x * wlr_output->scale;
 | 
					 | 
				
			||||||
	box->y = y * wlr_output->scale;
 | 
					 | 
				
			||||||
	box->width = deco_box.width * wlr_output->scale;
 | 
					 | 
				
			||||||
	box->height = deco_box.height * wlr_output->scale;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_damage_whole(struct roots_output *output) {
 | 
					 | 
				
			||||||
	wlr_output_damage_add_whole(output->damage);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool view_accept_damage(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct roots_view *view) {
 | 
					 | 
				
			||||||
	if (view->wlr_surface == NULL) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (output->fullscreen_view == NULL) {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (output->fullscreen_view == view) {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	if (output->fullscreen_view->type == ROOTS_XWAYLAND_VIEW &&
 | 
					 | 
				
			||||||
			view->type == ROOTS_XWAYLAND_VIEW) {
 | 
					 | 
				
			||||||
		// Special case: accept damage from children
 | 
					 | 
				
			||||||
		struct wlr_xwayland_surface *xsurface =
 | 
					 | 
				
			||||||
			roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
		struct wlr_xwayland_surface *fullscreen_xsurface =
 | 
					 | 
				
			||||||
			roots_xwayland_surface_from_view(output->fullscreen_view)->xwayland_surface;
 | 
					 | 
				
			||||||
		while (xsurface != NULL) {
 | 
					 | 
				
			||||||
			if (fullscreen_xsurface == xsurface) {
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			xsurface = xsurface->parent;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void damage_surface_iterator(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, struct wlr_box *_box, float rotation,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	bool *whole = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box = *_box;
 | 
					 | 
				
			||||||
	scale_box(&box, output->wlr_output->scale);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int center_x = box.x + box.width/2;
 | 
					 | 
				
			||||||
	int center_y = box.y + box.height/2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (pixman_region32_not_empty(&surface->buffer_damage)) {
 | 
					 | 
				
			||||||
		pixman_region32_t damage;
 | 
					 | 
				
			||||||
		pixman_region32_init(&damage);
 | 
					 | 
				
			||||||
		wlr_surface_get_effective_damage(surface, &damage);
 | 
					 | 
				
			||||||
		wlr_region_scale(&damage, &damage, output->wlr_output->scale);
 | 
					 | 
				
			||||||
		if (ceil(output->wlr_output->scale) > surface->current.scale) {
 | 
					 | 
				
			||||||
			// When scaling up a surface, it'll become blurry so we need to
 | 
					 | 
				
			||||||
			// expand the damage region
 | 
					 | 
				
			||||||
			wlr_region_expand(&damage, &damage,
 | 
					 | 
				
			||||||
				ceil(output->wlr_output->scale) - surface->current.scale);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		pixman_region32_translate(&damage, box.x, box.y);
 | 
					 | 
				
			||||||
		wlr_region_rotated_bounds(&damage, &damage, rotation,
 | 
					 | 
				
			||||||
			center_x, center_y);
 | 
					 | 
				
			||||||
		wlr_output_damage_add(output->damage, &damage);
 | 
					 | 
				
			||||||
		pixman_region32_fini(&damage);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (*whole) {
 | 
					 | 
				
			||||||
		wlr_box_rotated_bounds(&box, &box, rotation);
 | 
					 | 
				
			||||||
		wlr_output_damage_add_box(output->damage, &box);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_output_schedule_frame(output->wlr_output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_damage_whole_local_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, double ox, double oy) {
 | 
					 | 
				
			||||||
	bool whole = true;
 | 
					 | 
				
			||||||
	output_surface_for_each_surface(output, surface, ox, oy,
 | 
					 | 
				
			||||||
		damage_surface_iterator, &whole);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void damage_whole_decoration(struct roots_view *view,
 | 
					 | 
				
			||||||
		struct roots_output *output) {
 | 
					 | 
				
			||||||
	if (!view->decorated || view->wlr_surface == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	get_decoration_box(view, output, &box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_box_rotated_bounds(&box, &box, view->rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_output_damage_add_box(output->damage, &box);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_damage_whole_view(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct roots_view *view) {
 | 
					 | 
				
			||||||
	if (!view_accept_damage(output, view)) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	damage_whole_decoration(view, output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool whole = true;
 | 
					 | 
				
			||||||
	output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_damage_whole_drag_icon(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct roots_drag_icon *icon) {
 | 
					 | 
				
			||||||
	bool whole = true;
 | 
					 | 
				
			||||||
	output_surface_for_each_surface(output, icon->wlr_drag_icon->surface,
 | 
					 | 
				
			||||||
		icon->x, icon->y, damage_surface_iterator, &whole);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_damage_from_local_surface(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, double ox, double oy) {
 | 
					 | 
				
			||||||
	bool whole = false;
 | 
					 | 
				
			||||||
	output_surface_for_each_surface(output, surface, ox, oy,
 | 
					 | 
				
			||||||
		damage_surface_iterator, &whole);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_damage_from_view(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct roots_view *view) {
 | 
					 | 
				
			||||||
	if (!view_accept_damage(output, view)) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool whole = false;
 | 
					 | 
				
			||||||
	output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void set_mode(struct wlr_output *output,
 | 
					 | 
				
			||||||
		struct roots_output_config *oc) {
 | 
					 | 
				
			||||||
	int mhz = (int)(oc->mode.refresh_rate * 1000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (wl_list_empty(&output->modes)) {
 | 
					 | 
				
			||||||
		// Output has no mode, try setting a custom one
 | 
					 | 
				
			||||||
		wlr_output_set_custom_mode(output, oc->mode.width, oc->mode.height, mhz);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output_mode *mode, *best = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(mode, &output->modes, link) {
 | 
					 | 
				
			||||||
		if (mode->width == oc->mode.width && mode->height == oc->mode.height) {
 | 
					 | 
				
			||||||
			if (mode->refresh == mhz) {
 | 
					 | 
				
			||||||
				best = mode;
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			best = mode;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (!best) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Configured mode for %s not available", output->name);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Assigning configured mode to %s", output->name);
 | 
					 | 
				
			||||||
		wlr_output_set_mode(output, best);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void update_output_manager_config(struct roots_desktop *desktop) {
 | 
					 | 
				
			||||||
	struct wlr_output_configuration_v1 *config =
 | 
					 | 
				
			||||||
		wlr_output_configuration_v1_create();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_output *output;
 | 
					 | 
				
			||||||
	wl_list_for_each(output, &desktop->outputs, link) {
 | 
					 | 
				
			||||||
		struct wlr_output_configuration_head_v1 *config_head =
 | 
					 | 
				
			||||||
			wlr_output_configuration_head_v1_create(config, output->wlr_output);
 | 
					 | 
				
			||||||
		struct wlr_box *output_box = wlr_output_layout_get_box(
 | 
					 | 
				
			||||||
			output->desktop->layout, output->wlr_output);
 | 
					 | 
				
			||||||
		if (output_box) {
 | 
					 | 
				
			||||||
			config_head->state.x = output_box->x;
 | 
					 | 
				
			||||||
			config_head->state.y = output_box->y;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_output_manager_v1_set_configuration(desktop->output_manager_v1, config);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_output_manager_apply(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, output_manager_apply);
 | 
					 | 
				
			||||||
	struct wlr_output_configuration_v1 *config = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool ok = true;
 | 
					 | 
				
			||||||
	struct wlr_output_configuration_head_v1 *config_head;
 | 
					 | 
				
			||||||
	// First disable outputs we need to disable
 | 
					 | 
				
			||||||
	wl_list_for_each(config_head, &config->heads, link) {
 | 
					 | 
				
			||||||
		struct wlr_output *wlr_output = config_head->state.output;
 | 
					 | 
				
			||||||
		if (!config_head->state.enabled) {
 | 
					 | 
				
			||||||
			ok &= wlr_output_enable(wlr_output, false);
 | 
					 | 
				
			||||||
			wlr_output_layout_remove(desktop->layout, wlr_output);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Then enable outputs that need to
 | 
					 | 
				
			||||||
	wl_list_for_each(config_head, &config->heads, link) {
 | 
					 | 
				
			||||||
		struct wlr_output *wlr_output = config_head->state.output;
 | 
					 | 
				
			||||||
		if (!config_head->state.enabled) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		ok &= wlr_output_enable(wlr_output, true);
 | 
					 | 
				
			||||||
		if (config_head->state.mode != NULL) {
 | 
					 | 
				
			||||||
			ok &= wlr_output_set_mode(wlr_output, config_head->state.mode);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			ok &= wlr_output_set_custom_mode(wlr_output,
 | 
					 | 
				
			||||||
				config_head->state.custom_mode.width,
 | 
					 | 
				
			||||||
				config_head->state.custom_mode.height,
 | 
					 | 
				
			||||||
				config_head->state.custom_mode.refresh);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		wlr_output_layout_add(desktop->layout, wlr_output,
 | 
					 | 
				
			||||||
			config_head->state.x, config_head->state.y);
 | 
					 | 
				
			||||||
		wlr_output_set_transform(wlr_output, config_head->state.transform);
 | 
					 | 
				
			||||||
		wlr_output_set_scale(wlr_output, config_head->state.scale);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ok) {
 | 
					 | 
				
			||||||
		wlr_output_configuration_v1_send_succeeded(config);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		wlr_output_configuration_v1_send_failed(config);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_output_configuration_v1_destroy(config);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	update_output_manager_config(desktop);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_output_manager_test(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, output_manager_test);
 | 
					 | 
				
			||||||
	struct wlr_output_configuration_v1 *config = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO: implement test-only mode
 | 
					 | 
				
			||||||
	wlr_output_configuration_v1_send_succeeded(config);
 | 
					 | 
				
			||||||
	wlr_output_configuration_v1_destroy(config);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_destroy(struct roots_output *output) {
 | 
					 | 
				
			||||||
	// TODO: cursor
 | 
					 | 
				
			||||||
	//example_config_configure_cursor(sample->config, sample->cursor,
 | 
					 | 
				
			||||||
	//	sample->compositor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_remove(&output->link);
 | 
					 | 
				
			||||||
	wl_list_remove(&output->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&output->enable.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&output->mode.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&output->transform.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&output->present.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&output->damage_frame.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&output->damage_destroy.link);
 | 
					 | 
				
			||||||
	free(output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_handle_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_output *output = wl_container_of(listener, output, destroy);
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = output->desktop;
 | 
					 | 
				
			||||||
	output_destroy(output);
 | 
					 | 
				
			||||||
	update_output_manager_config(desktop);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_handle_enable(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_output *output = wl_container_of(listener, output, enable);
 | 
					 | 
				
			||||||
	update_output_manager_config(output->desktop);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_damage_handle_frame(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_output *output =
 | 
					 | 
				
			||||||
		wl_container_of(listener, output, damage_frame);
 | 
					 | 
				
			||||||
	output_render(output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_damage_handle_destroy(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_output *output =
 | 
					 | 
				
			||||||
		wl_container_of(listener, output, damage_destroy);
 | 
					 | 
				
			||||||
	output_destroy(output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_handle_mode(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_output *output =
 | 
					 | 
				
			||||||
		wl_container_of(listener, output, mode);
 | 
					 | 
				
			||||||
	arrange_layers(output);
 | 
					 | 
				
			||||||
	update_output_manager_config(output->desktop);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_handle_transform(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_output *output =
 | 
					 | 
				
			||||||
		wl_container_of(listener, output, transform);
 | 
					 | 
				
			||||||
	arrange_layers(output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void surface_send_presented_iterator(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, struct wlr_box *_box, float rotation,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct wlr_presentation_event *event = data;
 | 
					 | 
				
			||||||
	wlr_presentation_send_surface_presented(output->desktop->presentation,
 | 
					 | 
				
			||||||
		surface, event);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void output_handle_present(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_output *output =
 | 
					 | 
				
			||||||
		wl_container_of(listener, output, present);
 | 
					 | 
				
			||||||
	struct wlr_output_event_present *output_event = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_presentation_event event = {
 | 
					 | 
				
			||||||
		.output = output->wlr_output,
 | 
					 | 
				
			||||||
		.tv_sec = (uint64_t)output_event->when->tv_sec,
 | 
					 | 
				
			||||||
		.tv_nsec = (uint32_t)output_event->when->tv_nsec,
 | 
					 | 
				
			||||||
		.refresh = (uint32_t)output_event->refresh,
 | 
					 | 
				
			||||||
		.seq = (uint64_t)output_event->seq,
 | 
					 | 
				
			||||||
		.flags = output_event->flags,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	output_for_each_surface(output,
 | 
					 | 
				
			||||||
		surface_send_presented_iterator, &event);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_new_output(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = wl_container_of(listener, desktop,
 | 
					 | 
				
			||||||
		new_output);
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = data;
 | 
					 | 
				
			||||||
	struct roots_input *input = desktop->server->input;
 | 
					 | 
				
			||||||
	struct roots_config *config = desktop->config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "Output '%s' added", wlr_output->name);
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "'%s %s %s' %"PRId32"mm x %"PRId32"mm", wlr_output->make,
 | 
					 | 
				
			||||||
		wlr_output->model, wlr_output->serial, wlr_output->phys_width,
 | 
					 | 
				
			||||||
		wlr_output->phys_height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_output *output = calloc(1, sizeof(struct roots_output));
 | 
					 | 
				
			||||||
	clock_gettime(CLOCK_MONOTONIC, &output->last_frame);
 | 
					 | 
				
			||||||
	output->desktop = desktop;
 | 
					 | 
				
			||||||
	output->wlr_output = wlr_output;
 | 
					 | 
				
			||||||
	wlr_output->data = output;
 | 
					 | 
				
			||||||
	wl_list_insert(&desktop->outputs, &output->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	output->damage = wlr_output_damage_create(wlr_output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	output->destroy.notify = output_handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_output->events.destroy, &output->destroy);
 | 
					 | 
				
			||||||
	output->enable.notify = output_handle_enable;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_output->events.enable, &output->enable);
 | 
					 | 
				
			||||||
	output->mode.notify = output_handle_mode;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_output->events.mode, &output->mode);
 | 
					 | 
				
			||||||
	output->transform.notify = output_handle_transform;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_output->events.transform, &output->transform);
 | 
					 | 
				
			||||||
	output->present.notify = output_handle_present;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_output->events.present, &output->present);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	output->damage_frame.notify = output_damage_handle_frame;
 | 
					 | 
				
			||||||
	wl_signal_add(&output->damage->events.frame, &output->damage_frame);
 | 
					 | 
				
			||||||
	output->damage_destroy.notify = output_damage_handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
 | 
					 | 
				
			||||||
	for (size_t i = 0; i < len; ++i) {
 | 
					 | 
				
			||||||
		wl_list_init(&output->layers[i]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_output_config *output_config =
 | 
					 | 
				
			||||||
		roots_config_get_output(config, wlr_output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output_mode *preferred_mode =
 | 
					 | 
				
			||||||
		wlr_output_preferred_mode(wlr_output);
 | 
					 | 
				
			||||||
	if (output_config) {
 | 
					 | 
				
			||||||
		if (output_config->enable) {
 | 
					 | 
				
			||||||
			if (wlr_output_is_drm(wlr_output)) {
 | 
					 | 
				
			||||||
				struct roots_output_mode_config *mode_config;
 | 
					 | 
				
			||||||
				wl_list_for_each(mode_config, &output_config->modes, link) {
 | 
					 | 
				
			||||||
					wlr_drm_connector_add_mode(wlr_output, &mode_config->info);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else if (!wl_list_empty(&output_config->modes)) {
 | 
					 | 
				
			||||||
				wlr_log(WLR_ERROR, "Can only add modes for DRM backend");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (output_config->mode.width) {
 | 
					 | 
				
			||||||
				set_mode(wlr_output, output_config);
 | 
					 | 
				
			||||||
			} else if (preferred_mode != NULL) {
 | 
					 | 
				
			||||||
				wlr_output_set_mode(wlr_output, preferred_mode);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			wlr_output_set_scale(wlr_output, output_config->scale);
 | 
					 | 
				
			||||||
			wlr_output_set_transform(wlr_output, output_config->transform);
 | 
					 | 
				
			||||||
			wlr_output_layout_add(desktop->layout, wlr_output, output_config->x,
 | 
					 | 
				
			||||||
				output_config->y);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			wlr_output_enable(wlr_output, false);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if (preferred_mode != NULL) {
 | 
					 | 
				
			||||||
			wlr_output_set_mode(wlr_output, preferred_mode);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		wlr_output_layout_add_auto(desktop->layout, wlr_output);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		roots_seat_configure_cursor(seat);
 | 
					 | 
				
			||||||
		roots_seat_configure_xcursor(seat);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	arrange_layers(output);
 | 
					 | 
				
			||||||
	output_damage_whole(output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	update_output_manager_config(desktop);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,422 +0,0 @@
 | 
				
			||||||
#define _POSIX_C_SOURCE 200809L
 | 
					 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_compositor.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_matrix.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_buffer.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_linux_dmabuf_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include <wlr/util/region.h>
 | 
					 | 
				
			||||||
#include "rootston/layers.h"
 | 
					 | 
				
			||||||
#include "rootston/output.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct render_data {
 | 
					 | 
				
			||||||
	pixman_region32_t *damage;
 | 
					 | 
				
			||||||
	float alpha;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void scissor_output(struct wlr_output *wlr_output,
 | 
					 | 
				
			||||||
		pixman_box32_t *rect) {
 | 
					 | 
				
			||||||
	struct wlr_renderer *renderer =
 | 
					 | 
				
			||||||
		wlr_backend_get_renderer(wlr_output->backend);
 | 
					 | 
				
			||||||
	assert(renderer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box = {
 | 
					 | 
				
			||||||
		.x = rect->x1,
 | 
					 | 
				
			||||||
		.y = rect->y1,
 | 
					 | 
				
			||||||
		.width = rect->x2 - rect->x1,
 | 
					 | 
				
			||||||
		.height = rect->y2 - rect->y1,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int ow, oh;
 | 
					 | 
				
			||||||
	wlr_output_transformed_resolution(wlr_output, &ow, &oh);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum wl_output_transform transform =
 | 
					 | 
				
			||||||
		wlr_output_transform_invert(wlr_output->transform);
 | 
					 | 
				
			||||||
	wlr_box_transform(&box, &box, transform, ow, oh);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_renderer_scissor(renderer, &box);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void render_texture(struct wlr_output *wlr_output,
 | 
					 | 
				
			||||||
		pixman_region32_t *output_damage, struct wlr_texture *texture,
 | 
					 | 
				
			||||||
		const struct wlr_box *box, const float matrix[static 9],
 | 
					 | 
				
			||||||
		float rotation, float alpha) {
 | 
					 | 
				
			||||||
	struct wlr_renderer *renderer =
 | 
					 | 
				
			||||||
		wlr_backend_get_renderer(wlr_output->backend);
 | 
					 | 
				
			||||||
	assert(renderer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box rotated;
 | 
					 | 
				
			||||||
	wlr_box_rotated_bounds(&rotated, box, rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pixman_region32_t damage;
 | 
					 | 
				
			||||||
	pixman_region32_init(&damage);
 | 
					 | 
				
			||||||
	pixman_region32_union_rect(&damage, &damage, rotated.x, rotated.y,
 | 
					 | 
				
			||||||
		rotated.width, rotated.height);
 | 
					 | 
				
			||||||
	pixman_region32_intersect(&damage, &damage, output_damage);
 | 
					 | 
				
			||||||
	bool damaged = pixman_region32_not_empty(&damage);
 | 
					 | 
				
			||||||
	if (!damaged) {
 | 
					 | 
				
			||||||
		goto buffer_damage_finish;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int nrects;
 | 
					 | 
				
			||||||
	pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
 | 
					 | 
				
			||||||
	for (int i = 0; i < nrects; ++i) {
 | 
					 | 
				
			||||||
		scissor_output(wlr_output, &rects[i]);
 | 
					 | 
				
			||||||
		wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buffer_damage_finish:
 | 
					 | 
				
			||||||
	pixman_region32_fini(&damage);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void render_surface_iterator(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, struct wlr_box *_box, float rotation,
 | 
					 | 
				
			||||||
		void *_data) {
 | 
					 | 
				
			||||||
	struct render_data *data = _data;
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = output->wlr_output;
 | 
					 | 
				
			||||||
	pixman_region32_t *output_damage = data->damage;
 | 
					 | 
				
			||||||
	float alpha = data->alpha;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_texture *texture = wlr_surface_get_texture(surface);
 | 
					 | 
				
			||||||
	if (!texture) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box = *_box;
 | 
					 | 
				
			||||||
	scale_box(&box, wlr_output->scale);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	float matrix[9];
 | 
					 | 
				
			||||||
	enum wl_output_transform transform =
 | 
					 | 
				
			||||||
		wlr_output_transform_invert(surface->current.transform);
 | 
					 | 
				
			||||||
	wlr_matrix_project_box(matrix, &box, transform, rotation,
 | 
					 | 
				
			||||||
		wlr_output->transform_matrix);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	render_texture(wlr_output, output_damage,
 | 
					 | 
				
			||||||
		texture, &box, matrix, rotation, alpha);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void render_decorations(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct roots_view *view, struct render_data *data) {
 | 
					 | 
				
			||||||
	if (!view->decorated || view->wlr_surface == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_renderer *renderer =
 | 
					 | 
				
			||||||
		wlr_backend_get_renderer(output->wlr_output->backend);
 | 
					 | 
				
			||||||
	assert(renderer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	get_decoration_box(view, output, &box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box rotated;
 | 
					 | 
				
			||||||
	wlr_box_rotated_bounds(&rotated, &box, view->rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pixman_region32_t damage;
 | 
					 | 
				
			||||||
	pixman_region32_init(&damage);
 | 
					 | 
				
			||||||
	pixman_region32_union_rect(&damage, &damage, rotated.x, rotated.y,
 | 
					 | 
				
			||||||
		rotated.width, rotated.height);
 | 
					 | 
				
			||||||
	pixman_region32_intersect(&damage, &damage, data->damage);
 | 
					 | 
				
			||||||
	bool damaged = pixman_region32_not_empty(&damage);
 | 
					 | 
				
			||||||
	if (!damaged) {
 | 
					 | 
				
			||||||
		goto buffer_damage_finish;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	float matrix[9];
 | 
					 | 
				
			||||||
	wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL,
 | 
					 | 
				
			||||||
		view->rotation, output->wlr_output->transform_matrix);
 | 
					 | 
				
			||||||
	float color[] = { 0.2, 0.2, 0.2, view->alpha };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int nrects;
 | 
					 | 
				
			||||||
	pixman_box32_t *rects =
 | 
					 | 
				
			||||||
		pixman_region32_rectangles(&damage, &nrects);
 | 
					 | 
				
			||||||
	for (int i = 0; i < nrects; ++i) {
 | 
					 | 
				
			||||||
		scissor_output(output->wlr_output, &rects[i]);
 | 
					 | 
				
			||||||
		wlr_render_quad_with_matrix(renderer, color, matrix);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buffer_damage_finish:
 | 
					 | 
				
			||||||
	pixman_region32_fini(&damage);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void render_view(struct roots_output *output, struct roots_view *view,
 | 
					 | 
				
			||||||
		struct render_data *data) {
 | 
					 | 
				
			||||||
	// Do not render views fullscreened on other outputs
 | 
					 | 
				
			||||||
	if (view->fullscreen_output != NULL && view->fullscreen_output != output) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	data->alpha = view->alpha;
 | 
					 | 
				
			||||||
	if (view->fullscreen_output == NULL) {
 | 
					 | 
				
			||||||
		render_decorations(output, view, data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	output_view_for_each_surface(output, view, render_surface_iterator, data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void render_layer(struct roots_output *output,
 | 
					 | 
				
			||||||
		pixman_region32_t *damage, struct wl_list *layer_surfaces) {
 | 
					 | 
				
			||||||
	struct render_data data = {
 | 
					 | 
				
			||||||
		.damage = damage,
 | 
					 | 
				
			||||||
		.alpha = 1.0f,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	output_layer_for_each_surface(output, layer_surfaces,
 | 
					 | 
				
			||||||
		render_surface_iterator, &data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void render_drag_icons(struct roots_output *output,
 | 
					 | 
				
			||||||
		pixman_region32_t *damage, struct roots_input *input) {
 | 
					 | 
				
			||||||
	struct render_data data = {
 | 
					 | 
				
			||||||
		.damage = damage,
 | 
					 | 
				
			||||||
		.alpha = 1.0f,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	output_drag_icons_for_each_surface(output, input,
 | 
					 | 
				
			||||||
		render_surface_iterator, &data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void count_surface_iterator(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, struct wlr_box *_box, float rotation,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	size_t *n = data;
 | 
					 | 
				
			||||||
	n++;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool scan_out_fullscreen_view(struct roots_output *output) {
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = output->wlr_output;
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = output->desktop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &desktop->server->input->seats, link) {
 | 
					 | 
				
			||||||
		struct roots_drag_icon *drag_icon = seat->drag_icon;
 | 
					 | 
				
			||||||
		if (drag_icon && drag_icon->wlr_drag_icon->mapped) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output_cursor *cursor;
 | 
					 | 
				
			||||||
	wl_list_for_each(cursor, &wlr_output->cursors, link) {
 | 
					 | 
				
			||||||
		if (cursor->enabled && cursor->visible &&
 | 
					 | 
				
			||||||
				wlr_output->hardware_cursor != cursor) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *view = output->fullscreen_view;
 | 
					 | 
				
			||||||
	assert(view != NULL);
 | 
					 | 
				
			||||||
	if (view->wlr_surface == NULL) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	size_t n_surfaces = 0;
 | 
					 | 
				
			||||||
	output_view_for_each_surface(output, view,
 | 
					 | 
				
			||||||
		count_surface_iterator, &n_surfaces);
 | 
					 | 
				
			||||||
	if (n_surfaces > 1) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
	if (view->type == ROOTS_XWAYLAND_VIEW) {
 | 
					 | 
				
			||||||
		struct roots_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
			roots_xwayland_surface_from_view(view);
 | 
					 | 
				
			||||||
		if (!wl_list_empty(&xwayland_surface->xwayland_surface->children)) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = view->wlr_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface->buffer == NULL) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((float)surface->current.scale != wlr_output->scale ||
 | 
					 | 
				
			||||||
			surface->current.transform != wlr_output->transform) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!wlr_output_attach_buffer(wlr_output, surface->buffer)) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return wlr_output_commit(wlr_output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void surface_send_frame_done_iterator(struct roots_output *output,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface, struct wlr_box *box, float rotation,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct timespec *when = data;
 | 
					 | 
				
			||||||
	wlr_surface_send_frame_done(surface, when);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void output_render(struct roots_output *output) {
 | 
					 | 
				
			||||||
	struct wlr_output *wlr_output = output->wlr_output;
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = output->desktop;
 | 
					 | 
				
			||||||
	struct roots_server *server = desktop->server;
 | 
					 | 
				
			||||||
	struct wlr_renderer *renderer =
 | 
					 | 
				
			||||||
		wlr_backend_get_renderer(wlr_output->backend);
 | 
					 | 
				
			||||||
	assert(renderer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!wlr_output->enabled) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct timespec now;
 | 
					 | 
				
			||||||
	clock_gettime(CLOCK_MONOTONIC, &now);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const struct wlr_box *output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(desktop->layout, wlr_output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we can delegate the fullscreen surface to the output
 | 
					 | 
				
			||||||
	if (output->fullscreen_view != NULL &&
 | 
					 | 
				
			||||||
			output->fullscreen_view->wlr_surface != NULL) {
 | 
					 | 
				
			||||||
		struct roots_view *view = output->fullscreen_view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Make sure the view is centered on screen
 | 
					 | 
				
			||||||
		struct wlr_box view_box;
 | 
					 | 
				
			||||||
		view_get_box(view, &view_box);
 | 
					 | 
				
			||||||
		double view_x = (double)(output_box->width - view_box.width) / 2 +
 | 
					 | 
				
			||||||
			output_box->x;
 | 
					 | 
				
			||||||
		double view_y = (double)(output_box->height - view_box.height) / 2 +
 | 
					 | 
				
			||||||
			output_box->y;
 | 
					 | 
				
			||||||
		view_move(view, view_x, view_y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Fullscreen views are rendered on a black background
 | 
					 | 
				
			||||||
		clear_color[0] = clear_color[1] = clear_color[2] = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Check if we can scan-out the fullscreen view
 | 
					 | 
				
			||||||
		static bool last_scanned_out = false;
 | 
					 | 
				
			||||||
		bool scanned_out = scan_out_fullscreen_view(output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (scanned_out && !last_scanned_out) {
 | 
					 | 
				
			||||||
			wlr_log(WLR_DEBUG, "Scanning out fullscreen view");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (last_scanned_out && !scanned_out) {
 | 
					 | 
				
			||||||
			wlr_log(WLR_DEBUG, "Stopping fullscreen view scan out");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		last_scanned_out = scanned_out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (scanned_out) {
 | 
					 | 
				
			||||||
			goto send_frame_done;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool needs_frame;
 | 
					 | 
				
			||||||
	pixman_region32_t buffer_damage;
 | 
					 | 
				
			||||||
	pixman_region32_init(&buffer_damage);
 | 
					 | 
				
			||||||
	if (!wlr_output_damage_attach_render(output->damage, &needs_frame,
 | 
					 | 
				
			||||||
			&buffer_damage)) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct render_data data = {
 | 
					 | 
				
			||||||
		.damage = &buffer_damage,
 | 
					 | 
				
			||||||
		.alpha = 1.0,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!needs_frame) {
 | 
					 | 
				
			||||||
		// Output doesn't need swap and isn't damaged, skip rendering completely
 | 
					 | 
				
			||||||
		goto buffer_damage_finish;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!pixman_region32_not_empty(&buffer_damage)) {
 | 
					 | 
				
			||||||
		// Output isn't damaged but needs buffer swap
 | 
					 | 
				
			||||||
		goto renderer_end;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (server->config->debug_damage_tracking) {
 | 
					 | 
				
			||||||
		wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int nrects;
 | 
					 | 
				
			||||||
	pixman_box32_t *rects = pixman_region32_rectangles(&buffer_damage, &nrects);
 | 
					 | 
				
			||||||
	for (int i = 0; i < nrects; ++i) {
 | 
					 | 
				
			||||||
		scissor_output(output->wlr_output, &rects[i]);
 | 
					 | 
				
			||||||
		wlr_renderer_clear(renderer, clear_color);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// If a view is fullscreen on this output, render it
 | 
					 | 
				
			||||||
	if (output->fullscreen_view != NULL) {
 | 
					 | 
				
			||||||
		struct roots_view *view = output->fullscreen_view;
 | 
					 | 
				
			||||||
		render_view(output, view, &data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// During normal rendering the xwayland window tree isn't traversed
 | 
					 | 
				
			||||||
		// because all windows are rendered. Here we only want to render
 | 
					 | 
				
			||||||
		// the fullscreen window's children so we have to traverse the tree.
 | 
					 | 
				
			||||||
#if WLR_HAS_XWAYLAND
 | 
					 | 
				
			||||||
		if (view->type == ROOTS_XWAYLAND_VIEW) {
 | 
					 | 
				
			||||||
			struct roots_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
				roots_xwayland_surface_from_view(view);
 | 
					 | 
				
			||||||
			output_xwayland_children_for_each_surface(output,
 | 
					 | 
				
			||||||
				xwayland_surface->xwayland_surface,
 | 
					 | 
				
			||||||
				render_surface_iterator, &data);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		// Render background and bottom layers under views
 | 
					 | 
				
			||||||
		render_layer(output, &buffer_damage,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
 | 
					 | 
				
			||||||
		render_layer(output, &buffer_damage,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Render all views
 | 
					 | 
				
			||||||
		struct roots_view *view;
 | 
					 | 
				
			||||||
		wl_list_for_each_reverse(view, &desktop->views, link) {
 | 
					 | 
				
			||||||
			render_view(output, view, &data);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Render top layer above views
 | 
					 | 
				
			||||||
		render_layer(output, &buffer_damage,
 | 
					 | 
				
			||||||
			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	render_drag_icons(output, &buffer_damage, server->input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	render_layer(output, &buffer_damage,
 | 
					 | 
				
			||||||
		&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
renderer_end:
 | 
					 | 
				
			||||||
	wlr_output_render_software_cursors(wlr_output, &buffer_damage);
 | 
					 | 
				
			||||||
	wlr_renderer_scissor(renderer, NULL);
 | 
					 | 
				
			||||||
	wlr_renderer_end(renderer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int width, height;
 | 
					 | 
				
			||||||
	wlr_output_transformed_resolution(wlr_output, &width, &height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pixman_region32_t frame_damage;
 | 
					 | 
				
			||||||
	pixman_region32_init(&frame_damage);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum wl_output_transform transform =
 | 
					 | 
				
			||||||
		wlr_output_transform_invert(wlr_output->transform);
 | 
					 | 
				
			||||||
	wlr_region_transform(&frame_damage, &output->damage->current,
 | 
					 | 
				
			||||||
		transform, width, height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (server->config->debug_damage_tracking) {
 | 
					 | 
				
			||||||
		pixman_region32_union_rect(&frame_damage, &frame_damage,
 | 
					 | 
				
			||||||
			0, 0, wlr_output->width, wlr_output->height);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_output_set_damage(wlr_output, &frame_damage);
 | 
					 | 
				
			||||||
	pixman_region32_fini(&frame_damage);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!wlr_output_commit(wlr_output)) {
 | 
					 | 
				
			||||||
		goto buffer_damage_finish;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	output->last_frame = desktop->last_frame = now;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buffer_damage_finish:
 | 
					 | 
				
			||||||
	pixman_region32_fini(&buffer_damage);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
send_frame_done:
 | 
					 | 
				
			||||||
	// Send frame done events to all surfaces
 | 
					 | 
				
			||||||
	output_for_each_surface(output, surface_send_frame_done_iterator, &now);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,63 +0,0 @@
 | 
				
			||||||
[core]
 | 
					 | 
				
			||||||
# X11 support
 | 
					 | 
				
			||||||
#  - true: enables X11, xwayland is started only when an X11 client connects
 | 
					 | 
				
			||||||
#  - immediate: enables X11, xwayland is started immediately
 | 
					 | 
				
			||||||
#  - false: disables xwayland
 | 
					 | 
				
			||||||
xwayland=false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Single output configuration. String after colon must match output's name.
 | 
					 | 
				
			||||||
[output:VGA-1]
 | 
					 | 
				
			||||||
# Set logical (layout) coordinates for this screen
 | 
					 | 
				
			||||||
x = 1920
 | 
					 | 
				
			||||||
y = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Screen transformation
 | 
					 | 
				
			||||||
# possible values are:
 | 
					 | 
				
			||||||
# '90', '180' or '270' - rotate output by specified angle clockwise
 | 
					 | 
				
			||||||
# 'flipped' - flip output horizontally
 | 
					 | 
				
			||||||
# 'flipped-90', 'flipped-180', 'flipped-270' - flip output horizontally
 | 
					 | 
				
			||||||
#                                              and rotate by specified angle
 | 
					 | 
				
			||||||
rotate = 90
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Additional video mode to add
 | 
					 | 
				
			||||||
# Format is generated by cvt and is documented in x.org.conf(5)
 | 
					 | 
				
			||||||
modeline = 87.25 720 776 848  976 1440 1443 1453 1493 -hsync +vsync
 | 
					 | 
				
			||||||
modeline = 65.13 768 816 896 1024 1024 1025 1028 1060 -HSync +VSync
 | 
					 | 
				
			||||||
# Select one of the above modes
 | 
					 | 
				
			||||||
mode = 768x1024
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[cursor]
 | 
					 | 
				
			||||||
# Restrict cursor movements to single output
 | 
					 | 
				
			||||||
map-to-output = VGA-1
 | 
					 | 
				
			||||||
# Restrict cursor movements to concrete rectangle
 | 
					 | 
				
			||||||
geometry = 2500x800
 | 
					 | 
				
			||||||
# Load a custom XCursor theme
 | 
					 | 
				
			||||||
theme = default
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Single device configuration. String after colon must match device's name.
 | 
					 | 
				
			||||||
[device:PixArt Dell MS116 USB Optical Mouse]
 | 
					 | 
				
			||||||
# Restrict cursor movements for this mouse to single output
 | 
					 | 
				
			||||||
map-to-output = VGA-1
 | 
					 | 
				
			||||||
# Restrict cursor movements for this mouse to concrete rectangle
 | 
					 | 
				
			||||||
geometry = 2500x800
 | 
					 | 
				
			||||||
# tap_enabled=true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[keyboard]
 | 
					 | 
				
			||||||
meta-key = Logo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Keybindings
 | 
					 | 
				
			||||||
# Maps key combinations with commands to execute
 | 
					 | 
				
			||||||
# Commands include:
 | 
					 | 
				
			||||||
# - "exit" to stop the compositor
 | 
					 | 
				
			||||||
# - "exec" to execute a shell command
 | 
					 | 
				
			||||||
# - "close" to close the current view
 | 
					 | 
				
			||||||
# - "next_window" to cycle through windows
 | 
					 | 
				
			||||||
# - "alpha" to cycle a window's alpha channel
 | 
					 | 
				
			||||||
# - "break_pointer_constraint" to decline and deactivate all pointer constraints
 | 
					 | 
				
			||||||
[bindings]
 | 
					 | 
				
			||||||
Logo+Shift+e = exit
 | 
					 | 
				
			||||||
Logo+q = close
 | 
					 | 
				
			||||||
Logo+m = maximize
 | 
					 | 
				
			||||||
Logo+Escape = break_pointer_constraint
 | 
					 | 
				
			||||||
Alt+Tab = next_window
 | 
					 | 
				
			||||||
Ctrl+Shift+a = alpha
 | 
					 | 
				
			||||||
							
								
								
									
										1642
									
								
								rootston/seat.c
									
										
									
									
									
								
							
							
						
						
									
										1642
									
								
								rootston/seat.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,22 +0,0 @@
 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/bindings.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_switch_handle_toggle(struct roots_switch *switch_device,
 | 
					 | 
				
			||||||
		struct wlr_event_switch_toggle *event) {
 | 
					 | 
				
			||||||
	struct wl_list *bound_switches =
 | 
					 | 
				
			||||||
		&switch_device->seat->input->server->config->switches;
 | 
					 | 
				
			||||||
	struct roots_switch_config *sc;
 | 
					 | 
				
			||||||
	wl_list_for_each(sc, bound_switches, link) {
 | 
					 | 
				
			||||||
		if ((sc->name != NULL && strcmp(event->device->name, sc->name) != 0) &&
 | 
					 | 
				
			||||||
				(sc->name == NULL && event->switch_type != sc->switch_type)) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (sc->switch_state != WLR_SWITCH_STATE_TOGGLE &&
 | 
					 | 
				
			||||||
				event->switch_state != sc->switch_state) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		execute_binding_command(switch_device->seat,
 | 
					 | 
				
			||||||
			switch_device->seat->input, sc->command);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,312 +0,0 @@
 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
#include "rootston/text_input.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_text_input *relay_get_focusable_text_input(
 | 
					 | 
				
			||||||
		struct roots_input_method_relay *relay) {
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(text_input, &relay->text_inputs, link) {
 | 
					 | 
				
			||||||
		if (text_input->pending_focused_surface) {
 | 
					 | 
				
			||||||
			return text_input;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_text_input *relay_get_focused_text_input(
 | 
					 | 
				
			||||||
		struct roots_input_method_relay *relay) {
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(text_input, &relay->text_inputs, link) {
 | 
					 | 
				
			||||||
		if (text_input->input->focused_surface) {
 | 
					 | 
				
			||||||
			return text_input;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_im_commit(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		input_method_commit);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = relay_get_focused_text_input(relay);
 | 
					 | 
				
			||||||
	if (!text_input) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	struct wlr_input_method_v2 *context = data;
 | 
					 | 
				
			||||||
	assert(context == relay->input_method);
 | 
					 | 
				
			||||||
	if (context->current.preedit.text) {
 | 
					 | 
				
			||||||
		wlr_text_input_v3_send_preedit_string(text_input->input,
 | 
					 | 
				
			||||||
			context->current.preedit.text,
 | 
					 | 
				
			||||||
			context->current.preedit.cursor_begin,
 | 
					 | 
				
			||||||
			context->current.preedit.cursor_end);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (context->current.commit_text) {
 | 
					 | 
				
			||||||
		wlr_text_input_v3_send_commit_string(text_input->input,
 | 
					 | 
				
			||||||
			context->current.commit_text);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (context->current.delete.before_length
 | 
					 | 
				
			||||||
			|| context->current.delete.after_length) {
 | 
					 | 
				
			||||||
		wlr_text_input_v3_send_delete_surrounding_text(text_input->input,
 | 
					 | 
				
			||||||
			context->current.delete.before_length,
 | 
					 | 
				
			||||||
			context->current.delete.after_length);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_text_input_v3_send_done(text_input->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void text_input_set_pending_focused_surface(
 | 
					 | 
				
			||||||
		struct roots_text_input *text_input, struct wlr_surface *surface) {
 | 
					 | 
				
			||||||
	text_input->pending_focused_surface = surface;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.destroy,
 | 
					 | 
				
			||||||
		&text_input->pending_focused_surface_destroy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void text_input_clear_pending_focused_surface(
 | 
					 | 
				
			||||||
		struct roots_text_input *text_input) {
 | 
					 | 
				
			||||||
	wl_list_remove(&text_input->pending_focused_surface_destroy.link);
 | 
					 | 
				
			||||||
	wl_list_init(&text_input->pending_focused_surface_destroy.link);
 | 
					 | 
				
			||||||
	text_input->pending_focused_surface = NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_im_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		input_method_destroy);
 | 
					 | 
				
			||||||
	struct wlr_input_method_v2 *context = data;
 | 
					 | 
				
			||||||
	assert(context == relay->input_method);
 | 
					 | 
				
			||||||
	relay->input_method = NULL;
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = relay_get_focused_text_input(relay);
 | 
					 | 
				
			||||||
	if (text_input) {
 | 
					 | 
				
			||||||
		// keyboard focus is still there, so keep the surface at hand in case
 | 
					 | 
				
			||||||
		// the input method returns
 | 
					 | 
				
			||||||
		text_input_set_pending_focused_surface(text_input,
 | 
					 | 
				
			||||||
			text_input->input->focused_surface);
 | 
					 | 
				
			||||||
		wlr_text_input_v3_send_leave(text_input->input);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void relay_send_im_done(struct roots_input_method_relay *relay,
 | 
					 | 
				
			||||||
		struct wlr_text_input_v3 *input) {
 | 
					 | 
				
			||||||
	struct wlr_input_method_v2 *input_method = relay->input_method;
 | 
					 | 
				
			||||||
	if (!input_method) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_INFO, "Sending IM_DONE but im is gone");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// TODO: only send each of those if they were modified
 | 
					 | 
				
			||||||
	wlr_input_method_v2_send_surrounding_text(input_method,
 | 
					 | 
				
			||||||
		input->current.surrounding.text, input->current.surrounding.cursor,
 | 
					 | 
				
			||||||
		input->current.surrounding.anchor);
 | 
					 | 
				
			||||||
	wlr_input_method_v2_send_text_change_cause(input_method,
 | 
					 | 
				
			||||||
		input->current.text_change_cause);
 | 
					 | 
				
			||||||
	wlr_input_method_v2_send_content_type(input_method,
 | 
					 | 
				
			||||||
		input->current.content_type.hint, input->current.content_type.purpose);
 | 
					 | 
				
			||||||
	wlr_input_method_v2_send_done(input_method);
 | 
					 | 
				
			||||||
	// TODO: pass intent, display popup size
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_text_input *text_input_to_roots(
 | 
					 | 
				
			||||||
		struct roots_input_method_relay *relay,
 | 
					 | 
				
			||||||
		struct wlr_text_input_v3 *text_input) {
 | 
					 | 
				
			||||||
	struct roots_text_input *roots_text_input = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(roots_text_input, &relay->text_inputs, link) {
 | 
					 | 
				
			||||||
		if (roots_text_input->input == text_input) {
 | 
					 | 
				
			||||||
			return roots_text_input;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_text_input_enable(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		text_input_enable);
 | 
					 | 
				
			||||||
	if (relay->input_method == NULL) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_INFO, "Enabling text input when input method is gone");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = text_input_to_roots(relay,
 | 
					 | 
				
			||||||
		(struct wlr_text_input_v3*)data);
 | 
					 | 
				
			||||||
	wlr_input_method_v2_send_activate(relay->input_method);
 | 
					 | 
				
			||||||
	relay_send_im_done(relay, text_input->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_text_input_commit(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		text_input_commit);
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = text_input_to_roots(relay,
 | 
					 | 
				
			||||||
		(struct wlr_text_input_v3*)data);
 | 
					 | 
				
			||||||
	if (!text_input->input->current_enabled) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_INFO, "Inactive text input tried to commit an update");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "Text input committed update");
 | 
					 | 
				
			||||||
	if (relay->input_method == NULL) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_INFO, "Text input committed, but input method is gone");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	relay_send_im_done(relay, text_input->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void relay_disable_text_input(struct roots_input_method_relay *relay,
 | 
					 | 
				
			||||||
		struct roots_text_input *text_input) {
 | 
					 | 
				
			||||||
	if (relay->input_method == NULL) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Disabling text input, but input method is gone");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_input_method_v2_send_deactivate(relay->input_method);
 | 
					 | 
				
			||||||
	relay_send_im_done(relay, text_input->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_text_input_disable(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		text_input_disable);
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = text_input_to_roots(relay,
 | 
					 | 
				
			||||||
		(struct wlr_text_input_v3*)data);
 | 
					 | 
				
			||||||
	relay_disable_text_input(relay, text_input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_text_input_destroy(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		text_input_destroy);
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = text_input_to_roots(relay,
 | 
					 | 
				
			||||||
		(struct wlr_text_input_v3*)data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (text_input->input->current_enabled) {
 | 
					 | 
				
			||||||
		relay_disable_text_input(relay, text_input);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	text_input_clear_pending_focused_surface(text_input);
 | 
					 | 
				
			||||||
	wl_list_remove(&text_input->link);
 | 
					 | 
				
			||||||
	text_input->input = NULL;
 | 
					 | 
				
			||||||
	free(text_input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_pending_focused_surface_destroy(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = wl_container_of(listener, text_input,
 | 
					 | 
				
			||||||
		pending_focused_surface_destroy);
 | 
					 | 
				
			||||||
	struct wlr_surface *surface = data;
 | 
					 | 
				
			||||||
	assert(text_input->pending_focused_surface == surface);
 | 
					 | 
				
			||||||
	text_input->pending_focused_surface = NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_text_input *roots_text_input_create(
 | 
					 | 
				
			||||||
		struct roots_input_method_relay *relay,
 | 
					 | 
				
			||||||
		struct wlr_text_input_v3 *text_input) {
 | 
					 | 
				
			||||||
	struct roots_text_input *input = calloc(1, sizeof(struct roots_text_input));
 | 
					 | 
				
			||||||
	if (!input) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	input->input = text_input;
 | 
					 | 
				
			||||||
	input->relay = relay;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_signal_add(&text_input->events.enable, &relay->text_input_enable);
 | 
					 | 
				
			||||||
	relay->text_input_enable.notify = handle_text_input_enable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_signal_add(&text_input->events.commit, &relay->text_input_commit);
 | 
					 | 
				
			||||||
	relay->text_input_commit.notify = handle_text_input_commit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_signal_add(&text_input->events.disable, &relay->text_input_disable);
 | 
					 | 
				
			||||||
	relay->text_input_disable.notify = handle_text_input_disable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_signal_add(&text_input->events.destroy, &relay->text_input_destroy);
 | 
					 | 
				
			||||||
	relay->text_input_destroy.notify = handle_text_input_destroy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	input->pending_focused_surface_destroy.notify =
 | 
					 | 
				
			||||||
		handle_pending_focused_surface_destroy;
 | 
					 | 
				
			||||||
	wl_list_init(&input->pending_focused_surface_destroy.link);
 | 
					 | 
				
			||||||
	return input;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void relay_handle_text_input(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		text_input_new);
 | 
					 | 
				
			||||||
	struct wlr_text_input_v3 *wlr_text_input = data;
 | 
					 | 
				
			||||||
	if (relay->seat->seat != wlr_text_input->seat) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = roots_text_input_create(relay,
 | 
					 | 
				
			||||||
		wlr_text_input);
 | 
					 | 
				
			||||||
	if (!text_input) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wl_list_insert(&relay->text_inputs, &text_input->link);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void relay_handle_input_method(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_input_method_relay *relay = wl_container_of(listener, relay,
 | 
					 | 
				
			||||||
		input_method_new);
 | 
					 | 
				
			||||||
	struct wlr_input_method_v2 *input_method = data;
 | 
					 | 
				
			||||||
	if (relay->seat->seat != input_method->seat) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (relay->input_method != NULL) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_INFO, "Attempted to connect second input method to a seat");
 | 
					 | 
				
			||||||
		wlr_input_method_v2_send_unavailable(input_method);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	relay->input_method = input_method;
 | 
					 | 
				
			||||||
	wl_signal_add(&relay->input_method->events.commit,
 | 
					 | 
				
			||||||
		&relay->input_method_commit);
 | 
					 | 
				
			||||||
	relay->input_method_commit.notify = handle_im_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&relay->input_method->events.destroy,
 | 
					 | 
				
			||||||
		&relay->input_method_destroy);
 | 
					 | 
				
			||||||
	relay->input_method_destroy.notify = handle_im_destroy;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input = relay_get_focusable_text_input(relay);
 | 
					 | 
				
			||||||
	if (text_input) {
 | 
					 | 
				
			||||||
		wlr_text_input_v3_send_enter(text_input->input,
 | 
					 | 
				
			||||||
			text_input->pending_focused_surface);
 | 
					 | 
				
			||||||
		text_input_clear_pending_focused_surface(text_input);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_input_method_relay_init(struct roots_seat *seat,
 | 
					 | 
				
			||||||
		struct roots_input_method_relay *relay) {
 | 
					 | 
				
			||||||
	relay->seat = seat;
 | 
					 | 
				
			||||||
	wl_list_init(&relay->text_inputs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	relay->text_input_new.notify = relay_handle_text_input;
 | 
					 | 
				
			||||||
	wl_signal_add(&seat->input->server->desktop->text_input->events.text_input,
 | 
					 | 
				
			||||||
		&relay->text_input_new);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	relay->input_method_new.notify = relay_handle_input_method;
 | 
					 | 
				
			||||||
	wl_signal_add(
 | 
					 | 
				
			||||||
		&seat->input->server->desktop->input_method->events.input_method,
 | 
					 | 
				
			||||||
		&relay->input_method_new);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void roots_input_method_relay_set_focus(struct roots_input_method_relay *relay,
 | 
					 | 
				
			||||||
		struct wlr_surface *surface) {
 | 
					 | 
				
			||||||
	struct roots_text_input *text_input;
 | 
					 | 
				
			||||||
	wl_list_for_each(text_input, &relay->text_inputs, link) {
 | 
					 | 
				
			||||||
		if (text_input->pending_focused_surface) {
 | 
					 | 
				
			||||||
			assert(text_input->input->focused_surface == NULL);
 | 
					 | 
				
			||||||
			if (surface != text_input->pending_focused_surface) {
 | 
					 | 
				
			||||||
				text_input_clear_pending_focused_surface(text_input);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else if (text_input->input->focused_surface) {
 | 
					 | 
				
			||||||
			assert(text_input->pending_focused_surface == NULL);
 | 
					 | 
				
			||||||
			if (surface != text_input->input->focused_surface) {
 | 
					 | 
				
			||||||
				relay_disable_text_input(relay, text_input);
 | 
					 | 
				
			||||||
				wlr_text_input_v3_send_leave(text_input->input);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (surface
 | 
					 | 
				
			||||||
				&& wl_resource_get_client(text_input->input->resource)
 | 
					 | 
				
			||||||
				== wl_resource_get_client(surface->resource)) {
 | 
					 | 
				
			||||||
			if (relay->input_method) {
 | 
					 | 
				
			||||||
				wlr_text_input_v3_send_enter(text_input->input, surface);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				text_input_set_pending_focused_surface(text_input, surface);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										685
									
								
								rootston/view.c
									
										
									
									
									
								
							
							
						
						
									
										685
									
								
								rootston/view.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,685 +0,0 @@
 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_output_layout.h>
 | 
					 | 
				
			||||||
#include "rootston/desktop.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
#include "rootston/view.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_init(struct roots_view *view, const struct roots_view_interface *impl,
 | 
					 | 
				
			||||||
		enum roots_view_type type, struct roots_desktop *desktop) {
 | 
					 | 
				
			||||||
	assert(impl->destroy);
 | 
					 | 
				
			||||||
	view->impl = impl;
 | 
					 | 
				
			||||||
	view->type = type;
 | 
					 | 
				
			||||||
	view->desktop = desktop;
 | 
					 | 
				
			||||||
	view->alpha = 1.0f;
 | 
					 | 
				
			||||||
	wl_signal_init(&view->events.unmap);
 | 
					 | 
				
			||||||
	wl_signal_init(&view->events.destroy);
 | 
					 | 
				
			||||||
	wl_list_init(&view->children);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_destroy(struct roots_view *view) {
 | 
					 | 
				
			||||||
	if (view == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_signal_emit(&view->events.destroy, view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->wlr_surface != NULL) {
 | 
					 | 
				
			||||||
		view_unmap(view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Can happen if fullscreened while unmapped, and hasn't been mapped
 | 
					 | 
				
			||||||
	if (view->fullscreen_output != NULL) {
 | 
					 | 
				
			||||||
		view->fullscreen_output->fullscreen_view = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->impl->destroy(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_get_box(const struct roots_view *view, struct wlr_box *box) {
 | 
					 | 
				
			||||||
	box->x = view->box.x;
 | 
					 | 
				
			||||||
	box->y = view->box.y;
 | 
					 | 
				
			||||||
	box->width = view->box.width;
 | 
					 | 
				
			||||||
	box->height = view->box.height;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_get_deco_box(const struct roots_view *view, struct wlr_box *box) {
 | 
					 | 
				
			||||||
	view_get_box(view, box);
 | 
					 | 
				
			||||||
	if (!view->decorated) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	box->x -= view->border_width;
 | 
					 | 
				
			||||||
	box->y -= (view->border_width + view->titlebar_height);
 | 
					 | 
				
			||||||
	box->width += view->border_width * 2;
 | 
					 | 
				
			||||||
	box->height += (view->border_width * 2 + view->titlebar_height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum roots_deco_part view_get_deco_part(struct roots_view *view, double sx,
 | 
					 | 
				
			||||||
		double sy) {
 | 
					 | 
				
			||||||
	if (!view->decorated) {
 | 
					 | 
				
			||||||
		return ROOTS_DECO_PART_NONE;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int sw = view->wlr_surface->current.width;
 | 
					 | 
				
			||||||
	int sh = view->wlr_surface->current.height;
 | 
					 | 
				
			||||||
	int bw = view->border_width;
 | 
					 | 
				
			||||||
	int titlebar_h = view->titlebar_height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (sx > 0 && sx < sw && sy < 0 && sy > -view->titlebar_height) {
 | 
					 | 
				
			||||||
		return ROOTS_DECO_PART_TITLEBAR;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum roots_deco_part parts = 0;
 | 
					 | 
				
			||||||
	if (sy >= -(titlebar_h + bw) &&
 | 
					 | 
				
			||||||
			sy <= sh + bw) {
 | 
					 | 
				
			||||||
		if (sx < 0 && sx > -bw) {
 | 
					 | 
				
			||||||
			parts |= ROOTS_DECO_PART_LEFT_BORDER;
 | 
					 | 
				
			||||||
		} else if (sx > sw && sx < sw + bw) {
 | 
					 | 
				
			||||||
			parts |= ROOTS_DECO_PART_RIGHT_BORDER;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (sx >= -bw && sx <= sw + bw) {
 | 
					 | 
				
			||||||
		if (sy > sh && sy <= sh + bw) {
 | 
					 | 
				
			||||||
			parts |= ROOTS_DECO_PART_BOTTOM_BORDER;
 | 
					 | 
				
			||||||
		} else if (sy >= -(titlebar_h + bw) && sy < 0) {
 | 
					 | 
				
			||||||
			parts |= ROOTS_DECO_PART_TOP_BORDER;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO corners
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return parts;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void view_update_output(const struct roots_view *view,
 | 
					 | 
				
			||||||
		const struct wlr_box *before) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = view->desktop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->wlr_surface == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	view_get_box(view, &box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_output *output;
 | 
					 | 
				
			||||||
	wl_list_for_each(output, &desktop->outputs, link) {
 | 
					 | 
				
			||||||
		bool intersected = before != NULL && wlr_output_layout_intersects(
 | 
					 | 
				
			||||||
			desktop->layout, output->wlr_output, before);
 | 
					 | 
				
			||||||
		bool intersects = wlr_output_layout_intersects(desktop->layout,
 | 
					 | 
				
			||||||
			output->wlr_output, &box);
 | 
					 | 
				
			||||||
		if (intersected && !intersects) {
 | 
					 | 
				
			||||||
			wlr_surface_send_leave(view->wlr_surface, output->wlr_output);
 | 
					 | 
				
			||||||
			if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
				wlr_foreign_toplevel_handle_v1_output_leave(
 | 
					 | 
				
			||||||
					view->toplevel_handle, output->wlr_output);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (!intersected && intersects) {
 | 
					 | 
				
			||||||
			wlr_surface_send_enter(view->wlr_surface, output->wlr_output);
 | 
					 | 
				
			||||||
			if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
				wlr_foreign_toplevel_handle_v1_output_enter(
 | 
					 | 
				
			||||||
					view->toplevel_handle, output->wlr_output);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_move(struct roots_view *view, double x, double y) {
 | 
					 | 
				
			||||||
	if (view->box.x == x && view->box.y == y) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box before;
 | 
					 | 
				
			||||||
	view_get_box(view, &before);
 | 
					 | 
				
			||||||
	if (view->impl->move) {
 | 
					 | 
				
			||||||
		view->impl->move(view, x, y);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		view_update_position(view, x, y);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	view_update_output(view, &before);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_activate(struct roots_view *view, bool activate) {
 | 
					 | 
				
			||||||
	if (view->impl->activate) {
 | 
					 | 
				
			||||||
		view->impl->activate(view, activate);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_set_activated(view->toplevel_handle,
 | 
					 | 
				
			||||||
			activate);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_resize(struct roots_view *view, uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	struct wlr_box before;
 | 
					 | 
				
			||||||
	view_get_box(view, &before);
 | 
					 | 
				
			||||||
	if (view->impl->resize) {
 | 
					 | 
				
			||||||
		view->impl->resize(view, width, height);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	view_update_output(view, &before);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_move_resize(struct roots_view *view, double x, double y,
 | 
					 | 
				
			||||||
		uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	bool update_x = x != view->box.x;
 | 
					 | 
				
			||||||
	bool update_y = y != view->box.y;
 | 
					 | 
				
			||||||
	if (!update_x && !update_y) {
 | 
					 | 
				
			||||||
		view_resize(view, width, height);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->impl->move_resize) {
 | 
					 | 
				
			||||||
		view->impl->move_resize(view, x, y, width, height);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_x = update_x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_y = update_y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.x = x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.y = y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.width = width;
 | 
					 | 
				
			||||||
	view->pending_move_resize.height = height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_resize(view, width, height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct wlr_output *view_get_output(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct wlr_box view_box;
 | 
					 | 
				
			||||||
	view_get_box(view, &view_box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double output_x, output_y;
 | 
					 | 
				
			||||||
	wlr_output_layout_closest_point(view->desktop->layout, NULL,
 | 
					 | 
				
			||||||
		view->box.x + (double)view_box.width/2,
 | 
					 | 
				
			||||||
		view->box.y + (double)view_box.height/2,
 | 
					 | 
				
			||||||
		&output_x, &output_y);
 | 
					 | 
				
			||||||
	return wlr_output_layout_output_at(view->desktop->layout, output_x,
 | 
					 | 
				
			||||||
		output_y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_arrange_maximized(struct roots_view *view) {
 | 
					 | 
				
			||||||
	if (view->fullscreen_output != NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box view_box;
 | 
					 | 
				
			||||||
	view_get_box(view, &view_box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output *output = view_get_output(view);
 | 
					 | 
				
			||||||
	struct roots_output *roots_output = output->data;
 | 
					 | 
				
			||||||
	struct wlr_box *output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(view->desktop->layout, output);
 | 
					 | 
				
			||||||
	struct wlr_box usable_area;
 | 
					 | 
				
			||||||
	memcpy(&usable_area, &roots_output->usable_area,
 | 
					 | 
				
			||||||
			sizeof(struct wlr_box));
 | 
					 | 
				
			||||||
	usable_area.x += output_box->x;
 | 
					 | 
				
			||||||
	usable_area.y += output_box->y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_move_resize(view, usable_area.x, usable_area.y,
 | 
					 | 
				
			||||||
			usable_area.width, usable_area.height);
 | 
					 | 
				
			||||||
	view_rotate(view, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_maximize(struct roots_view *view, bool maximized) {
 | 
					 | 
				
			||||||
	if (view->maximized == maximized || view->fullscreen_output != NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->impl->maximize) {
 | 
					 | 
				
			||||||
		view->impl->maximize(view, maximized);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel_handle,
 | 
					 | 
				
			||||||
			maximized);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!view->maximized && maximized) {
 | 
					 | 
				
			||||||
		view->maximized = true;
 | 
					 | 
				
			||||||
		view->saved.x = view->box.x;
 | 
					 | 
				
			||||||
		view->saved.y = view->box.y;
 | 
					 | 
				
			||||||
		view->saved.rotation = view->rotation;
 | 
					 | 
				
			||||||
		view->saved.width = view->box.width;
 | 
					 | 
				
			||||||
		view->saved.height = view->box.height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		view_arrange_maximized(view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->maximized && !maximized) {
 | 
					 | 
				
			||||||
		view->maximized = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		view_move_resize(view, view->saved.x, view->saved.y, view->saved.width,
 | 
					 | 
				
			||||||
			view->saved.height);
 | 
					 | 
				
			||||||
		view_rotate(view, view->saved.rotation);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_set_fullscreen(struct roots_view *view, bool fullscreen,
 | 
					 | 
				
			||||||
		struct wlr_output *output) {
 | 
					 | 
				
			||||||
	bool was_fullscreen = view->fullscreen_output != NULL;
 | 
					 | 
				
			||||||
	if (was_fullscreen == fullscreen) {
 | 
					 | 
				
			||||||
		// TODO: support changing the output?
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO: check if client is focused?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->impl->set_fullscreen) {
 | 
					 | 
				
			||||||
		view->impl->set_fullscreen(view, fullscreen);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel_handle,
 | 
					 | 
				
			||||||
				fullscreen);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!was_fullscreen && fullscreen) {
 | 
					 | 
				
			||||||
		if (output == NULL) {
 | 
					 | 
				
			||||||
			output = view_get_output(view);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		struct roots_output *roots_output =
 | 
					 | 
				
			||||||
			desktop_output_from_wlr_output(view->desktop, output);
 | 
					 | 
				
			||||||
		if (roots_output == NULL) {
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct wlr_box view_box;
 | 
					 | 
				
			||||||
		view_get_box(view, &view_box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		view->saved.x = view->box.x;
 | 
					 | 
				
			||||||
		view->saved.y = view->box.y;
 | 
					 | 
				
			||||||
		view->saved.rotation = view->rotation;
 | 
					 | 
				
			||||||
		view->saved.width = view_box.width;
 | 
					 | 
				
			||||||
		view->saved.height = view_box.height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct wlr_box *output_box =
 | 
					 | 
				
			||||||
			wlr_output_layout_get_box(view->desktop->layout, output);
 | 
					 | 
				
			||||||
		view_move_resize(view, output_box->x, output_box->y, output_box->width,
 | 
					 | 
				
			||||||
			output_box->height);
 | 
					 | 
				
			||||||
		view_rotate(view, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		roots_output->fullscreen_view = view;
 | 
					 | 
				
			||||||
		view->fullscreen_output = roots_output;
 | 
					 | 
				
			||||||
		output_damage_whole(roots_output);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (was_fullscreen && !fullscreen) {
 | 
					 | 
				
			||||||
		view_move_resize(view, view->saved.x, view->saved.y, view->saved.width,
 | 
					 | 
				
			||||||
			view->saved.height);
 | 
					 | 
				
			||||||
		view_rotate(view, view->saved.rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		output_damage_whole(view->fullscreen_output);
 | 
					 | 
				
			||||||
		view->fullscreen_output->fullscreen_view = NULL;
 | 
					 | 
				
			||||||
		view->fullscreen_output = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_rotate(struct roots_view *view, float rotation) {
 | 
					 | 
				
			||||||
	if (view->rotation == rotation) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	view->rotation = rotation;
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_cycle_alpha(struct roots_view *view) {
 | 
					 | 
				
			||||||
	view->alpha -= 0.05;
 | 
					 | 
				
			||||||
	/* Don't go completely transparent */
 | 
					 | 
				
			||||||
	if (view->alpha < 0.1) {
 | 
					 | 
				
			||||||
		view->alpha = 1.0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_close(struct roots_view *view) {
 | 
					 | 
				
			||||||
	if (view->impl->close) {
 | 
					 | 
				
			||||||
		view->impl->close(view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool view_center(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	view_get_box(view, &box);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop = view->desktop;
 | 
					 | 
				
			||||||
	struct roots_input *input = desktop->server->input;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = input_last_active_seat(input);
 | 
					 | 
				
			||||||
	if (!seat) {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output *output = wlr_output_layout_output_at(desktop->layout,
 | 
					 | 
				
			||||||
		seat->cursor->cursor->x, seat->cursor->cursor->y);
 | 
					 | 
				
			||||||
	if (!output) {
 | 
					 | 
				
			||||||
		// empty layout
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const struct wlr_output_layout_output *l_output =
 | 
					 | 
				
			||||||
		wlr_output_layout_get(desktop->layout, output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int width, height;
 | 
					 | 
				
			||||||
	wlr_output_effective_resolution(output, &width, &height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double view_x = (double)(width - box.width) / 2 + l_output->x;
 | 
					 | 
				
			||||||
	double view_y = (double)(height - box.height) / 2 + l_output->y;
 | 
					 | 
				
			||||||
	view_move(view, view_x, view_y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_child_destroy(struct roots_view_child *child) {
 | 
					 | 
				
			||||||
	if (child == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	view_damage_whole(child->view);
 | 
					 | 
				
			||||||
	wl_list_remove(&child->link);
 | 
					 | 
				
			||||||
	wl_list_remove(&child->commit.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&child->new_subsurface.link);
 | 
					 | 
				
			||||||
	child->impl->destroy(child);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void view_child_handle_commit(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_view_child *child = wl_container_of(listener, child, commit);
 | 
					 | 
				
			||||||
	view_apply_damage(child->view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void view_child_handle_new_subsurface(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_view_child *child =
 | 
					 | 
				
			||||||
		wl_container_of(listener, child, new_subsurface);
 | 
					 | 
				
			||||||
	struct wlr_subsurface *wlr_subsurface = data;
 | 
					 | 
				
			||||||
	subsurface_create(child->view, wlr_subsurface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_child_init(struct roots_view_child *child,
 | 
					 | 
				
			||||||
		const struct roots_view_child_interface *impl, struct roots_view *view,
 | 
					 | 
				
			||||||
		struct wlr_surface *wlr_surface) {
 | 
					 | 
				
			||||||
	assert(impl->destroy);
 | 
					 | 
				
			||||||
	child->impl = impl;
 | 
					 | 
				
			||||||
	child->view = view;
 | 
					 | 
				
			||||||
	child->wlr_surface = wlr_surface;
 | 
					 | 
				
			||||||
	child->commit.notify = view_child_handle_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_surface->events.commit, &child->commit);
 | 
					 | 
				
			||||||
	child->new_subsurface.notify = view_child_handle_new_subsurface;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
 | 
					 | 
				
			||||||
	wl_list_insert(&view->children, &child->link);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_child_interface subsurface_impl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void subsurface_destroy(struct roots_view_child *child) {
 | 
					 | 
				
			||||||
	assert(child->impl == &subsurface_impl);
 | 
					 | 
				
			||||||
	struct roots_subsurface *subsurface = (struct roots_subsurface *)child;
 | 
					 | 
				
			||||||
	wl_list_remove(&subsurface->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&subsurface->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&subsurface->unmap.link);
 | 
					 | 
				
			||||||
	free(subsurface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_child_interface subsurface_impl = {
 | 
					 | 
				
			||||||
	.destroy = subsurface_destroy,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void subsurface_handle_destroy(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_subsurface *subsurface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, subsurface, destroy);
 | 
					 | 
				
			||||||
	view_child_destroy(&subsurface->view_child);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void subsurface_handle_map(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_subsurface *subsurface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, subsurface, map);
 | 
					 | 
				
			||||||
	struct roots_view *view = subsurface->view_child.view;
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	input_update_cursor_focus(view->desktop->server->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void subsurface_handle_unmap(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_subsurface *subsurface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, subsurface, unmap);
 | 
					 | 
				
			||||||
	struct roots_view *view = subsurface->view_child.view;
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	input_update_cursor_focus(view->desktop->server->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_subsurface *subsurface_create(struct roots_view *view,
 | 
					 | 
				
			||||||
		struct wlr_subsurface *wlr_subsurface) {
 | 
					 | 
				
			||||||
	struct roots_subsurface *subsurface =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_subsurface));
 | 
					 | 
				
			||||||
	if (subsurface == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	subsurface->wlr_subsurface = wlr_subsurface;
 | 
					 | 
				
			||||||
	view_child_init(&subsurface->view_child, &subsurface_impl,
 | 
					 | 
				
			||||||
		view, wlr_subsurface->surface);
 | 
					 | 
				
			||||||
	subsurface->destroy.notify = subsurface_handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
 | 
					 | 
				
			||||||
	subsurface->map.notify = subsurface_handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
 | 
					 | 
				
			||||||
	subsurface->unmap.notify = subsurface_handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
 | 
					 | 
				
			||||||
	return subsurface;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void view_handle_new_subsurface(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_view *view = wl_container_of(listener, view, new_subsurface);
 | 
					 | 
				
			||||||
	struct wlr_subsurface *wlr_subsurface = data;
 | 
					 | 
				
			||||||
	subsurface_create(view, wlr_subsurface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_map(struct roots_view *view, struct wlr_surface *surface) {
 | 
					 | 
				
			||||||
	assert(view->wlr_surface == NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->wlr_surface = surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_subsurface *subsurface;
 | 
					 | 
				
			||||||
	wl_list_for_each(subsurface, &view->wlr_surface->subsurfaces,
 | 
					 | 
				
			||||||
			parent_link) {
 | 
					 | 
				
			||||||
		subsurface_create(view, subsurface);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->new_subsurface.notify = view_handle_new_subsurface;
 | 
					 | 
				
			||||||
	wl_signal_add(&view->wlr_surface->events.new_subsurface,
 | 
					 | 
				
			||||||
		&view->new_subsurface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_insert(&view->desktop->views, &view->link);
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	input_update_cursor_focus(view->desktop->server->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_unmap(struct roots_view *view) {
 | 
					 | 
				
			||||||
	assert(view->wlr_surface != NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_signal_emit(&view->events.unmap, view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	wl_list_remove(&view->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_remove(&view->new_subsurface.link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view_child *child, *tmp;
 | 
					 | 
				
			||||||
	wl_list_for_each_safe(child, tmp, &view->children, link) {
 | 
					 | 
				
			||||||
		view_child_destroy(child);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->fullscreen_output != NULL) {
 | 
					 | 
				
			||||||
		output_damage_whole(view->fullscreen_output);
 | 
					 | 
				
			||||||
		view->fullscreen_output->fullscreen_view = NULL;
 | 
					 | 
				
			||||||
		view->fullscreen_output = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->wlr_surface = NULL;
 | 
					 | 
				
			||||||
	view->box.width = view->box.height = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_destroy(view->toplevel_handle);
 | 
					 | 
				
			||||||
		view->toplevel_handle = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_initial_focus(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct roots_input *input = view->desktop->server->input;
 | 
					 | 
				
			||||||
	// TODO what seat gets focus? the one with the last input event?
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		roots_seat_set_focus(seat, view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_setup(struct roots_view *view) {
 | 
					 | 
				
			||||||
	view_initial_focus(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->fullscreen_output == NULL && !view->maximized) {
 | 
					 | 
				
			||||||
		view_center(view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_create_foreign_toplevel_handle(view);
 | 
					 | 
				
			||||||
	view_update_output(view, NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_apply_damage(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct roots_output *output;
 | 
					 | 
				
			||||||
	wl_list_for_each(output, &view->desktop->outputs, link) {
 | 
					 | 
				
			||||||
		output_damage_from_view(output, view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_damage_whole(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct roots_output *output;
 | 
					 | 
				
			||||||
	wl_list_for_each(output, &view->desktop->outputs, link) {
 | 
					 | 
				
			||||||
		output_damage_whole_view(output, view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_for_each_surface(struct roots_view *view,
 | 
					 | 
				
			||||||
		wlr_surface_iterator_func_t iterator, void *user_data) {
 | 
					 | 
				
			||||||
	if (view->impl->for_each_surface) {
 | 
					 | 
				
			||||||
		view->impl->for_each_surface(view, iterator, user_data);
 | 
					 | 
				
			||||||
	} else if (view->wlr_surface) {
 | 
					 | 
				
			||||||
		wlr_surface_for_each_surface(view->wlr_surface, iterator, user_data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_update_position(struct roots_view *view, int x, int y) {
 | 
					 | 
				
			||||||
	if (view->box.x == x && view->box.y == y) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	view->box.x = x;
 | 
					 | 
				
			||||||
	view->box.y = y;
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_update_size(struct roots_view *view, int width, int height) {
 | 
					 | 
				
			||||||
	if (view->box.width == width && view->box.height == height) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	view->box.width = width;
 | 
					 | 
				
			||||||
	view->box.height = height;
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_update_decorated(struct roots_view *view, bool decorated) {
 | 
					 | 
				
			||||||
	if (view->decorated == decorated) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
	view->decorated = decorated;
 | 
					 | 
				
			||||||
	if (decorated) {
 | 
					 | 
				
			||||||
		view->border_width = 4;
 | 
					 | 
				
			||||||
		view->titlebar_height = 12;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		view->border_width = 0;
 | 
					 | 
				
			||||||
		view->titlebar_height = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	view_damage_whole(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_set_title(struct roots_view *view, const char *title) {
 | 
					 | 
				
			||||||
	if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle, title);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_set_app_id(struct roots_view *view, const char *app_id) {
 | 
					 | 
				
			||||||
	if (view->toplevel_handle) {
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel_handle, app_id);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_toplevel_handle_request_maximize(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_view *view = wl_container_of(listener, view,
 | 
					 | 
				
			||||||
			toplevel_handle_request_maximize);
 | 
					 | 
				
			||||||
	struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
 | 
					 | 
				
			||||||
	view_maximize(view, event->maximized);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_toplevel_handle_request_activate(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_view *view =
 | 
					 | 
				
			||||||
		wl_container_of(listener, view, toplevel_handle_request_activate);
 | 
					 | 
				
			||||||
	struct wlr_foreign_toplevel_handle_v1_activated_event *event = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &view->desktop->server->input->seats, link) {
 | 
					 | 
				
			||||||
		if (event->seat == seat->seat) {
 | 
					 | 
				
			||||||
			roots_seat_set_focus(seat, view);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_toplevel_handle_request_fullscreen(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data)  {
 | 
					 | 
				
			||||||
	struct roots_view *view =
 | 
					 | 
				
			||||||
		wl_container_of(listener, view, toplevel_handle_request_fullscreen);
 | 
					 | 
				
			||||||
	struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
 | 
					 | 
				
			||||||
	view_set_fullscreen(view, event->fullscreen, event->output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_toplevel_handle_request_close(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_view *view =
 | 
					 | 
				
			||||||
		wl_container_of(listener, view, toplevel_handle_request_close);
 | 
					 | 
				
			||||||
	view_close(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void view_create_foreign_toplevel_handle(struct roots_view *view) {
 | 
					 | 
				
			||||||
	view->toplevel_handle =
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_create(
 | 
					 | 
				
			||||||
			view->desktop->foreign_toplevel_manager_v1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->toplevel_handle_request_maximize.notify =
 | 
					 | 
				
			||||||
		handle_toplevel_handle_request_maximize;
 | 
					 | 
				
			||||||
	wl_signal_add(&view->toplevel_handle->events.request_maximize,
 | 
					 | 
				
			||||||
			&view->toplevel_handle_request_maximize);
 | 
					 | 
				
			||||||
	view->toplevel_handle_request_activate.notify =
 | 
					 | 
				
			||||||
		handle_toplevel_handle_request_activate;
 | 
					 | 
				
			||||||
	wl_signal_add(&view->toplevel_handle->events.request_activate,
 | 
					 | 
				
			||||||
			&view->toplevel_handle_request_activate);
 | 
					 | 
				
			||||||
	view->toplevel_handle_request_fullscreen.notify =
 | 
					 | 
				
			||||||
		handle_toplevel_handle_request_fullscreen;
 | 
					 | 
				
			||||||
	wl_signal_add(&view->toplevel_handle->events.request_fullscreen,
 | 
					 | 
				
			||||||
			&view->toplevel_handle_request_fullscreen);
 | 
					 | 
				
			||||||
	view->toplevel_handle_request_close.notify =
 | 
					 | 
				
			||||||
		handle_toplevel_handle_request_close;
 | 
					 | 
				
			||||||
	wl_signal_add(&view->toplevel_handle->events.request_close,
 | 
					 | 
				
			||||||
			&view->toplevel_handle_request_close);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,21 +0,0 @@
 | 
				
			||||||
#define _POSIX_C_SOURCE 199309L
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_virtual_keyboard_v1.h>
 | 
					 | 
				
			||||||
#include "rootston/virtual_keyboard.h"
 | 
					 | 
				
			||||||
#include "rootston/seat.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, virtual_keyboard_new);
 | 
					 | 
				
			||||||
	struct wlr_virtual_keyboard_v1 *keyboard = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_seat *seat = input_seat_from_wlr_seat(desktop->server->input,
 | 
					 | 
				
			||||||
		keyboard->seat);
 | 
					 | 
				
			||||||
	if (!seat) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "could not find roots seat");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_seat_add_device(seat, &keyboard->input_device);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,572 +0,0 @@
 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_surface.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/cursor.h"
 | 
					 | 
				
			||||||
#include "rootston/desktop.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
#include "rootston/view.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_child_interface popup_impl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_destroy(struct roots_view_child *child) {
 | 
					 | 
				
			||||||
	assert(child->impl == &popup_impl);
 | 
					 | 
				
			||||||
	struct roots_xdg_popup *popup = (struct roots_xdg_popup *)child;
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->new_popup.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->unmap.link);
 | 
					 | 
				
			||||||
	free(popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_child_interface popup_impl = {
 | 
					 | 
				
			||||||
	.destroy = popup_destroy,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup *popup =
 | 
					 | 
				
			||||||
		wl_container_of(listener, popup, destroy);
 | 
					 | 
				
			||||||
	view_child_destroy(&popup->view_child);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_map(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup *popup = wl_container_of(listener, popup, map);
 | 
					 | 
				
			||||||
	view_damage_whole(popup->view_child.view);
 | 
					 | 
				
			||||||
	input_update_cursor_focus(popup->view_child.view->desktop->server->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_unmap(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup *popup = wl_container_of(listener, popup, unmap);
 | 
					 | 
				
			||||||
	view_damage_whole(popup->view_child.view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_xdg_popup *popup_create(struct roots_view *view,
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *wlr_popup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup *popup =
 | 
					 | 
				
			||||||
		wl_container_of(listener, popup, new_popup);
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *wlr_popup = data;
 | 
					 | 
				
			||||||
	popup_create(popup->view_child.view, wlr_popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_unconstrain(struct roots_xdg_popup *popup) {
 | 
					 | 
				
			||||||
	// get the output of the popup's positioner anchor point and convert it to
 | 
					 | 
				
			||||||
	// the toplevel parent's coordinate system and then pass it to
 | 
					 | 
				
			||||||
	// wlr_xdg_popup_v6_unconstrain_from_box
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO: unconstrain popups for rotated windows
 | 
					 | 
				
			||||||
	if (popup->view_child.view->rotation != 0.0) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *view = popup->view_child.view;
 | 
					 | 
				
			||||||
	struct wlr_output_layout *layout = view->desktop->layout;
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int anchor_lx, anchor_ly;
 | 
					 | 
				
			||||||
	wlr_xdg_popup_get_anchor_point(wlr_popup, &anchor_lx, &anchor_ly);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int popup_lx, popup_ly;
 | 
					 | 
				
			||||||
	wlr_xdg_popup_get_toplevel_coords(wlr_popup, wlr_popup->geometry.x,
 | 
					 | 
				
			||||||
		wlr_popup->geometry.y, &popup_lx, &popup_ly);
 | 
					 | 
				
			||||||
	popup_lx += view->box.x;
 | 
					 | 
				
			||||||
	popup_ly += view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	anchor_lx += popup_lx;
 | 
					 | 
				
			||||||
	anchor_ly += popup_ly;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double dest_x = 0, dest_y = 0;
 | 
					 | 
				
			||||||
	wlr_output_layout_closest_point(layout, NULL, anchor_lx, anchor_ly,
 | 
					 | 
				
			||||||
		&dest_x, &dest_y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output *output =
 | 
					 | 
				
			||||||
		wlr_output_layout_output_at(layout, dest_x, dest_y);
 | 
					 | 
				
			||||||
	if (output == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box *output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(view->desktop->layout, output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// the output box expressed in the coordinate system of the toplevel parent
 | 
					 | 
				
			||||||
	// of the popup
 | 
					 | 
				
			||||||
	struct wlr_box output_toplevel_sx_box = {
 | 
					 | 
				
			||||||
		.x = output_box->x - view->box.x,
 | 
					 | 
				
			||||||
		.y = output_box->y - view->box.y,
 | 
					 | 
				
			||||||
		.width = output_box->width,
 | 
					 | 
				
			||||||
		.height = output_box->height,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_popup_unconstrain_from_box(
 | 
					 | 
				
			||||||
			popup->wlr_popup, &output_toplevel_sx_box);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_xdg_popup *popup_create(struct roots_view *view,
 | 
					 | 
				
			||||||
		struct wlr_xdg_popup *wlr_popup) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup *popup =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_xdg_popup));
 | 
					 | 
				
			||||||
	if (popup == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	popup->wlr_popup = wlr_popup;
 | 
					 | 
				
			||||||
	view_child_init(&popup->view_child, &popup_impl,
 | 
					 | 
				
			||||||
		view, wlr_popup->base->surface);
 | 
					 | 
				
			||||||
	popup->destroy.notify = popup_handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
 | 
					 | 
				
			||||||
	popup->map.notify = popup_handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.map, &popup->map);
 | 
					 | 
				
			||||||
	popup->unmap.notify = popup_handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
 | 
					 | 
				
			||||||
	popup->new_popup.notify = popup_handle_new_popup;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	popup_unconstrain(popup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return popup;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void get_size(struct roots_view *view, struct wlr_box *box) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view)->xdg_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box geo_box;
 | 
					 | 
				
			||||||
	wlr_xdg_surface_get_geometry(xdg_surface, &geo_box);
 | 
					 | 
				
			||||||
	box->width = geo_box.width;
 | 
					 | 
				
			||||||
	box->height = geo_box.height;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void activate(struct roots_view *view, bool active) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view)->xdg_surface;
 | 
					 | 
				
			||||||
	if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		wlr_xdg_toplevel_set_activated(xdg_surface, active);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void apply_size_constraints(struct wlr_xdg_surface *xdg_surface,
 | 
					 | 
				
			||||||
		uint32_t width, uint32_t height, uint32_t *dest_width,
 | 
					 | 
				
			||||||
		uint32_t *dest_height) {
 | 
					 | 
				
			||||||
	*dest_width = width;
 | 
					 | 
				
			||||||
	*dest_height = height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_state *state = &xdg_surface->toplevel->current;
 | 
					 | 
				
			||||||
	if (width < state->min_width) {
 | 
					 | 
				
			||||||
		*dest_width = state->min_width;
 | 
					 | 
				
			||||||
	} else if (state->max_width > 0 &&
 | 
					 | 
				
			||||||
			width > state->max_width) {
 | 
					 | 
				
			||||||
		*dest_width = state->max_width;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (height < state->min_height) {
 | 
					 | 
				
			||||||
		*dest_height = state->min_height;
 | 
					 | 
				
			||||||
	} else if (state->max_height > 0 &&
 | 
					 | 
				
			||||||
			height > state->max_height) {
 | 
					 | 
				
			||||||
		*dest_height = state->max_height;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void resize(struct roots_view *view, uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view)->xdg_surface;
 | 
					 | 
				
			||||||
	if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t constrained_width, constrained_height;
 | 
					 | 
				
			||||||
	apply_size_constraints(xdg_surface, width, height, &constrained_width,
 | 
					 | 
				
			||||||
		&constrained_height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_set_size(xdg_surface, constrained_width,
 | 
					 | 
				
			||||||
		constrained_height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void move_resize(struct roots_view *view, double x, double y,
 | 
					 | 
				
			||||||
		uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view);
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *wlr_xdg_surface = xdg_surface->xdg_surface;
 | 
					 | 
				
			||||||
	if (wlr_xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool update_x = x != view->box.x;
 | 
					 | 
				
			||||||
	bool update_y = y != view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t constrained_width, constrained_height;
 | 
					 | 
				
			||||||
	apply_size_constraints(wlr_xdg_surface, width, height, &constrained_width,
 | 
					 | 
				
			||||||
		&constrained_height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (update_x) {
 | 
					 | 
				
			||||||
		x = x + width - constrained_width;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (update_y) {
 | 
					 | 
				
			||||||
		y = y + height - constrained_height;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_x = update_x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_y = update_y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.x = x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.y = y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.width = constrained_width;
 | 
					 | 
				
			||||||
	view->pending_move_resize.height = constrained_height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t serial = wlr_xdg_toplevel_set_size(wlr_xdg_surface,
 | 
					 | 
				
			||||||
		constrained_width, constrained_height);
 | 
					 | 
				
			||||||
	if (serial > 0) {
 | 
					 | 
				
			||||||
		xdg_surface->pending_move_resize_configure_serial = serial;
 | 
					 | 
				
			||||||
	} else if (xdg_surface->pending_move_resize_configure_serial == 0) {
 | 
					 | 
				
			||||||
		view_update_position(view, x, y);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void maximize(struct roots_view *view, bool maximized) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view)->xdg_surface;
 | 
					 | 
				
			||||||
	if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_set_maximized(xdg_surface, maximized);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void set_fullscreen(struct roots_view *view, bool fullscreen) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view)->xdg_surface;
 | 
					 | 
				
			||||||
	if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_set_fullscreen(xdg_surface, fullscreen);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void close(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view)->xdg_surface;
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *popup = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(popup, &xdg_surface->popups, link) {
 | 
					 | 
				
			||||||
		wlr_xdg_popup_destroy(popup->base);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_send_close(xdg_surface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void for_each_surface(struct roots_view *view,
 | 
					 | 
				
			||||||
		wlr_surface_iterator_func_t iterator, void *user_data) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view)->xdg_surface;
 | 
					 | 
				
			||||||
	wlr_xdg_surface_for_each_surface(xdg_surface, iterator, user_data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void destroy(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_from_view(view);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->surface_commit.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->new_popup.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->unmap.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_move.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_resize.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_maximize.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_fullscreen.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->set_title.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->set_app_id.link);
 | 
					 | 
				
			||||||
	roots_xdg_surface->xdg_surface->data = NULL;
 | 
					 | 
				
			||||||
	free(roots_xdg_surface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_interface view_impl = {
 | 
					 | 
				
			||||||
	.activate = activate,
 | 
					 | 
				
			||||||
	.resize = resize,
 | 
					 | 
				
			||||||
	.move_resize = move_resize,
 | 
					 | 
				
			||||||
	.maximize = maximize,
 | 
					 | 
				
			||||||
	.set_fullscreen = set_fullscreen,
 | 
					 | 
				
			||||||
	.close = close,
 | 
					 | 
				
			||||||
	.for_each_surface = for_each_surface,
 | 
					 | 
				
			||||||
	.destroy = destroy,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_move(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_move);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct roots_input *input = view->desktop->server->input;
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_move_event *e = data;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat);
 | 
					 | 
				
			||||||
	// TODO verify event serial
 | 
					 | 
				
			||||||
	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	roots_seat_begin_move(seat, view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_resize(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_resize);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct roots_input *input = view->desktop->server->input;
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_resize_event *e = data;
 | 
					 | 
				
			||||||
	// TODO verify event serial
 | 
					 | 
				
			||||||
	struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat);
 | 
					 | 
				
			||||||
	assert(seat);
 | 
					 | 
				
			||||||
	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	roots_seat_begin_resize(seat, view, e->edges);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_maximize(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_maximize);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *surface = roots_xdg_surface->xdg_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_maximize(view, surface->toplevel->client_pending.maximized);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_fullscreen(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_fullscreen);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *surface = roots_xdg_surface->xdg_surface;
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_set_fullscreen_event *e = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_fullscreen(view, e->fullscreen, e->output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_set_title(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, set_title);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_title(&roots_xdg_surface->view,
 | 
					 | 
				
			||||||
			roots_xdg_surface->xdg_surface->toplevel->title);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_set_app_id(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, set_app_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_app_id(&roots_xdg_surface->view,
 | 
					 | 
				
			||||||
			roots_xdg_surface->xdg_surface->toplevel->app_id);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_surface_commit(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, surface_commit);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *surface = roots_surface->xdg_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!surface->mapped) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_apply_damage(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box size;
 | 
					 | 
				
			||||||
	get_size(view, &size);
 | 
					 | 
				
			||||||
	view_update_size(view, size.width, size.height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t pending_serial =
 | 
					 | 
				
			||||||
		roots_surface->pending_move_resize_configure_serial;
 | 
					 | 
				
			||||||
	if (pending_serial > 0 && pending_serial >= surface->configure_serial) {
 | 
					 | 
				
			||||||
		double x = view->box.x;
 | 
					 | 
				
			||||||
		double y = view->box.y;
 | 
					 | 
				
			||||||
		if (view->pending_move_resize.update_x) {
 | 
					 | 
				
			||||||
			x = view->pending_move_resize.x + view->pending_move_resize.width -
 | 
					 | 
				
			||||||
				size.width;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (view->pending_move_resize.update_y) {
 | 
					 | 
				
			||||||
			y = view->pending_move_resize.y + view->pending_move_resize.height -
 | 
					 | 
				
			||||||
				size.height;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		view_update_position(view, x, y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (pending_serial == surface->configure_serial) {
 | 
					 | 
				
			||||||
			roots_surface->pending_move_resize_configure_serial = 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_new_popup(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, new_popup);
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup *wlr_popup = data;
 | 
					 | 
				
			||||||
	popup_create(&roots_xdg_surface->view, wlr_popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_map(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, map);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	get_size(view, &box);
 | 
					 | 
				
			||||||
	view->box.width = box.width;
 | 
					 | 
				
			||||||
	view->box.height = box.height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_map(view, roots_xdg_surface->xdg_surface->surface);
 | 
					 | 
				
			||||||
	view_setup(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle,
 | 
					 | 
				
			||||||
			roots_xdg_surface->xdg_surface->toplevel->title ?: "none");
 | 
					 | 
				
			||||||
	wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel_handle,
 | 
					 | 
				
			||||||
			roots_xdg_surface->xdg_surface->toplevel->app_id ?: "none");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_unmap(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, unmap);
 | 
					 | 
				
			||||||
	view_unmap(&roots_xdg_surface->view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, destroy);
 | 
					 | 
				
			||||||
	view_destroy(&roots_xdg_surface->view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *surface = data;
 | 
					 | 
				
			||||||
	assert(surface->role != WLR_XDG_SURFACE_ROLE_NONE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "new xdg popup");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, xdg_shell_surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "new xdg toplevel: title=%s, app_id=%s",
 | 
					 | 
				
			||||||
		surface->toplevel->title, surface->toplevel->app_id);
 | 
					 | 
				
			||||||
	wlr_xdg_surface_ping(surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *roots_surface =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_xdg_surface));
 | 
					 | 
				
			||||||
	if (!roots_surface) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_init(&roots_surface->view, &view_impl, ROOTS_XDG_SHELL_VIEW, desktop);
 | 
					 | 
				
			||||||
	roots_surface->xdg_surface = surface;
 | 
					 | 
				
			||||||
	surface->data = roots_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_maximize(&roots_surface->view, surface->toplevel->client_pending.maximized);
 | 
					 | 
				
			||||||
	view_set_fullscreen(&roots_surface->view, surface->toplevel->client_pending.fullscreen,
 | 
					 | 
				
			||||||
		surface->toplevel->client_pending.fullscreen_output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->surface_commit.notify = handle_surface_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->surface->events.commit,
 | 
					 | 
				
			||||||
		&roots_surface->surface_commit);
 | 
					 | 
				
			||||||
	roots_surface->destroy.notify = handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
 | 
					 | 
				
			||||||
	roots_surface->map.notify = handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.map, &roots_surface->map);
 | 
					 | 
				
			||||||
	roots_surface->unmap.notify = handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.unmap, &roots_surface->unmap);
 | 
					 | 
				
			||||||
	roots_surface->request_move.notify = handle_request_move;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_move,
 | 
					 | 
				
			||||||
		&roots_surface->request_move);
 | 
					 | 
				
			||||||
	roots_surface->request_resize.notify = handle_request_resize;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_resize,
 | 
					 | 
				
			||||||
		&roots_surface->request_resize);
 | 
					 | 
				
			||||||
	roots_surface->request_maximize.notify = handle_request_maximize;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_maximize,
 | 
					 | 
				
			||||||
		&roots_surface->request_maximize);
 | 
					 | 
				
			||||||
	roots_surface->request_fullscreen.notify = handle_request_fullscreen;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_fullscreen,
 | 
					 | 
				
			||||||
		&roots_surface->request_fullscreen);
 | 
					 | 
				
			||||||
	roots_surface->set_title.notify = handle_set_title;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.set_title, &roots_surface->set_title);
 | 
					 | 
				
			||||||
	roots_surface->set_app_id.notify = handle_set_app_id;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.set_app_id,
 | 
					 | 
				
			||||||
			&roots_surface->set_app_id);
 | 
					 | 
				
			||||||
	roots_surface->new_popup.notify = handle_new_popup;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void decoration_handle_destroy(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_toplevel_decoration *decoration =
 | 
					 | 
				
			||||||
		wl_container_of(listener, decoration, destroy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	decoration->surface->xdg_toplevel_decoration = NULL;
 | 
					 | 
				
			||||||
	view_update_decorated(&decoration->surface->view, false);
 | 
					 | 
				
			||||||
	wl_list_remove(&decoration->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&decoration->request_mode.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&decoration->surface_commit.link);
 | 
					 | 
				
			||||||
	free(decoration);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void decoration_handle_request_mode(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_toplevel_decoration *decoration =
 | 
					 | 
				
			||||||
		wl_container_of(listener, decoration, request_mode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum wlr_xdg_toplevel_decoration_v1_mode mode =
 | 
					 | 
				
			||||||
		decoration->wlr_decoration->client_pending_mode;
 | 
					 | 
				
			||||||
	if (mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE) {
 | 
					 | 
				
			||||||
		mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_decoration_v1_set_mode(decoration->wlr_decoration, mode);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void decoration_handle_surface_commit(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_toplevel_decoration *decoration =
 | 
					 | 
				
			||||||
		wl_container_of(listener, decoration, surface_commit);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool decorated = decoration->wlr_decoration->current_mode ==
 | 
					 | 
				
			||||||
		WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
 | 
					 | 
				
			||||||
	view_update_decorated(&decoration->surface->view, decorated);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "new xdg toplevel decoration");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_xdg_surface *xdg_surface = wlr_decoration->surface->data;
 | 
					 | 
				
			||||||
	assert(xdg_surface != NULL);
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface *wlr_xdg_surface = xdg_surface->xdg_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_xdg_toplevel_decoration *decoration =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_xdg_toplevel_decoration));
 | 
					 | 
				
			||||||
	if (decoration == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	decoration->wlr_decoration = wlr_decoration;
 | 
					 | 
				
			||||||
	decoration->surface = xdg_surface;
 | 
					 | 
				
			||||||
	xdg_surface->xdg_toplevel_decoration = decoration;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	decoration->destroy.notify = decoration_handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_decoration->events.destroy, &decoration->destroy);
 | 
					 | 
				
			||||||
	decoration->request_mode.notify = decoration_handle_request_mode;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_decoration->events.request_mode,
 | 
					 | 
				
			||||||
		&decoration->request_mode);
 | 
					 | 
				
			||||||
	decoration->surface_commit.notify = decoration_handle_surface_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_xdg_surface->surface->events.commit,
 | 
					 | 
				
			||||||
		&decoration->surface_commit);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	decoration_handle_request_mode(&decoration->request_mode, wlr_decoration);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_surface *roots_xdg_surface_from_view(struct roots_view *view) {
 | 
					 | 
				
			||||||
	assert(view->impl == &view_impl);
 | 
					 | 
				
			||||||
	return (struct roots_xdg_surface *)view;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,504 +0,0 @@
 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_surface.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xdg_shell_v6.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include "rootston/desktop.h"
 | 
					 | 
				
			||||||
#include "rootston/input.h"
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_child_interface popup_impl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_destroy(struct roots_view_child *child) {
 | 
					 | 
				
			||||||
	assert(child->impl == &popup_impl);
 | 
					 | 
				
			||||||
	struct roots_xdg_popup_v6 *popup = (struct roots_xdg_popup_v6 *)child;
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->new_popup.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&popup->unmap.link);
 | 
					 | 
				
			||||||
	free(popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_child_interface popup_impl = {
 | 
					 | 
				
			||||||
	.destroy = popup_destroy,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup_v6 *popup =
 | 
					 | 
				
			||||||
		wl_container_of(listener, popup, destroy);
 | 
					 | 
				
			||||||
	view_child_destroy(&popup->view_child);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_map(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup_v6 *popup =
 | 
					 | 
				
			||||||
		wl_container_of(listener, popup, map);
 | 
					 | 
				
			||||||
	view_damage_whole(popup->view_child.view);
 | 
					 | 
				
			||||||
	input_update_cursor_focus(popup->view_child.view->desktop->server->input);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_unmap(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup_v6 *popup =
 | 
					 | 
				
			||||||
		wl_container_of(listener, popup, unmap);
 | 
					 | 
				
			||||||
	view_damage_whole(popup->view_child.view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view,
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup_v6 *wlr_popup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup_v6 *popup =
 | 
					 | 
				
			||||||
		wl_container_of(listener, popup, new_popup);
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup_v6 *wlr_popup = data;
 | 
					 | 
				
			||||||
	popup_create(popup->view_child.view, wlr_popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void popup_unconstrain(struct roots_xdg_popup_v6 *popup) {
 | 
					 | 
				
			||||||
	// get the output of the popup's positioner anchor point and convert it to
 | 
					 | 
				
			||||||
	// the toplevel parent's coordinate system and then pass it to
 | 
					 | 
				
			||||||
	// wlr_xdg_popup_v6_unconstrain_from_box
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO: unconstrain popups for rotated windows
 | 
					 | 
				
			||||||
	if (popup->view_child.view->rotation != 0.0) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_view *view = popup->view_child.view;
 | 
					 | 
				
			||||||
	struct wlr_output_layout *layout = view->desktop->layout;
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_popup;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int anchor_lx, anchor_ly;
 | 
					 | 
				
			||||||
	wlr_xdg_popup_v6_get_anchor_point(wlr_popup, &anchor_lx, &anchor_ly);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int popup_lx, popup_ly;
 | 
					 | 
				
			||||||
	wlr_xdg_popup_v6_get_toplevel_coords(wlr_popup, wlr_popup->geometry.x,
 | 
					 | 
				
			||||||
		wlr_popup->geometry.y, &popup_lx, &popup_ly);
 | 
					 | 
				
			||||||
	popup_lx += view->box.x;
 | 
					 | 
				
			||||||
	popup_ly += view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	anchor_lx += popup_lx;
 | 
					 | 
				
			||||||
	anchor_ly += popup_ly;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double dest_x = 0, dest_y = 0;
 | 
					 | 
				
			||||||
	wlr_output_layout_closest_point(layout, NULL, anchor_lx, anchor_ly,
 | 
					 | 
				
			||||||
		&dest_x, &dest_y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_output *output =
 | 
					 | 
				
			||||||
		wlr_output_layout_output_at(layout, dest_x, dest_y);
 | 
					 | 
				
			||||||
	if (output == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box *output_box =
 | 
					 | 
				
			||||||
		wlr_output_layout_get_box(view->desktop->layout, output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// the output box expressed in the coordinate system of the toplevel parent
 | 
					 | 
				
			||||||
	// of the popup
 | 
					 | 
				
			||||||
	struct wlr_box output_toplevel_sx_box = {
 | 
					 | 
				
			||||||
		.x = output_box->x - view->box.x,
 | 
					 | 
				
			||||||
		.y = output_box->y - view->box.y,
 | 
					 | 
				
			||||||
		.width = output_box->width,
 | 
					 | 
				
			||||||
		.height = output_box->height,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_popup_v6_unconstrain_from_box(popup->wlr_popup, &output_toplevel_sx_box);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view,
 | 
					 | 
				
			||||||
		struct wlr_xdg_popup_v6 *wlr_popup) {
 | 
					 | 
				
			||||||
	struct roots_xdg_popup_v6 *popup =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_xdg_popup_v6));
 | 
					 | 
				
			||||||
	if (popup == NULL) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	popup->wlr_popup = wlr_popup;
 | 
					 | 
				
			||||||
	view_child_init(&popup->view_child, &popup_impl,
 | 
					 | 
				
			||||||
		view, wlr_popup->base->surface);
 | 
					 | 
				
			||||||
	popup->destroy.notify = popup_handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
 | 
					 | 
				
			||||||
	popup->map.notify = popup_handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.map, &popup->map);
 | 
					 | 
				
			||||||
	popup->unmap.notify = popup_handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
 | 
					 | 
				
			||||||
	popup->new_popup.notify = popup_handle_new_popup;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	popup_unconstrain(popup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return popup;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void get_size(struct roots_view *view, struct wlr_box *box) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view)->xdg_surface_v6;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box geo_box;
 | 
					 | 
				
			||||||
	wlr_xdg_surface_v6_get_geometry(surface, &geo_box);
 | 
					 | 
				
			||||||
	box->width = geo_box.width;
 | 
					 | 
				
			||||||
	box->height = geo_box.height;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void activate(struct roots_view *view, bool active) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view)->xdg_surface_v6;
 | 
					 | 
				
			||||||
	if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		wlr_xdg_toplevel_v6_set_activated(surface, active);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void apply_size_constraints(struct wlr_xdg_surface_v6 *surface,
 | 
					 | 
				
			||||||
		uint32_t width, uint32_t height, uint32_t *dest_width,
 | 
					 | 
				
			||||||
		uint32_t *dest_height) {
 | 
					 | 
				
			||||||
	*dest_width = width;
 | 
					 | 
				
			||||||
	*dest_height = height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_v6_state *state = &surface->toplevel->current;
 | 
					 | 
				
			||||||
	if (width < state->min_width) {
 | 
					 | 
				
			||||||
		*dest_width = state->min_width;
 | 
					 | 
				
			||||||
	} else if (state->max_width > 0 &&
 | 
					 | 
				
			||||||
			width > state->max_width) {
 | 
					 | 
				
			||||||
		*dest_width = state->max_width;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (height < state->min_height) {
 | 
					 | 
				
			||||||
		*dest_height = state->min_height;
 | 
					 | 
				
			||||||
	} else if (state->max_height > 0 &&
 | 
					 | 
				
			||||||
			height > state->max_height) {
 | 
					 | 
				
			||||||
		*dest_height = state->max_height;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void resize(struct roots_view *view, uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view)->xdg_surface_v6;
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t constrained_width, constrained_height;
 | 
					 | 
				
			||||||
	apply_size_constraints(surface, width, height, &constrained_width,
 | 
					 | 
				
			||||||
		&constrained_height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_v6_set_size(surface, constrained_width,
 | 
					 | 
				
			||||||
		constrained_height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void move_resize(struct roots_view *view, double x, double y,
 | 
					 | 
				
			||||||
		uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view);
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface = roots_surface->xdg_surface_v6;
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool update_x = x != view->box.x;
 | 
					 | 
				
			||||||
	bool update_y = y != view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t constrained_width, constrained_height;
 | 
					 | 
				
			||||||
	apply_size_constraints(surface, width, height, &constrained_width,
 | 
					 | 
				
			||||||
		&constrained_height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (update_x) {
 | 
					 | 
				
			||||||
		x = x + width - constrained_width;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (update_y) {
 | 
					 | 
				
			||||||
		y = y + height - constrained_height;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_x = update_x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_y = update_y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.x = x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.y = y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.width = constrained_width;
 | 
					 | 
				
			||||||
	view->pending_move_resize.height = constrained_height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t serial = wlr_xdg_toplevel_v6_set_size(surface, constrained_width,
 | 
					 | 
				
			||||||
		constrained_height);
 | 
					 | 
				
			||||||
	if (serial > 0) {
 | 
					 | 
				
			||||||
		roots_surface->pending_move_resize_configure_serial = serial;
 | 
					 | 
				
			||||||
	} else if (roots_surface->pending_move_resize_configure_serial == 0) {
 | 
					 | 
				
			||||||
		view_update_position(view, x, y);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void maximize(struct roots_view *view, bool maximized) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view)->xdg_surface_v6;
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_v6_set_maximized(surface, maximized);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void set_fullscreen(struct roots_view *view, bool fullscreen) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view)->xdg_surface_v6;
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xdg_toplevel_v6_set_fullscreen(surface, fullscreen);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void close(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view)->xdg_surface_v6;
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup_v6 *popup = NULL;
 | 
					 | 
				
			||||||
	wl_list_for_each(popup, &surface->popups, link) {
 | 
					 | 
				
			||||||
		wlr_xdg_surface_v6_send_close(popup->base);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	wlr_xdg_surface_v6_send_close(surface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void for_each_surface(struct roots_view *view,
 | 
					 | 
				
			||||||
		wlr_surface_iterator_func_t iterator, void *user_data) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view)->xdg_surface_v6;
 | 
					 | 
				
			||||||
	wlr_xdg_surface_v6_for_each_surface(surface, iterator, user_data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void destroy(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		roots_xdg_surface_v6_from_view(view);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->surface_commit.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->new_popup.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->unmap.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_move.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_resize.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_maximize.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->request_fullscreen.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->set_title.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_xdg_surface->set_app_id.link);
 | 
					 | 
				
			||||||
	free(roots_xdg_surface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_interface view_impl = {
 | 
					 | 
				
			||||||
	.activate = activate,
 | 
					 | 
				
			||||||
	.resize = resize,
 | 
					 | 
				
			||||||
	.move_resize = move_resize,
 | 
					 | 
				
			||||||
	.maximize = maximize,
 | 
					 | 
				
			||||||
	.set_fullscreen = set_fullscreen,
 | 
					 | 
				
			||||||
	.close = close,
 | 
					 | 
				
			||||||
	.for_each_surface = for_each_surface,
 | 
					 | 
				
			||||||
	.destroy = destroy,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_move(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_move);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct roots_input *input = view->desktop->server->input;
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_v6_move_event *e = data;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat);
 | 
					 | 
				
			||||||
	// TODO verify event serial
 | 
					 | 
				
			||||||
	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	roots_seat_begin_move(seat, view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_resize(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_resize);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct roots_input *input = view->desktop->server->input;
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_v6_resize_event *e = data;
 | 
					 | 
				
			||||||
	// TODO verify event serial
 | 
					 | 
				
			||||||
	struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat);
 | 
					 | 
				
			||||||
	assert(seat);
 | 
					 | 
				
			||||||
	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	roots_seat_begin_resize(seat, view, e->edges);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_maximize(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_maximize);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface = roots_xdg_surface->xdg_surface_v6;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_maximize(view, surface->toplevel->client_pending.maximized);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_fullscreen(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, request_fullscreen);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface = roots_xdg_surface->xdg_surface_v6;
 | 
					 | 
				
			||||||
	struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_fullscreen(view, e->fullscreen, e->output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_set_title(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, set_title);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_title(&roots_xdg_surface->view,
 | 
					 | 
				
			||||||
		roots_xdg_surface->xdg_surface_v6->toplevel->title);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_set_app_id(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, set_app_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_app_id(&roots_xdg_surface->view,
 | 
					 | 
				
			||||||
		roots_xdg_surface->xdg_surface_v6->toplevel->app_id);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_surface_commit(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, surface_commit);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface = roots_surface->xdg_surface_v6;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!surface->mapped) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_apply_damage(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box size;
 | 
					 | 
				
			||||||
	get_size(view, &size);
 | 
					 | 
				
			||||||
	view_update_size(view, size.width, size.height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t pending_serial =
 | 
					 | 
				
			||||||
		roots_surface->pending_move_resize_configure_serial;
 | 
					 | 
				
			||||||
	if (pending_serial > 0 && pending_serial >= surface->configure_serial) {
 | 
					 | 
				
			||||||
		double x = view->box.x;
 | 
					 | 
				
			||||||
		double y = view->box.y;
 | 
					 | 
				
			||||||
		if (view->pending_move_resize.update_x) {
 | 
					 | 
				
			||||||
			x = view->pending_move_resize.x + view->pending_move_resize.width -
 | 
					 | 
				
			||||||
				size.width;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (view->pending_move_resize.update_y) {
 | 
					 | 
				
			||||||
			y = view->pending_move_resize.y + view->pending_move_resize.height -
 | 
					 | 
				
			||||||
				size.height;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		view_update_position(view, x, y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (pending_serial == surface->configure_serial) {
 | 
					 | 
				
			||||||
			roots_surface->pending_move_resize_configure_serial = 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_new_popup(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, new_popup);
 | 
					 | 
				
			||||||
	struct wlr_xdg_popup_v6 *wlr_popup = data;
 | 
					 | 
				
			||||||
	popup_create(&roots_xdg_surface->view, wlr_popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_map(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, map);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_xdg_surface->view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_box box;
 | 
					 | 
				
			||||||
	get_size(view, &box);
 | 
					 | 
				
			||||||
	view->box.width = box.width;
 | 
					 | 
				
			||||||
	view->box.height = box.height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_map(view, roots_xdg_surface->xdg_surface_v6->surface);
 | 
					 | 
				
			||||||
	view_setup(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle,
 | 
					 | 
				
			||||||
		roots_xdg_surface->xdg_surface_v6->toplevel->title ?: "none");
 | 
					 | 
				
			||||||
	wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel_handle,
 | 
					 | 
				
			||||||
		roots_xdg_surface->xdg_surface_v6->toplevel->app_id ?: "none");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_unmap(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_xdg_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_xdg_surface, unmap);
 | 
					 | 
				
			||||||
	view_unmap(&roots_xdg_surface->view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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_destroy(&roots_xdg_surface->view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct wlr_xdg_surface_v6 *surface = data;
 | 
					 | 
				
			||||||
	assert(surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_DEBUG, "new xdg popup");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, xdg_shell_v6_surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "new xdg toplevel: title=%s, app_id=%s",
 | 
					 | 
				
			||||||
		surface->toplevel->title, surface->toplevel->app_id);
 | 
					 | 
				
			||||||
	wlr_xdg_surface_v6_ping(surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_xdg_surface_v6 *roots_surface =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_xdg_surface_v6));
 | 
					 | 
				
			||||||
	if (!roots_surface) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_init(&roots_surface->view, &view_impl, ROOTS_XDG_SHELL_V6_VIEW, desktop);
 | 
					 | 
				
			||||||
	roots_surface->xdg_surface_v6 = surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_maximize(&roots_surface->view, surface->toplevel->client_pending.maximized);
 | 
					 | 
				
			||||||
	view_set_fullscreen(&roots_surface->view, surface->toplevel->client_pending.fullscreen,
 | 
					 | 
				
			||||||
		surface->toplevel->client_pending.fullscreen_output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->surface_commit.notify = handle_surface_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->surface->events.commit,
 | 
					 | 
				
			||||||
		&roots_surface->surface_commit);
 | 
					 | 
				
			||||||
	roots_surface->destroy.notify = handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
 | 
					 | 
				
			||||||
	roots_surface->map.notify = handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.map, &roots_surface->map);
 | 
					 | 
				
			||||||
	roots_surface->unmap.notify = handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.unmap, &roots_surface->unmap);
 | 
					 | 
				
			||||||
	roots_surface->request_move.notify = handle_request_move;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_move,
 | 
					 | 
				
			||||||
		&roots_surface->request_move);
 | 
					 | 
				
			||||||
	roots_surface->request_resize.notify = handle_request_resize;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_resize,
 | 
					 | 
				
			||||||
		&roots_surface->request_resize);
 | 
					 | 
				
			||||||
	roots_surface->request_maximize.notify = handle_request_maximize;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_maximize,
 | 
					 | 
				
			||||||
		&roots_surface->request_maximize);
 | 
					 | 
				
			||||||
	roots_surface->request_fullscreen.notify = handle_request_fullscreen;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.request_fullscreen,
 | 
					 | 
				
			||||||
		&roots_surface->request_fullscreen);
 | 
					 | 
				
			||||||
	roots_surface->set_title.notify = handle_set_title;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.set_title,
 | 
					 | 
				
			||||||
		&roots_surface->set_title);
 | 
					 | 
				
			||||||
	roots_surface->set_app_id.notify = handle_set_app_id;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->toplevel->events.set_app_id,
 | 
					 | 
				
			||||||
			&roots_surface->set_app_id);
 | 
					 | 
				
			||||||
	roots_surface->new_popup.notify = handle_new_popup;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xdg_surface_v6 *roots_xdg_surface_v6_from_view(
 | 
					 | 
				
			||||||
		struct roots_view *view) {
 | 
					 | 
				
			||||||
	assert(view->impl == &view_impl);
 | 
					 | 
				
			||||||
	return (struct roots_xdg_surface_v6 *)view;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,355 +0,0 @@
 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					 | 
				
			||||||
#include <wlr/config.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_surface.h>
 | 
					 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					 | 
				
			||||||
#include <wlr/xwayland.h>
 | 
					 | 
				
			||||||
#include "rootston/server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void activate(struct roots_view *view, bool active) {
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_activate(xwayland_surface, active);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void move(struct roots_view *view, double x, double y) {
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
	view_update_position(view, x, y);
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_configure(xwayland_surface, x, y,
 | 
					 | 
				
			||||||
		xwayland_surface->width, xwayland_surface->height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void apply_size_constraints(
 | 
					 | 
				
			||||||
		struct wlr_xwayland_surface *xwayland_surface, uint32_t width,
 | 
					 | 
				
			||||||
		uint32_t height, uint32_t *dest_width, uint32_t *dest_height) {
 | 
					 | 
				
			||||||
	*dest_width = width;
 | 
					 | 
				
			||||||
	*dest_height = height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface_size_hints *size_hints =
 | 
					 | 
				
			||||||
		xwayland_surface->size_hints;
 | 
					 | 
				
			||||||
	if (size_hints != NULL) {
 | 
					 | 
				
			||||||
		if (width < (uint32_t)size_hints->min_width) {
 | 
					 | 
				
			||||||
			*dest_width = size_hints->min_width;
 | 
					 | 
				
			||||||
		} else if (size_hints->max_width > 0 &&
 | 
					 | 
				
			||||||
				width > (uint32_t)size_hints->max_width) {
 | 
					 | 
				
			||||||
			*dest_width = size_hints->max_width;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (height < (uint32_t)size_hints->min_height) {
 | 
					 | 
				
			||||||
			*dest_height = size_hints->min_height;
 | 
					 | 
				
			||||||
		} else if (size_hints->max_height > 0 &&
 | 
					 | 
				
			||||||
				height > (uint32_t)size_hints->max_height) {
 | 
					 | 
				
			||||||
			*dest_height = size_hints->max_height;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void resize(struct roots_view *view, uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t constrained_width, constrained_height;
 | 
					 | 
				
			||||||
	apply_size_constraints(xwayland_surface, width, height, &constrained_width,
 | 
					 | 
				
			||||||
		&constrained_height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_configure(xwayland_surface, xwayland_surface->x,
 | 
					 | 
				
			||||||
			xwayland_surface->y, constrained_width, constrained_height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void move_resize(struct roots_view *view, double x, double y,
 | 
					 | 
				
			||||||
		uint32_t width, uint32_t height) {
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool update_x = x != view->box.x;
 | 
					 | 
				
			||||||
	bool update_y = y != view->box.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t constrained_width, constrained_height;
 | 
					 | 
				
			||||||
	apply_size_constraints(xwayland_surface, width, height, &constrained_width,
 | 
					 | 
				
			||||||
		&constrained_height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (update_x) {
 | 
					 | 
				
			||||||
		x = x + width - constrained_width;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (update_y) {
 | 
					 | 
				
			||||||
		y = y + height - constrained_height;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_x = update_x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.update_y = update_y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.x = x;
 | 
					 | 
				
			||||||
	view->pending_move_resize.y = y;
 | 
					 | 
				
			||||||
	view->pending_move_resize.width = constrained_width;
 | 
					 | 
				
			||||||
	view->pending_move_resize.height = constrained_height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_configure(xwayland_surface, x, y, constrained_width,
 | 
					 | 
				
			||||||
		constrained_height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void close(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_close(xwayland_surface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void maximize(struct roots_view *view, bool maximized) {
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_set_maximized(xwayland_surface, maximized);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void set_fullscreen(struct roots_view *view, bool fullscreen) {
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view)->xwayland_surface;
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_set_fullscreen(xwayland_surface, fullscreen);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void destroy(struct roots_view *view) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		roots_xwayland_surface_from_view(view);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->destroy.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->request_configure.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->request_move.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->request_resize.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->request_maximize.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->set_title.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->set_class.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->map.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->unmap.link);
 | 
					 | 
				
			||||||
	free(roots_surface);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct roots_view_interface view_impl = {
 | 
					 | 
				
			||||||
	.activate = activate,
 | 
					 | 
				
			||||||
	.resize = resize,
 | 
					 | 
				
			||||||
	.move = move,
 | 
					 | 
				
			||||||
	.move_resize = move_resize,
 | 
					 | 
				
			||||||
	.maximize = maximize,
 | 
					 | 
				
			||||||
	.set_fullscreen = set_fullscreen,
 | 
					 | 
				
			||||||
	.close = close,
 | 
					 | 
				
			||||||
	.destroy = destroy,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_destroy(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, destroy);
 | 
					 | 
				
			||||||
	view_destroy(&roots_surface->view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_configure(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, request_configure);
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_surface->xwayland_surface;
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface_configure_event *event = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_update_position(&roots_surface->view, event->x, event->y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_configure(xwayland_surface, event->x, event->y,
 | 
					 | 
				
			||||||
		event->width, event->height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct roots_seat *guess_seat_for_view(struct roots_view *view) {
 | 
					 | 
				
			||||||
	// the best we can do is to pick the first seat that has the surface focused
 | 
					 | 
				
			||||||
	// for the pointer
 | 
					 | 
				
			||||||
	struct roots_input *input = view->desktop->server->input;
 | 
					 | 
				
			||||||
	struct roots_seat *seat;
 | 
					 | 
				
			||||||
	wl_list_for_each(seat, &input->seats, link) {
 | 
					 | 
				
			||||||
		if (seat->seat->pointer_state.focused_surface == view->wlr_surface) {
 | 
					 | 
				
			||||||
			return seat;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_move(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, request_move);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = guess_seat_for_view(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_seat_begin_move(seat, view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_resize(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, request_resize);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
	struct roots_seat *seat = guess_seat_for_view(view);
 | 
					 | 
				
			||||||
	struct wlr_xwayland_resize_event *e = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	roots_seat_begin_resize(seat, view, e->edges);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_maximize(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, request_maximize);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_surface->xwayland_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool maximized = xwayland_surface->maximized_vert &&
 | 
					 | 
				
			||||||
		xwayland_surface->maximized_horz;
 | 
					 | 
				
			||||||
	view_maximize(view, maximized);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_request_fullscreen(struct wl_listener *listener,
 | 
					 | 
				
			||||||
		void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, request_fullscreen);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface =
 | 
					 | 
				
			||||||
		roots_surface->xwayland_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_fullscreen(view, xwayland_surface->fullscreen, NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_set_title(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, set_title);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_title(&roots_surface->view,
 | 
					 | 
				
			||||||
		roots_surface->xwayland_surface->title);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_set_class(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, set_class);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_set_app_id(&roots_surface->view,
 | 
					 | 
				
			||||||
		roots_surface->xwayland_surface->class);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_surface_commit(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, surface_commit);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
	struct wlr_surface *wlr_surface = view->wlr_surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_apply_damage(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int width = wlr_surface->current.width;
 | 
					 | 
				
			||||||
	int height = wlr_surface->current.height;
 | 
					 | 
				
			||||||
	view_update_size(view, width, height);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	double x = view->box.x;
 | 
					 | 
				
			||||||
	double y = view->box.y;
 | 
					 | 
				
			||||||
	if (view->pending_move_resize.update_x) {
 | 
					 | 
				
			||||||
		x = view->pending_move_resize.x + view->pending_move_resize.width -
 | 
					 | 
				
			||||||
			width;
 | 
					 | 
				
			||||||
		view->pending_move_resize.update_x = false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (view->pending_move_resize.update_y) {
 | 
					 | 
				
			||||||
		y = view->pending_move_resize.y + view->pending_move_resize.height -
 | 
					 | 
				
			||||||
			height;
 | 
					 | 
				
			||||||
		view->pending_move_resize.update_y = false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	view_update_position(view, x, y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_map(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, map);
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *surface = data;
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->box.x = surface->x;
 | 
					 | 
				
			||||||
	view->box.y = surface->y;
 | 
					 | 
				
			||||||
	view->box.width = surface->surface->current.width;
 | 
					 | 
				
			||||||
	view->box.height = surface->surface->current.height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->surface_commit.notify = handle_surface_commit;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->surface->events.commit,
 | 
					 | 
				
			||||||
		&roots_surface->surface_commit);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_map(view, surface->surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!surface->override_redirect) {
 | 
					 | 
				
			||||||
		if (surface->decorations == WLR_XWAYLAND_SURFACE_DECORATIONS_ALL) {
 | 
					 | 
				
			||||||
			view->decorated = true;
 | 
					 | 
				
			||||||
			view->border_width = 4;
 | 
					 | 
				
			||||||
			view->titlebar_height = 12;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		view_setup(view);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle,
 | 
					 | 
				
			||||||
			roots_surface->xwayland_surface->title ?: "none");
 | 
					 | 
				
			||||||
		wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel_handle,
 | 
					 | 
				
			||||||
			roots_surface->xwayland_surface->class ?: "none");
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		view_initial_focus(view);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void handle_unmap(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		wl_container_of(listener, roots_surface, unmap);
 | 
					 | 
				
			||||||
	struct roots_view *view = &roots_surface->view;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_remove(&roots_surface->surface_commit.link);
 | 
					 | 
				
			||||||
	view_unmap(view);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_xwayland_surface(struct wl_listener *listener, void *data) {
 | 
					 | 
				
			||||||
	struct roots_desktop *desktop =
 | 
					 | 
				
			||||||
		wl_container_of(listener, desktop, xwayland_surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct wlr_xwayland_surface *surface = data;
 | 
					 | 
				
			||||||
	wlr_log(WLR_DEBUG, "new xwayland surface: title=%s, class=%s, instance=%s",
 | 
					 | 
				
			||||||
		surface->title, surface->class, surface->instance);
 | 
					 | 
				
			||||||
	wlr_xwayland_surface_ping(surface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct roots_xwayland_surface *roots_surface =
 | 
					 | 
				
			||||||
		calloc(1, sizeof(struct roots_xwayland_surface));
 | 
					 | 
				
			||||||
	if (roots_surface == NULL) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view_init(&roots_surface->view, &view_impl, ROOTS_XWAYLAND_VIEW, desktop);
 | 
					 | 
				
			||||||
	roots_surface->view.box.x = surface->x;
 | 
					 | 
				
			||||||
	roots_surface->view.box.y = surface->y;
 | 
					 | 
				
			||||||
	roots_surface->xwayland_surface = surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	roots_surface->destroy.notify = handle_destroy;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
 | 
					 | 
				
			||||||
	roots_surface->request_configure.notify = handle_request_configure;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.request_configure,
 | 
					 | 
				
			||||||
		&roots_surface->request_configure);
 | 
					 | 
				
			||||||
	roots_surface->map.notify = handle_map;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.map, &roots_surface->map);
 | 
					 | 
				
			||||||
	roots_surface->unmap.notify = handle_unmap;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.unmap, &roots_surface->unmap);
 | 
					 | 
				
			||||||
	roots_surface->request_move.notify = handle_request_move;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.request_move, &roots_surface->request_move);
 | 
					 | 
				
			||||||
	roots_surface->request_resize.notify = handle_request_resize;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.request_resize,
 | 
					 | 
				
			||||||
		&roots_surface->request_resize);
 | 
					 | 
				
			||||||
	roots_surface->request_maximize.notify = handle_request_maximize;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.request_maximize,
 | 
					 | 
				
			||||||
		&roots_surface->request_maximize);
 | 
					 | 
				
			||||||
	roots_surface->request_fullscreen.notify = handle_request_fullscreen;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.request_fullscreen,
 | 
					 | 
				
			||||||
		&roots_surface->request_fullscreen);
 | 
					 | 
				
			||||||
	roots_surface->set_title.notify = handle_set_title;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.set_title, &roots_surface->set_title);
 | 
					 | 
				
			||||||
	roots_surface->set_class.notify = handle_set_class;
 | 
					 | 
				
			||||||
	wl_signal_add(&surface->events.set_class,
 | 
					 | 
				
			||||||
			&roots_surface->set_class);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct roots_xwayland_surface *roots_xwayland_surface_from_view(
 | 
					 | 
				
			||||||
		struct roots_view *view) {
 | 
					 | 
				
			||||||
	assert(view->impl == &view_impl);
 | 
					 | 
				
			||||||
	return (struct roots_xwayland_surface *)view;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue