mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-11-25 08:32:30 +00:00
Remove rootston
This commit is contained in:
parent
913cac1835
commit
58b2584863
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,4 +7,3 @@ build/
|
||||
build-*/
|
||||
wayland-*-protocol.*
|
||||
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))
|
||||
|
||||
subdir('examples')
|
||||
subdir('rootston')
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
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('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('rootston', type: 'boolean', value: true, description: 'Build the rootston example compositor')
|
||||
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…
Reference in New Issue
Block a user