diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a6c0208..f450563a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,7 +107,7 @@ int main(int argc, const char **argv) { } int desired_output = atoi(argv[1]); - sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); + sway_log(WLR_INFO, "Using output %d of %d", desired_output, registry->outputs->length); int i; struct output_state *output = registry->outputs->items[desired_output]; struct window *window = window_setup(registry, 100, 100, false); diff --git a/HACKING.md b/HACKING.md index 3600db24..8965d3ec 100644 --- a/HACKING.md +++ b/HACKING.md @@ -3,9 +3,9 @@ Use `sway_log(importance, fmt, ...)` to log. The following importances are available: -* `L_DEBUG`: Debug messages, only shows with `sway -d` -* `L_INFO`: Informational messages -* `L_ERROR`: Error messages +* `WLR_DEBUG`: Debug messages, only shows with `sway -d` +* `WLR_INFO`: Informational messages +* `WLR_ERROR`: Error messages `sway_log` is a macro that calls `_sway_log` with the current filename and line number, which are written into the log with your message. diff --git a/common/background-image.c b/common/background-image.c index e5fb4433..f3d2551e 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -18,7 +18,7 @@ enum background_mode parse_background_mode(const char *mode) { } else if (strcmp(mode, "solid_color") == 0) { return BACKGROUND_MODE_SOLID_COLOR; } - wlr_log(L_ERROR, "Unsupported background mode: %s", mode); + wlr_log(WLR_ERROR, "Unsupported background mode: %s", mode); return BACKGROUND_MODE_INVALID; } @@ -28,7 +28,7 @@ cairo_surface_t *load_background_image(const char *path) { GError *err = NULL; GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); if (!pixbuf) { - wlr_log(L_ERROR, "Failed to load background image (%s).", + wlr_log(WLR_ERROR, "Failed to load background image (%s).", err->message); return false; } @@ -38,11 +38,11 @@ cairo_surface_t *load_background_image(const char *path) { image = cairo_image_surface_create_from_png(path); #endif //HAVE_GDK_PIXBUF if (!image) { - wlr_log(L_ERROR, "Failed to read background image."); + wlr_log(WLR_ERROR, "Failed to read background image."); return NULL; } if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { - wlr_log(L_ERROR, "Failed to read background image: %s." + wlr_log(WLR_ERROR, "Failed to read background image: %s." #ifndef HAVE_GDK_PIXBUF "\nSway was compiled without gdk_pixbuf support, so only" "\nPNG images can be loaded. This is the likely cause." diff --git a/common/ipc-client.c b/common/ipc-client.c index a88df080..4d2d88cc 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -97,7 +97,7 @@ struct ipc_response *ipc_recv_response(int socketfd) { error_2: free(response); error_1: - wlr_log(L_ERROR, "Unable to allocate memory for IPC response"); + wlr_log(WLR_ERROR, "Unable to allocate memory for IPC response"); return NULL; } diff --git a/common/list.c b/common/list.c index 39cc10e1..66d52f70 100644 --- a/common/list.c +++ b/common/list.c @@ -2,6 +2,7 @@ #include #include #include +#include "log.h" list_t *create_list(void) { list_t *list = malloc(sizeof(list_t)); @@ -82,6 +83,20 @@ void list_swap(list_t *list, int src, int dest) { list->items[dest] = tmp; } +void list_move_to_end(list_t *list, void *item) { + int i; + for (i = 0; i < list->length; ++i) { + if (list->items[i] == item) { + break; + } + } + if (!sway_assert(i < list->length, "Item not found in list")) { + return; + } + list_del(list, i); + list_add(list, item); +} + static void list_rotate(list_t *list, int from, int to) { void *tmp = list->items[to]; diff --git a/common/log.c b/common/log.c index 2cc7289c..847f3952 100644 --- a/common/log.c +++ b/common/log.c @@ -8,7 +8,7 @@ void sway_terminate(int code); void _sway_abort(const char *format, ...) { va_list args; va_start(args, format); - _wlr_vlog(L_ERROR, format, args); + _wlr_vlog(WLR_ERROR, format, args); va_end(args); sway_terminate(EXIT_FAILURE); } @@ -20,7 +20,7 @@ bool _sway_assert(bool condition, const char *format, ...) { va_list args; va_start(args, format); - _wlr_vlog(L_ERROR, format, args); + _wlr_vlog(WLR_ERROR, format, args); va_end(args); #ifndef NDEBUG diff --git a/common/pango.c b/common/pango.c index c88e50ce..92703f80 100644 --- a/common/pango.c +++ b/common/pango.c @@ -81,7 +81,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, pango_layout_set_markup(layout, buf, -1); free(buf); } else { - wlr_log(L_ERROR, "pango_parse_markup '%s' -> error %s", text, + wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text, error->message); g_error_free(error); markup = false; // fallback to plain text diff --git a/common/readline.c b/common/readline.c index 1c396a90..a2c69018 100644 --- a/common/readline.c +++ b/common/readline.c @@ -9,7 +9,7 @@ char *read_line(FILE *file) { char *string = malloc(size); char lastChar = '\0'; if (!string) { - wlr_log(L_ERROR, "Unable to allocate memory for read_line"); + wlr_log(WLR_ERROR, "Unable to allocate memory for read_line"); return NULL; } while (1) { @@ -30,7 +30,7 @@ char *read_line(FILE *file) { char *new_string = realloc(string, size *= 2); if (!new_string) { free(string); - wlr_log(L_ERROR, "Unable to allocate memory for read_line"); + wlr_log(WLR_ERROR, "Unable to allocate memory for read_line"); return NULL; } string = new_string; diff --git a/common/unicode.c b/common/unicode.c index 38a9b48e..5070e083 100644 --- a/common/unicode.c +++ b/common/unicode.c @@ -92,7 +92,7 @@ static const struct { int utf8_size(const char *s) { uint8_t c = (uint8_t)*s; - for (size_t i = 0; i < sizeof(sizes) / 2; ++i) { + for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { if ((c & sizes[i].mask) == sizes[i].result) { return sizes[i].octets; } diff --git a/common/util.c b/common/util.c index fb7f9454..e8a88772 100644 --- a/common/util.c +++ b/common/util.c @@ -95,7 +95,7 @@ pid_t get_parent_pid(pid_t child) { token = strtok(NULL, sep); // parent pid parent = strtol(token, NULL, 10); } - + free(buffer); fclose(stat); } @@ -113,7 +113,7 @@ uint32_t parse_color(const char *color) { int len = strlen(color); if (len != 6 && len != 8) { - wlr_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color); + wlr_log(WLR_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color); return 0xFFFFFFFF; } uint32_t res = (uint32_t)strtoul(color, NULL, 16); diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 6bb03279..2e39deb6 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -22,7 +22,6 @@ types=( 'get_marks' 'get_bar_config' 'get_version' -'get_clipboard' ) _arguments -s \ diff --git a/include/ipc.h b/include/ipc.h index 8172c782..0010718b 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -13,11 +13,12 @@ enum ipc_command_type { IPC_GET_MARKS = 5, IPC_GET_BAR_CONFIG = 6, IPC_GET_VERSION = 7, + IPC_GET_BINDING_MODES = 8, + IPC_GET_CONFIG = 9, // sway-specific command types IPC_GET_INPUTS = 100, - IPC_GET_CLIPBOARD = 101, - IPC_GET_SEATS = 102, + IPC_GET_SEATS = 101, // Events sent from sway to clients. Events have the highest bits set. IPC_EVENT_WORKSPACE = ((1<<31) | 0), diff --git a/include/list.h b/include/list.h index 7eead4ac..5a0d7d80 100644 --- a/include/list.h +++ b/include/list.h @@ -24,4 +24,6 @@ int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to void list_stable_sort(list_t *list, int compare(const void *a, const void *b)); // swap two elements in a list void list_swap(list_t *list, int src, int dest); +// move item to end of list +void list_move_to_end(list_t *list, void *item); #endif diff --git a/include/log.h b/include/log.h index a9748127..dd526143 100644 --- a/include/log.h +++ b/include/log.h @@ -3,13 +3,19 @@ #include #include +#ifdef __GNUC__ +#define ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) +#else +#define ATTRIB_PRINTF(start, end) +#endif + void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2); #define sway_abort(FMT, ...) \ - _sway_abort("[%s:%d] " FMT, wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) + _sway_abort("[%s:%d] " FMT, _wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3); #define sway_assert(COND, FMT, ...) \ - _sway_assert(COND, "[%s:%d] %s:" FMT, wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) + _sway_assert(COND, "[%s:%d] %s:" FMT, _wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) void error_handler(int sig); diff --git a/include/sway/commands.h b/include/sway/commands.h index 7ca0bda8..f53d335a 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -79,7 +79,7 @@ void free_cmd_results(struct cmd_results *results); * * Free the JSON string later on. */ -const char *cmd_results_to_json(struct cmd_results *results); +char *cmd_results_to_json(struct cmd_results *results); struct cmd_results *add_color(const char *name, char *buffer, const char *color); @@ -95,7 +95,6 @@ sway_cmd cmd_client_unfocused; sway_cmd cmd_client_urgent; sway_cmd cmd_client_placeholder; sway_cmd cmd_client_background; -sway_cmd cmd_clipboard; sway_cmd cmd_commands; sway_cmd cmd_debuglog; sway_cmd cmd_default_border; @@ -107,13 +106,14 @@ sway_cmd cmd_exit; sway_cmd cmd_floating; sway_cmd cmd_floating_maximum_size; sway_cmd cmd_floating_minimum_size; -sway_cmd cmd_floating_mod; +sway_cmd cmd_floating_modifier; sway_cmd cmd_floating_scroll; sway_cmd cmd_focus; sway_cmd cmd_focus_follows_mouse; sway_cmd cmd_focus_wrapping; sway_cmd cmd_font; sway_cmd cmd_for_window; +sway_cmd cmd_force_display_urgency_hint; sway_cmd cmd_force_focus_wrapping; sway_cmd cmd_fullscreen; sway_cmd cmd_gaps; @@ -153,6 +153,7 @@ sway_cmd cmd_swaybg_command; sway_cmd cmd_swap; sway_cmd cmd_title_format; sway_cmd cmd_unmark; +sway_cmd cmd_urgent; sway_cmd cmd_workspace; sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_workspace_layout; @@ -208,8 +209,10 @@ sway_cmd input_cmd_natural_scroll; sway_cmd input_cmd_pointer_accel; sway_cmd input_cmd_repeat_delay; sway_cmd input_cmd_repeat_rate; +sway_cmd input_cmd_scroll_button; sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; +sway_cmd input_cmd_tap_button_map; sway_cmd input_cmd_xkb_layout; sway_cmd input_cmd_xkb_model; sway_cmd input_cmd_xkb_options; diff --git a/include/sway/config.h b/include/sway/config.h index 9b583323..9d2e1bf9 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -49,6 +49,7 @@ struct sway_mode { char *name; list_t *keysym_bindings; list_t *keycode_bindings; + bool pango; }; struct input_config_mapped_from_region { @@ -73,9 +74,11 @@ struct input_config { float pointer_accel; int repeat_delay; int repeat_rate; + int scroll_button; int scroll_method; int send_events; int tap; + int tap_button_map; char *xkb_layout; char *xkb_model; @@ -263,11 +266,10 @@ enum ipc_feature { IPC_FEATURE_EVENT_WINDOW = 2048, IPC_FEATURE_EVENT_BINDING = 4096, IPC_FEATURE_EVENT_INPUT = 8192, - IPC_FEATURE_GET_CLIPBOARD = 16384, - IPC_FEATURE_GET_SEATS = 32768, + IPC_FEATURE_GET_SEATS = 16384, IPC_FEATURE_ALL_COMMANDS = - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384 | 32768, + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384, IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192, IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS, @@ -314,6 +316,7 @@ struct sway_config { char *font; size_t font_height; bool pango_markup; + size_t urgent_timeout; // Flags bool focus_follows_mouse; @@ -332,6 +335,7 @@ struct sway_config { int gaps_outer; list_t *config_chain; + const char *current_config_path; const char *current_config; enum sway_container_border border; @@ -447,8 +451,14 @@ void merge_output_config(struct output_config *dst, struct output_config *src); void apply_output_config(struct output_config *oc, struct sway_container *output); +struct output_config *store_output_config(struct output_config *oc); + +void apply_output_config_to_outputs(struct output_config *oc); + void free_output_config(struct output_config *oc); +void create_default_output_configs(void); + int workspace_output_cmp_workspace(const void *a, const void *b); int sway_binding_cmp(const void *a, const void *b); @@ -484,7 +494,4 @@ void config_update_font_height(bool recalculate); /* Global config singleton. */ extern struct sway_config *config; -/* Config file currently being read */ -extern const char *current_config_path; - #endif diff --git a/include/sway/criteria.h b/include/sway/criteria.h index bd3ca0ac..6a8337c5 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -6,9 +6,10 @@ #include "tree/view.h" enum criteria_type { - CT_COMMAND = 1 << 0, - CT_ASSIGN_OUTPUT = 1 << 1, + CT_COMMAND = 1 << 0, + CT_ASSIGN_OUTPUT = 1 << 1, CT_ASSIGN_WORKSPACE = 1 << 2, + CT_NO_FOCUS = 1 << 3, }; struct criteria { diff --git a/include/sway/debug.h b/include/sway/debug.h index 2430d319..38d4eccd 100644 --- a/include/sway/debug.h +++ b/include/sway/debug.h @@ -1,7 +1,15 @@ #ifndef SWAY_DEBUG_H #define SWAY_DEBUG_H +// Tree extern bool enable_debug_tree; void update_debug_tree(); +// Damage +extern const char *damage_debug; + +// Transactions +extern int txn_timeout_ms; +extern bool txn_debug; + #endif diff --git a/include/sway/desktop.h b/include/sway/desktop.h index f1ad759a..348fb187 100644 --- a/include/sway/desktop.h +++ b/include/sway/desktop.h @@ -1,4 +1,8 @@ #include +struct sway_container; + void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole); + +void desktop_damage_whole_container(struct sway_container *con); diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h new file mode 100644 index 00000000..e5ed8a3d --- /dev/null +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -0,0 +1,28 @@ +#ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H +#define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H +#include +#include +#include "sway/server.h" + +struct sway_idle_inhibit_manager_v1 { + struct wlr_idle_inhibit_manager_v1 *wlr_manager; + struct wl_listener new_idle_inhibitor_v1; + struct wl_list inhibitors; + + struct wlr_idle *idle; +}; + +struct sway_idle_inhibitor_v1 { + struct sway_idle_inhibit_manager_v1 *manager; + struct sway_view *view; + + struct wl_list link; + struct wl_listener destroy; +}; + +void idle_inhibit_v1_check_active( + struct sway_idle_inhibit_manager_v1 *manager); + +struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( + struct wl_display *wl_display, struct wlr_idle *idle); +#endif diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7ab80eb8..cee4afed 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -6,34 +6,25 @@ /** * Transactions enable us to perform atomic layout updates. * - * When we want to make adjustments to the layout, we create a transaction. - * A transaction contains a list of affected containers and their new state. + * A transaction contains a list of containers and their new state. * A state might contain a new size, or new border settings, or new parent/child * relationships. * - * Calling transaction_commit() makes sway notify of all the affected clients - * with their new sizes. We then wait for all the views to respond with their - * new surface sizes. When all are ready, or when a timeout has passed, we apply - * the updates all at the same time. + * Committing a transaction makes sway notify of all the affected clients with + * their new sizes. We then wait for all the views to respond with their new + * surface sizes. When all are ready, or when a timeout has passed, we apply the + * updates all at the same time. + * + * When we want to make adjustments to the layout, we change the pending state + * in containers, mark them as dirty and call transaction_commit_dirty(). This + * create and commits a transaction from the dirty containers. */ -struct sway_transaction; - /** - * Create a new transaction. + * Find all dirty containers, create and commit a transaction containing them, + * and unmark them as dirty. */ -struct sway_transaction *transaction_create(void); - -/** - * Add a container's pending state to the transaction. - */ -void transaction_add_container(struct sway_transaction *transaction, - struct sway_container *container); - -/** - * Submit a transaction to the client views for configuration. - */ -void transaction_commit(struct sway_transaction *transaction); +void transaction_commit_dirty(void); /** * Notify the transaction system that a view is ready for the new layout. diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 5dd109ca..b0a3a7c5 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -11,6 +11,7 @@ struct sway_cursor { } previous; struct wlr_xcursor_manager *xcursor_manager; + const char *image; struct wl_client *image_client; struct wl_listener motion; @@ -37,4 +38,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state); +void cursor_set_image(struct sway_cursor *cursor, const char *image, + struct wl_client *client); + #endif diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 0e440701..ab25788f 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -3,6 +3,7 @@ #include #include +#include #include "sway/input/input-manager.h" struct sway_seat_device { @@ -52,6 +53,24 @@ struct sway_seat { int32_t touch_id; double touch_x, touch_y; + // Operations (drag and resize) + enum { + OP_NONE, + OP_MOVE, + OP_RESIZE, + } operation; + + struct sway_container *op_container; + enum wlr_edges op_resize_edge; + uint32_t op_button; + bool op_resize_preserve_ratio; + double op_ref_lx, op_ref_ly; // cursor's x/y at start of op + double op_ref_width, op_ref_height; // container's size at start of op + double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op + + uint32_t last_button; + uint32_t last_button_serial; + struct wl_listener focus_destroy; struct wl_listener new_container; struct wl_listener new_drag_icon; @@ -83,7 +102,7 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp); void seat_set_focus_surface(struct sway_seat *seat, - struct wlr_surface *surface); + struct wlr_surface *surface, bool unfocus); void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface *layer); @@ -118,17 +137,6 @@ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_container *seat_get_active_child(struct sway_seat *seat, struct sway_container *container); -/** - * Return the immediate child of container which was most recently focused, with - * fallback to selecting the child in the parent's `current` (rendered) children - * list. - * - * This is useful for when a tabbed container and its children are destroyed but - * still being rendered, and we have to render an appropriate child. - */ -struct sway_container *seat_get_active_current_child(struct sway_seat *seat, - struct sway_container *container); - /** * Iterate over the focus-inactive children of the container calling the * function on each. @@ -145,4 +153,15 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); +void seat_begin_move(struct sway_seat *seat, struct sway_container *con, + uint32_t button); + +void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, + uint32_t button, enum wlr_edges edge); + +void seat_end_mouse_operation(struct sway_seat *seat); + +void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, + uint32_t button, enum wlr_button_state state); + #endif diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index dd16a175..6469f097 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -9,14 +9,12 @@ struct sway_server; void ipc_init(struct sway_server *server); -void ipc_terminate(void); - struct sockaddr_un *ipc_user_sockaddr(void); void ipc_event_workspace(struct sway_container *old, struct sway_container *new, const char *change); void ipc_event_window(struct sway_container *window, const char *change); void ipc_event_barconfig_update(struct bar_config *bar); -void ipc_event_mode(const char *mode); +void ipc_event_mode(const char *mode, bool pango); #endif diff --git a/include/sway/output.h b/include/sway/output.h index 19fc5e99..b6cda83c 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -38,6 +38,16 @@ struct sway_output { } events; }; +/** + * Contains a surface's root geometry information. For instance, when rendering + * a popup, this will contain the parent view's position and size. + */ +struct root_geometry { + double x, y; + int width, height; + float rotation; +}; + void output_damage_whole(struct sway_output *output); void output_damage_surface(struct sway_output *output, double ox, double oy, @@ -54,4 +64,37 @@ void output_damage_whole_container(struct sway_output *output, struct sway_container *output_by_name(const char *name); void output_enable(struct sway_output *output); + +bool output_has_opaque_lockscreen(struct sway_output *output, + struct sway_seat *seat); + +struct sway_container *output_get_active_workspace(struct sway_output *output); + +void output_render(struct sway_output *output, struct timespec *when, + pixman_region32_t *damage); + +bool output_get_surface_box(struct root_geometry *geo, + struct sway_output *output, struct wlr_surface *surface, int sx, int sy, + struct wlr_box *surface_box); + +void output_surface_for_each_surface(struct wlr_surface *surface, + double ox, double oy, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); + +void output_view_for_each_surface(struct sway_view *view, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); + +void output_layer_for_each_surface(struct wl_list *layer_surfaces, + struct root_geometry *geo, wlr_surface_iterator_func_t iterator, + void *user_data); + +void output_unmanaged_for_each_surface(struct wl_list *unmanaged, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); + +void output_drag_icons_for_each_surface(struct wl_list *drag_icons, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); + #endif diff --git a/include/sway/scratchpad.h b/include/sway/scratchpad.h new file mode 100644 index 00000000..5af5256f --- /dev/null +++ b/include/sway/scratchpad.h @@ -0,0 +1,26 @@ +#ifndef _SWAY_SCRATCHPAD_H +#define _SWAY_SCRATCHPAD_H + +#include "tree/container.h" + +/** + * Move a container to the scratchpad. + */ +void scratchpad_add_container(struct sway_container *con); + +/** + * Remove a container from the scratchpad. + */ +void scratchpad_remove_container(struct sway_container *con); + +/** + * Show or hide the next container on the scratchpad. + */ +void scratchpad_toggle_auto(void); + +/** + * Show or hide a specific container on the scratchpad. + */ +void scratchpad_toggle_container(struct sway_container *con); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index 1e1aa3cc..70bde6d4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -23,12 +23,14 @@ struct sway_server { struct wlr_compositor *compositor; struct wlr_data_device_manager *data_device_manager; - struct wlr_idle *idle; struct sway_input_manager *input; struct wl_listener new_output; + struct wlr_idle *idle; + struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; + struct wlr_layer_shell *layer_shell; struct wl_listener layer_shell_surface; @@ -45,10 +47,7 @@ struct sway_server { bool debug_txn_timings; list_t *transactions; - - // When a view is being destroyed and is waiting for a transaction to - // complete it will be stored here. - list_t *destroying_containers; + list_t *dirty_containers; }; struct sway_server server; @@ -57,10 +56,12 @@ struct sway_server server; bool server_privileged_prepare(struct sway_server *server); bool server_init(struct sway_server *server); void server_fini(struct sway_server *server); +bool server_start_backend(struct sway_server *server); void server_run(struct sway_server *server); void handle_new_output(struct wl_listener *listener, void *data); +void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_surface(struct wl_listener *listener, void *data); diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index 58235642..d6abcc81 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -11,26 +11,8 @@ void remove_gaps(struct sway_container *c); void add_gaps(struct sway_container *c); /** - * Arrange layout for all the children of the given container, and add them to - * the given transaction. - * - * Use this function if you need to arrange multiple sections of the tree in one - * transaction. - * - * You must set the desired state of the container before calling - * arrange_windows, then don't change any state-tracked properties in the - * container until you've called transaction_commit. + * Arrange layout for all the children of the given container. */ -void arrange_windows(struct sway_container *container, - struct sway_transaction *transaction); - -/** - * Arrange layout for the given container and commit the transaction. - * - * This function is a wrapper around arrange_windows, and handles creating and - * committing the transaction for you. Use this function if you're only doing - * one arrange operation. - */ -void arrange_and_commit(struct sway_container *container); +void arrange_windows(struct sway_container *container); #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 728daa84..2a4be18c 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -68,6 +68,9 @@ struct sway_container_state { struct sway_container *parent; list_t *children; + struct sway_container *focused_inactive_child; + bool focused; + // View properties double view_x, view_y; double view_width, view_height; @@ -132,6 +135,11 @@ struct sway_container { struct sway_container *parent; + // Indicates that the container is a scratchpad container. + // Both hidden and visible scratchpad containers have scratchpad=true. + // Hidden scratchpad containers have a NULL parent. + bool scratchpad; + float alpha; struct wlr_texture *title_focused; @@ -144,6 +152,10 @@ struct sway_container { bool destroying; + // If true, indicates that the container has pending state that differs from + // the current. + bool dirty; + struct { struct wl_signal destroy; // Raised after the tree updates, but before arrange_windows @@ -297,4 +309,30 @@ bool container_is_floating(struct sway_container *container); */ void container_get_box(struct sway_container *container, struct wlr_box *box); +/** + * Move a floating container by the specified amount. + */ +void container_floating_translate(struct sway_container *con, + double x_amount, double y_amount); + +/** + * Move a floating container to a new layout-local position. + */ +void container_floating_move_to(struct sway_container *con, + double lx, double ly); + +/** + * Mark a container as dirty if it isn't already. Dirty containers will be + * included in the next transaction then unmarked as dirty. + */ +void container_set_dirty(struct sway_container *container); + +bool container_has_urgent_child(struct sway_container *container); + +/** + * If the container is involved in a drag or resize operation via a mouse, this + * ends the operation. + */ +void container_end_mouse_operation(struct sway_container *container); + #endif diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index ba265623..7d7da2d7 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -14,10 +14,11 @@ enum movement_direction { }; enum resize_edge { - RESIZE_EDGE_LEFT, - RESIZE_EDGE_RIGHT, - RESIZE_EDGE_TOP, - RESIZE_EDGE_BOTTOM, + RESIZE_EDGE_NONE = 0, + RESIZE_EDGE_LEFT = 1, + RESIZE_EDGE_RIGHT = 2, + RESIZE_EDGE_TOP = 4, + RESIZE_EDGE_BOTTOM = 8, }; struct sway_container; @@ -34,6 +35,8 @@ struct sway_root { struct wl_list outputs; // sway_output::link + list_t *scratchpad; // struct sway_container + struct { struct wl_signal new_container; } events; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7dc8ac46..3bdfe252 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -26,6 +26,8 @@ enum sway_view_prop { }; struct sway_view_impl { + void (*get_constraints)(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height); const char *(*get_string_prop)(struct sway_view *view, enum sway_view_prop prop); uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); @@ -35,6 +37,7 @@ struct sway_view_impl { void (*set_tiled)(struct sway_view *view, bool tiled); void (*set_fullscreen)(struct sway_view *view, bool fullscreen); bool (*wants_floating)(struct sway_view *view); + bool (*has_client_side_decorations)(struct sway_view *view); void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); @@ -68,6 +71,11 @@ struct sway_view { bool border_bottom; bool border_left; bool border_right; + bool using_csd; + + struct timespec urgent; + bool allow_request_urgent; + struct wl_event_source *urgent_timer; bool destroying; @@ -102,6 +110,8 @@ struct sway_xdg_shell_v6_view { 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 new_popup; struct wl_listener map; struct wl_listener unmap; @@ -116,6 +126,8 @@ struct sway_xdg_shell_view { 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 new_popup; struct wl_listener map; struct wl_listener unmap; @@ -134,6 +146,7 @@ struct sway_xwayland_view { struct wl_listener set_title; struct wl_listener set_class; struct wl_listener set_window_type; + struct wl_listener set_hints; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -208,6 +221,9 @@ uint32_t view_get_window_type(struct sway_view *view); const char *view_get_shell(struct sway_view *view); +void view_get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height); + uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); @@ -304,4 +320,8 @@ void view_update_marks_textures(struct sway_view *view); */ bool view_is_visible(struct sway_view *view); +void view_set_urgent(struct sway_view *view, bool enable); + +bool view_is_urgent(struct sway_view *view); + #endif diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index d84e4a02..ff66da6b 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -10,6 +10,7 @@ struct sway_workspace { struct sway_view *fullscreen; struct sway_container *floating; list_t *output_priority; + bool urgent; }; extern char *prev_workspace_name; @@ -47,4 +48,6 @@ struct sway_container *workspace_for_pid(pid_t pid); void workspace_record_pid(pid_t pid); +void workspace_detect_urgent(struct sway_container *workspace); + #endif diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index af478f33..1cecea71 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -16,11 +16,29 @@ struct swaybar_pointer { int x, y; }; +enum x11_button { + NONE, + LEFT, + MIDDLE, + RIGHT, + SCROLL_UP, + SCROLL_DOWN, + SCROLL_LEFT, + SCROLL_RIGHT, + BACK, + FORWARD, +}; + +enum hotspot_event_handling { + HOTSPOT_IGNORE, + HOTSPOT_PROCESS, +}; + struct swaybar_hotspot { struct wl_list link; int x, y, width, height; - void (*callback)(struct swaybar_output *output, - int x, int y, uint32_t button, void *data); + enum hotspot_event_handling (*callback)(struct swaybar_output *output, + int x, int y, enum x11_button button, void *data); void (*destroy)(void *data); void *data; }; diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index bf12a842..de9b98d7 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -71,8 +71,10 @@ void status_error(struct status_line *status, const char *text); bool status_handle_readable(struct status_line *status); void status_line_free(struct status_line *status); bool i3bar_handle_readable(struct status_line *status); -void i3bar_block_send_click(struct status_line *status, - struct i3bar_block *block, int x, int y, uint32_t button); +enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, + struct i3bar_block *block, int x, int y, enum x11_button button); void i3bar_block_free(struct i3bar_block *block); +enum x11_button wl_button_to_x11_button(uint32_t button); +enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value); #endif diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index 2931fd61..950cfaaf 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -19,10 +19,33 @@ enum auth_state { AUTH_STATE_INVALID, }; +struct swaylock_colorset { + uint32_t input; + uint32_t cleared; + uint32_t verifying; + uint32_t wrong; +}; + +struct swaylock_colors { + uint32_t background; + uint32_t bs_highlight; + uint32_t key_highlight; + uint32_t separator; + struct swaylock_colorset inside; + struct swaylock_colorset line; + struct swaylock_colorset ring; + struct swaylock_colorset text; +}; + struct swaylock_args { - uint32_t color; + struct swaylock_colors colors; enum background_mode mode; + char *font; + uint32_t radius; + uint32_t thickness; + bool ignore_empty; bool show_indicator; + bool daemonize; }; struct swaylock_password { diff --git a/sway/commands.c b/sway/commands.c index 5b20857a..fdae1961 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -98,12 +98,18 @@ static struct cmd_handler handlers[] = { { "client.unfocused", cmd_client_unfocused }, { "client.urgent", cmd_client_urgent }, { "default_border", cmd_default_border }, + { "default_floating_border", cmd_default_floating_border }, { "exec", cmd_exec }, { "exec_always", cmd_exec_always }, + { "floating_maximum_size", cmd_floating_maximum_size }, + { "floating_minimum_size", cmd_floating_minimum_size }, + { "floating_modifier", cmd_floating_modifier }, + { "focus", cmd_focus }, { "focus_follows_mouse", cmd_focus_follows_mouse }, { "focus_wrapping", cmd_focus_wrapping }, { "font", cmd_font }, { "for_window", cmd_for_window }, + { "force_display_urgency_hint", cmd_force_display_urgency_hint }, { "force_focus_wrapping", cmd_force_focus_wrapping }, { "fullscreen", cmd_fullscreen }, { "gaps", cmd_gaps }, @@ -112,6 +118,7 @@ static struct cmd_handler handlers[] = { { "input", cmd_input }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, + { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "seat", cmd_seat }, { "set", cmd_set }, @@ -133,7 +140,6 @@ static struct cmd_handler command_handlers[] = { { "border", cmd_border }, { "exit", cmd_exit }, { "floating", cmd_floating }, - { "focus", cmd_focus }, { "fullscreen", cmd_fullscreen }, { "kill", cmd_kill }, { "layout", cmd_layout }, @@ -143,6 +149,7 @@ static struct cmd_handler command_handlers[] = { { "reload", cmd_reload }, { "rename", cmd_rename }, { "resize", cmd_resize }, + { "scratchpad", cmd_scratchpad }, { "split", cmd_split }, { "splith", cmd_splith }, { "splitt", cmd_splitt }, @@ -151,6 +158,7 @@ static struct cmd_handler command_handlers[] = { { "swap", cmd_swap }, { "title_format", cmd_title_format }, { "unmark", cmd_unmark }, + { "urgent", cmd_urgent }, }; static int handler_compare(const void *_a, const void *_b) { @@ -163,7 +171,7 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, int handlers_size) { struct cmd_handler d = { .command=line }; struct cmd_handler *res = NULL; - wlr_log(L_DEBUG, "find_handler(%s)", line); + wlr_log(WLR_DEBUG, "find_handler(%s)", line); bool config_loading = config->reading || !config->active; @@ -248,10 +256,10 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { cmd = argsep(&cmdlist, ","); cmd += strspn(cmd, whitespace); if (strcmp(cmd, "") == 0) { - wlr_log(L_INFO, "Ignoring empty command."); + wlr_log(WLR_INFO, "Ignoring empty command."); continue; } - wlr_log(L_INFO, "Handling command '%s'", cmd); + wlr_log(WLR_INFO, "Handling command '%s'", cmd); //TODO better handling of argv int argc; char **argv = split_args(cmd, &argc); @@ -319,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } while(head); cleanup: free(exec); - free(views); + list_free(views); if (!results) { results = cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -344,7 +352,7 @@ struct cmd_results *config_command(char *exec) { // Start block if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { - char *block = join_args(argv, argc - 1); + char *block = join_args(argv, argc - 1); results = cmd_results_new(CMD_BLOCK, block, NULL); free(block); goto cleanup; @@ -355,7 +363,7 @@ struct cmd_results *config_command(char *exec) { results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); goto cleanup; } - wlr_log(L_INFO, "handling config command '%s'", exec); + wlr_log(WLR_INFO, "handling config command '%s'", exec); struct cmd_handler *handler = find_handler(argv[0], NULL, 0); if (!handler) { char *input = argv[0] ? argv[0] : "(empty)"; @@ -388,7 +396,7 @@ cleanup: struct cmd_results *config_subcommand(char **argv, int argc, struct cmd_handler *handlers, size_t handlers_size) { char *command = join_args(argv, argc); - wlr_log(L_DEBUG, "Subcommand: %s", command); + wlr_log(WLR_DEBUG, "Subcommand: %s", command); free(command); struct cmd_handler *handler = find_handler(argv[0], handlers, @@ -428,8 +436,7 @@ struct cmd_results *config_commands_command(char *exec) { struct cmd_handler *handler = find_handler(cmd, NULL, 0); if (!handler && strcmp(cmd, "*") != 0) { - char *input = cmd ? cmd : "(empty)"; - results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); + results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command"); goto cleanup; } @@ -471,14 +478,16 @@ struct cmd_results *config_commands_command(char *exec) { } if (!policy) { policy = alloc_command_policy(cmd); - sway_assert(policy, "Unable to allocate security policy"); - if (policy) { - list_add(config->command_policies, policy); + if (!sway_assert(policy, "Unable to allocate security policy")) { + results = cmd_results_new(CMD_INVALID, cmd, + "Unable to allocate memory"); + goto cleanup; } + list_add(config->command_policies, policy); } policy->context = context; - wlr_log(L_INFO, "Set command policy for %s to %d", + wlr_log(WLR_INFO, "Set command policy for %s to %d", policy->command, policy->context); results = cmd_results_new(CMD_SUCCESS, NULL, NULL); @@ -492,7 +501,7 @@ struct cmd_results *cmd_results_new(enum cmd_status status, const char *input, const char *format, ...) { struct cmd_results *results = malloc(sizeof(struct cmd_results)); if (!results) { - wlr_log(L_ERROR, "Unable to allocate command results"); + wlr_log(WLR_ERROR, "Unable to allocate command results"); return NULL; } results->status = status; @@ -526,7 +535,7 @@ void free_cmd_results(struct cmd_results *results) { free(results); } -const char *cmd_results_to_json(struct cmd_results *results) { +char *cmd_results_to_json(struct cmd_results *results) { json_object *result_array = json_object_new_array(); json_object *root = json_object_new_object(); json_object_object_add(root, "success", @@ -541,9 +550,9 @@ const char *cmd_results_to_json(struct cmd_results *results) { } json_object_array_add(result_array, root); const char *json = json_object_to_json_string(result_array); - free(result_array); - free(root); - return json; + char *res = strdup(json); + json_object_put(result_array); + return res; } /** diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 9d15e166..0bc0929a 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -27,6 +27,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { if (strncmp(*argv, "→", strlen("→")) == 0) { if (argc < 3) { + free(criteria); return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); } ++argv; @@ -44,7 +45,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { criteria->target = join_args(argv, target_len); list_add(config->criteria, criteria); - wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, + wlr_log(WLR_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, criteria->target); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/bar.c b/sway/commands/bar.c index d84ce808..f6a70c17 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -63,13 +63,13 @@ struct cmd_results *cmd_bar(int argc, char **argv) { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *item = config->bars->items[i]; if (strcmp(item->id, argv[0]) == 0) { - wlr_log(L_DEBUG, "Selecting bar: %s", argv[0]); + wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]); bar = item; break; } } if (!bar) { - wlr_log(L_DEBUG, "Creating bar: %s", argv[0]); + wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]); bar = default_bar_config(); if (!bar) { return cmd_results_new(CMD_FAILURE, "bar", @@ -108,7 +108,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) { // Set current bar config->current_bar = bar; - wlr_log(L_DEBUG, "Creating bar %s", bar->id); + wlr_log(WLR_DEBUG, "Creating bar %s", bar->id); } return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index 3ba5f33f..0c48bee9 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c @@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { } if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->binding_mode_indicator = true; - wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", + wlr_log(WLR_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->binding_mode_indicator = false; - wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", + wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id); } return cmd_results_new(CMD_INVALID, "binding_mode_indicator", diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 80b7a593..2aa4e895 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -14,8 +14,8 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { } char *font = join_args(argv, argc); free(config->current_bar->font); - config->current_bar->font = strdup(font); - wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", + config->current_bar->font = font; + wlr_log(WLR_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c index 3160caed..18258526 100644 --- a/sway/commands/bar/height.c +++ b/sway/commands/bar/height.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_height(int argc, char **argv) { "Invalid height value: %s", argv[0]); } config->current_bar->height = height; - wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", + wlr_log(WLR_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 6641f184..502ce2c4 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c @@ -27,7 +27,7 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, if (!config->reading) { ipc_event_barconfig_update(bar); } - wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", + wlr_log(WLR_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id); } // free old mode diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index 6ce86fef..65fa69fd 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c @@ -24,7 +24,7 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) { } } - wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); + wlr_log(WLR_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); // free old bar id free(config->current_bar->id); diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 34bb0a4f..28e2d77b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -28,7 +28,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode if (!config->reading) { ipc_event_barconfig_update(bar); } - wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); + wlr_log(WLR_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); } // free old mode diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index 7ba4b125..09025fff 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c @@ -22,14 +22,15 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { mod |= tmp_mod; continue; } else { + error = cmd_results_new(CMD_INVALID, "modifier", + "Unknown modifier '%s'", split->items[i]); free_flat_list(split); - return cmd_results_new(CMD_INVALID, "modifier", - "Unknown modifier '%s'", split->items[i]); + return error; } } free_flat_list(split); config->current_bar->modifier = mod; - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index f7ca0aa4..72754e05 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c @@ -42,7 +42,7 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { if (add_output) { list_add(outputs, strdup(output)); - wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", + wlr_log(WLR_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index 480af724..857571fb 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c @@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { } if (strcasecmp("enabled", argv[0]) == 0) { config->current_bar->pango_markup = true; - wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", + wlr_log(WLR_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id); } else if (strcasecmp("disabled", argv[0]) == 0) { config->current_bar->pango_markup = false; - wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", + wlr_log(WLR_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id); } else { error = cmd_results_new(CMD_INVALID, "pango_markup", diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index 9c580483..44bb4ae3 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c @@ -15,8 +15,9 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) { char *valid[] = { "top", "bottom", "left", "right" }; for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { if (strcasecmp(valid[i], argv[0]) == 0) { - wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s", + wlr_log(WLR_DEBUG, "Setting bar position '%s' for bar: %s", argv[0], config->current_bar->id); + free(config->current_bar->position); config->current_bar->position = strdup(argv[0]); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 1e08df6d..392ab730 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) { } free(config->current_bar->separator_symbol); config->current_bar->separator_symbol = strdup(argv[0]); - wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", + wlr_log(WLR_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c index 5e199cde..6f6f81a3 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) { } free(config->current_bar->status_command); config->current_bar->status_command = join_args(argv, argc); - wlr_log(L_DEBUG, "Feeding bar with status command: %s", + wlr_log(WLR_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c index 4f24a356..4e47d047 100644 --- a/sway/commands/bar/strip_workspace_numbers.c +++ b/sway/commands/bar/strip_workspace_numbers.c @@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { } if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->strip_workspace_numbers = true; - wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", + wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->strip_workspace_numbers = false; - wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", + wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id); } else { return cmd_results_new(CMD_INVALID, diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c index 520cdd11..04e78e77 100644 --- a/sway/commands/bar/swaybar_command.c +++ b/sway/commands/bar/swaybar_command.c @@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) { } free(config->current_bar->swaybar_command); config->current_bar->swaybar_command = join_args(argv, argc); - wlr_log(L_DEBUG, "Using custom swaybar command: %s", + wlr_log(WLR_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c index 6edc3a0d..a4079b2a 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c @@ -14,11 +14,11 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { } if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->workspace_buttons = true; - wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", + wlr_log(WLR_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->workspace_buttons = false; - wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", + wlr_log(WLR_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id); } else { return cmd_results_new(CMD_INVALID, "workspace_buttons", diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index 7386f82c..701de00a 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c @@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { } if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->wrap_scroll = true; - wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", + wlr_log(WLR_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->wrap_scroll = false; - wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", + wlr_log(WLR_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id); } else { return cmd_results_new(CMD_INVALID, diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 821f9cd1..83e9e432 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -184,7 +184,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, for (int i = 0; i < mode_bindings->length; ++i) { struct sway_binding *config_binding = mode_bindings->items[i]; if (binding_key_compare(binding, config_binding)) { - wlr_log(L_DEBUG, "overwriting old binding with command '%s'", + wlr_log(WLR_DEBUG, "overwriting old binding with command '%s'", config_binding->command); free_sway_binding(config_binding); mode_bindings->items[i] = binding; @@ -196,7 +196,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_add(mode_bindings, binding); } - wlr_log(L_DEBUG, "%s - Bound %s to command %s", + wlr_log(WLR_DEBUG, "%s - Bound %s to command %s", bindtype, argv[0], binding->command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/border.c b/sway/commands/border.c index 6db85395..9c19e20a 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -42,7 +42,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { container_set_geometry_from_floating_view(view->swayc); } - arrange_and_commit(view->swayc); + arrange_windows(view->swayc); struct sway_seat *seat = input_manager_current_seat(input_manager); if (seat->cursor) { diff --git a/sway/commands/default_floating_border.c b/sway/commands/default_floating_border.c new file mode 100644 index 00000000..1bfc24af --- /dev/null +++ b/sway/commands/default_floating_border.c @@ -0,0 +1,29 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_default_floating_border(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "default_floating_border", + EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (strcmp(argv[0], "none") == 0) { + config->floating_border = B_NONE; + } else if (strcmp(argv[0], "normal") == 0) { + config->floating_border = B_NORMAL; + } else if (strcmp(argv[0], "pixel") == 0) { + config->floating_border = B_PIXEL; + } else { + return cmd_results_new(CMD_INVALID, "default_floating_border", + "Expected 'default_floating_border ' " + "or 'default_floating_border '"); + } + if (argc == 2) { + config->floating_border_thickness = atoi(argv[1]); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/exec.c b/sway/commands/exec.c index 363d5bef..7fc54123 100644 --- a/sway/commands/exec.c +++ b/sway/commands/exec.c @@ -8,7 +8,7 @@ struct cmd_results *cmd_exec(int argc, char **argv) { if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); if (config->reloading) { char *args = join_args(argv, argc); - wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); + wlr_log(WLR_DEBUG, "Ignoring 'exec %s' due to reload", args); free(args); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index abd52e59..9bf2b320 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -20,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { char *tmp = NULL; if (strcmp((char*)*argv, "--no-startup-id") == 0) { - wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); + wlr_log(WLR_INFO, "exec switch '--no-startup-id' not supported, ignored."); if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) { return error; } @@ -35,50 +35,49 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { strncpy(cmd, tmp, sizeof(cmd) - 1); cmd[sizeof(cmd) - 1] = 0; free(tmp); - wlr_log(L_DEBUG, "Executing %s", cmd); + wlr_log(WLR_DEBUG, "Executing %s", cmd); int fd[2]; if (pipe(fd) != 0) { - wlr_log(L_ERROR, "Unable to create pipe for fork"); + wlr_log(WLR_ERROR, "Unable to create pipe for fork"); } - pid_t pid; - pid_t *child = malloc(sizeof(pid_t)); // malloc'd so that Linux can avoid copying the process space - if (!child) { - return cmd_results_new(CMD_FAILURE, "exec_always", "Unable to allocate child pid"); - } + pid_t pid, child; // Fork process if ((pid = fork()) == 0) { // Fork child process again setsid(); - if ((*child = fork()) == 0) { - execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); - // Not reached - } close(fd[0]); + if ((child = fork()) == 0) { + close(fd[1]); + execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); + _exit(0); + } ssize_t s = 0; while ((size_t)s < sizeof(pid_t)) { - s += write(fd[1], ((uint8_t *)child) + s, sizeof(pid_t) - s); + s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s); } close(fd[1]); _exit(0); // Close child process } else if (pid < 0) { - free(child); + close(fd[0]); + close(fd[1]); return cmd_results_new(CMD_FAILURE, "exec_always", "fork() failed"); } close(fd[1]); // close write ssize_t s = 0; while ((size_t)s < sizeof(pid_t)) { - s += read(fd[0], ((uint8_t *)child) + s, sizeof(pid_t) - s); + s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s); } close(fd[0]); // cleanup child process waitpid(pid, NULL, 0); - if (*child > 0) { - wlr_log(L_DEBUG, "Child process created with pid %d", *child); - workspace_record_pid(*child); + if (child > 0) { + wlr_log(WLR_DEBUG, "Child process created with pid %d", child); + workspace_record_pid(child); } else { - free(child); + return cmd_results_new(CMD_FAILURE, "exec_always", + "Second fork() failed"); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/floating.c b/sway/commands/floating.c index e6003521..6ab56c3b 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -37,7 +37,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); struct sway_container *workspace = container_parent(container, C_WORKSPACE); - arrange_and_commit(workspace); + arrange_windows(workspace); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c new file mode 100644 index 00000000..0af78908 --- /dev/null +++ b/sway/commands/floating_minmax_size.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include +#include "sway/commands.h" +#include "log.h" + +static const char* min_usage = + "Expected 'floating_minimum_size x '"; + +static const char* max_usage = + "Expected 'floating_maximum_size x '"; + +static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, + const char *usage, int *config_width, int *config_height) { + struct cmd_results *error; + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 3))) { + return error; + } + + char *err; + int width = (int)strtol(argv[0], &err, 10); + if (*err) { + return cmd_results_new(CMD_INVALID, cmd_name, usage); + } + + if (strcmp(argv[1], "x") != 0) { + return cmd_results_new(CMD_INVALID, cmd_name, usage); + } + + int height = (int)strtol(argv[2], &err, 10); + if (*err) { + return cmd_results_new(CMD_INVALID, cmd_name, usage); + } + + *config_width = width; + *config_height = height; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) { + return handle_command(argc, argv, "floating_minimum_size", min_usage, + &config->floating_minimum_width, &config->floating_minimum_height); +} + +struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) { + return handle_command(argc, argv, "floating_maximum_size", max_usage, + &config->floating_maximum_width, &config->floating_maximum_height); +} diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c new file mode 100644 index 00000000..9432c9f1 --- /dev/null +++ b/sway/commands/floating_modifier.c @@ -0,0 +1,20 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "util.h" + +struct cmd_results *cmd_floating_modifier(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + uint32_t mod = get_modifier_mask_by_name(argv[0]); + if (!mod) { + return cmd_results_new(CMD_INVALID, "floating_modifier", + "Invalid modifier"); + } + + config->floating_mod = mod; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 74d9d535..9cd8bfae 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -1,10 +1,14 @@ #include #include #include "log.h" +#include "sway/commands.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/output.h" +#include "sway/tree/arrange.h" #include "sway/tree/view.h" -#include "sway/commands.h" +#include "sway/tree/workspace.h" +#include "stringop.h" static bool parse_movement_direction(const char *name, enum movement_direction *out) { @@ -27,7 +31,55 @@ static bool parse_movement_direction(const char *name, return true; } +static struct cmd_results *focus_mode(struct sway_container *con, + struct sway_seat *seat, bool floating) { + struct sway_container *ws = con->type == C_WORKSPACE ? + con : container_parent(con, C_WORKSPACE); + struct sway_container *new_focus = ws; + if (floating) { + new_focus = ws->sway_workspace->floating; + if (new_focus->children->length == 0) { + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + } + seat_set_focus(seat, seat_get_active_child(seat, new_focus)); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +static struct cmd_results *focus_output(struct sway_container *con, + struct sway_seat *seat, int argc, char **argv) { + if (!argc) { + return cmd_results_new(CMD_INVALID, "focus", + "Expected 'focus output '"); + } + char *identifier = join_args(argv, argc); + struct sway_container *output = output_by_name(identifier); + + if (!output) { + enum movement_direction direction; + if (!parse_movement_direction(identifier, &direction) || + direction == MOVE_PARENT || direction == MOVE_CHILD) { + free(identifier); + return cmd_results_new(CMD_INVALID, "focus", + "There is no output with that name"); + } + struct sway_container *focus = seat_get_focus(seat); + focus = container_parent(focus, C_OUTPUT); + output = container_get_in_direction(focus, seat, direction); + } + + free(identifier); + if (output) { + seat_set_focus(seat, seat_get_focus_inactive(seat, output)); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + struct cmd_results *cmd_focus(int argc, char **argv) { + if (config->reading || !config->active) { + return cmd_results_new(CMD_DEFER, NULL, NULL); + } struct sway_container *con = config->handler_context.current_container; struct sway_seat *seat = config->handler_context.seat; if (con->type < C_WORKSPACE) { @@ -40,11 +92,24 @@ struct cmd_results *cmd_focus(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } - // TODO mode_toggle + if (strcmp(argv[0], "floating") == 0) { + return focus_mode(con, seat, true); + } else if (strcmp(argv[0], "tiling") == 0) { + return focus_mode(con, seat, false); + } else if (strcmp(argv[0], "mode_toggle") == 0) { + return focus_mode(con, seat, !container_is_floating(con)); + } + + if (strcmp(argv[0], "output") == 0) { + argc--; argv++; + return focus_output(con, seat, argc, argv); + } + enum movement_direction direction = 0; if (!parse_movement_direction(argv[0], &direction)) { return cmd_results_new(CMD_INVALID, "focus", - "Expected 'focus ' or 'focus output '"); + "Expected 'focus ' " + "or 'focus output '"); } struct sway_container *next_focus = container_get_in_direction( diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index 8c425a1d..ac4d6563 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c @@ -24,7 +24,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { criteria->cmdlist = join_args(argv + 1, argc - 1); list_add(config->criteria, criteria); - wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); + wlr_log(WLR_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/force_display_urgency_hint.c b/sway/commands/force_display_urgency_hint.c new file mode 100644 index 00000000..5e5e2d55 --- /dev/null +++ b/sway/commands/force_display_urgency_hint.c @@ -0,0 +1,23 @@ +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_force_display_urgency_hint(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "force_display_urgency_hint", + EXPECTED_AT_LEAST, 1))) { + return error; + } + + char *err; + int timeout = (int)strtol(argv[0], &err, 10); + if (*err) { + if (strcmp(err, "ms") != 0) { + return cmd_results_new(CMD_INVALID, "force_display_urgency_hint", + "Expected 'force_display_urgency_hint ms'"); + } + } + + config->urgent_timeout = timeout > 0 ? timeout : 0; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 1a4d8b41..0b5beaa2 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -34,7 +34,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { view_set_fullscreen(view, wants_fullscreen); struct sway_container *workspace = container_parent(container, C_WORKSPACE); - arrange_and_commit(workspace->parent); + arrange_windows(workspace->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 801fb179..3906eb70 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -43,7 +43,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "gaps", "gaps edge_gaps on|off|toggle"); } - arrange_and_commit(&root_container); + arrange_windows(&root_container); } else { int amount_idx = 0; // the current index in argv enum gaps_op op = GAPS_OP_SET; @@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { if (amount_idx == 0) { // gaps config->gaps_inner = val; config->gaps_outer = val; - arrange_and_commit(&root_container); + arrange_windows(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } // Other variants. The middle-length variant (gaps inner|outer ) @@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { } else { config->gaps_outer = total; } - arrange_and_commit(&root_container); + arrange_windows(&root_container); } else { struct sway_container *c = config->handler_context.current_container; @@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { c->gaps_outer = total; } - arrange_and_commit(c->parent ? c->parent : &root_container); + arrange_windows(c->parent ? c->parent : &root_container); } } diff --git a/sway/commands/input.c b/sway/commands/input.c index 678c57c4..5b203ea0 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -20,8 +20,10 @@ static struct cmd_handler input_handlers[] = { { "pointer_accel", input_cmd_pointer_accel }, { "repeat_delay", input_cmd_repeat_delay }, { "repeat_rate", input_cmd_repeat_rate }, + { "scroll_button", input_cmd_scroll_button }, { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, + { "tap_button_map", input_cmd_tap_button_map }, { "xkb_layout", input_cmd_xkb_layout }, { "xkb_model", input_cmd_xkb_model }, { "xkb_options", input_cmd_xkb_options }, @@ -35,7 +37,7 @@ struct cmd_results *cmd_input(int argc, char **argv) { return error; } - wlr_log(L_DEBUG, "entering input block: %s", argv[0]); + wlr_log(WLR_DEBUG, "entering input block: %s", argv[0]); config->handler_context.input_config = new_input_config(argv[0]); if (!config->handler_context.input_config) { diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c index 37d6e133..a4108ec3 100644 --- a/sway/commands/input/accel_profile.c +++ b/sway/commands/input/accel_profile.c @@ -23,6 +23,7 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { } else if (strcasecmp(argv[0], "flat") == 0) { new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "accel_profile", "Expected 'accel_profile '"); } diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c index 8f1f0aa7..5d0d8cc2 100644 --- a/sway/commands/input/click_method.c +++ b/sway/commands/input/click_method.c @@ -26,6 +26,7 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) { } else if (strcasecmp(argv[0], "clickfinger") == 0) { new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock '"); } diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c index 995a2f47..73937507 100644 --- a/sway/commands/input/dwt.c +++ b/sway/commands/input/dwt.c @@ -22,6 +22,7 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) { } else if (strcasecmp(argv[0], "disabled") == 0) { new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt '"); } diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 2217f5ce..abfe3b12 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -16,7 +16,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "events", "No input device defined."); } - wlr_log(L_DEBUG, "events for device: %s", + wlr_log(WLR_DEBUG, "events for device: %s", current_input_config->identifier); struct input_config *new_config = new_input_config(current_input_config->identifier); @@ -29,6 +29,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "events", "Expected 'events '"); } diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c index 94b8e03e..769ce98c 100644 --- a/sway/commands/input/left_handed.c +++ b/sway/commands/input/left_handed.c @@ -23,6 +23,7 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) { } else if (strcasecmp(argv[0], "disabled") == 0) { new_config->left_handed = 0; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed '"); } diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index 80bb856d..40f04214 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c @@ -54,20 +54,28 @@ struct cmd_results *input_cmd_map_from_region(int argc, char **argv) { bool mm1, mm2; if (!parse_coords(argv[0], &new_config->mapped_from_region->x1, &new_config->mapped_from_region->y1, &mm1)) { + free(new_config->mapped_from_region); + free_input_config(new_config); return cmd_results_new(CMD_FAILURE, "map_from_region", "Invalid top-left coordinates"); } if (!parse_coords(argv[1], &new_config->mapped_from_region->x2, &new_config->mapped_from_region->y2, &mm2)) { + free(new_config->mapped_from_region); + free_input_config(new_config); return cmd_results_new(CMD_FAILURE, "map_from_region", "Invalid bottom-right coordinates"); } if (new_config->mapped_from_region->x1 > new_config->mapped_from_region->x2 || new_config->mapped_from_region->y1 > new_config->mapped_from_region->y2) { + free(new_config->mapped_from_region); + free_input_config(new_config); return cmd_results_new(CMD_FAILURE, "map_from_region", "Invalid rectangle"); } if (mm1 != mm2) { + free(new_config->mapped_from_region); + free_input_config(new_config); return cmd_results_new(CMD_FAILURE, "map_from_region", "Both coordinates must be in the same unit"); } diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c index a551fd51..7ca01629 100644 --- a/sway/commands/input/middle_emulation.c +++ b/sway/commands/input/middle_emulation.c @@ -24,6 +24,7 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation '"); } diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c index c4e19b78..55236790 100644 --- a/sway/commands/input/natural_scroll.c +++ b/sway/commands/input/natural_scroll.c @@ -23,6 +23,7 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { } else if (strcasecmp(argv[0], "disabled") == 0) { new_config->natural_scroll = 0; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll '"); } diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c index 171063aa..8bbd0724 100644 --- a/sway/commands/input/pointer_accel.c +++ b/sway/commands/input/pointer_accel.c @@ -20,6 +20,7 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { float pointer_accel = atof(argv[0]); if (pointer_accel < -1 || pointer_accel > 1) { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]"); } diff --git a/sway/commands/input/repeat_delay.c b/sway/commands/input/repeat_delay.c index ce265841..c9ddbf0e 100644 --- a/sway/commands/input/repeat_delay.c +++ b/sway/commands/input/repeat_delay.c @@ -20,6 +20,7 @@ struct cmd_results *input_cmd_repeat_delay(int argc, char **argv) { int repeat_delay = atoi(argv[0]); if (repeat_delay < 0) { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "repeat_delay", "Repeat delay cannot be negative"); } diff --git a/sway/commands/input/repeat_rate.c b/sway/commands/input/repeat_rate.c index f2ea2e69..56878176 100644 --- a/sway/commands/input/repeat_rate.c +++ b/sway/commands/input/repeat_rate.c @@ -20,6 +20,7 @@ struct cmd_results *input_cmd_repeat_rate(int argc, char **argv) { int repeat_rate = atoi(argv[0]); if (repeat_rate < 0) { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "repeat_rate", "Repeat rate cannot be negative"); } diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c new file mode 100644 index 00000000..350fcca2 --- /dev/null +++ b/sway/commands/input/scroll_button.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *current_input_config = + config->handler_context.input_config; + if (!current_input_config) { + return cmd_results_new(CMD_FAILURE, "scroll_button", + "No input device defined."); + } + struct input_config *new_config = + new_input_config(current_input_config->identifier); + + errno = 0; + char *endptr; + int scroll_button = strtol(*argv, &endptr, 10); + if (endptr == *argv && scroll_button == 0) { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "scroll_button", + "Scroll button identifier must be an integer."); + } + if (errno == ERANGE) { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "scroll_button", + "Scroll button identifier out of range."); + } + if (scroll_button < 0) { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "scroll_button", + "Scroll button identifier cannot be negative."); + } + new_config->scroll_button = scroll_button; + + apply_input_config(new_config); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c index 0a1c57ac..4c6ac6b6 100644 --- a/sway/commands/input/scroll_method.c +++ b/sway/commands/input/scroll_method.c @@ -27,6 +27,7 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { } else if (strcasecmp(argv[0], "on_button_down") == 0) { new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method '"); } diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c index e7f03058..a8d1a10c 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c @@ -23,11 +23,12 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) { } else if (strcasecmp(argv[0], "disabled") == 0) { new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; } else { + free_input_config(new_config); return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap '"); } - wlr_log(L_DEBUG, "apply-tap for device: %s", + wlr_log(WLR_DEBUG, "apply-tap for device: %s", current_input_config->identifier); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/tap_button_map.c b/sway/commands/input/tap_button_map.c new file mode 100644 index 00000000..bdbba472 --- /dev/null +++ b/sway/commands/input/tap_button_map.c @@ -0,0 +1,33 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *current_input_config = + config->handler_context.input_config; + if (!current_input_config) { + return cmd_results_new(CMD_FAILURE, "tap_button_map", + "No input device defined."); + } + struct input_config *new_config = + new_input_config(current_input_config->identifier); + + if (strcasecmp(argv[0], "lrm") == 0) { + new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; + } else if (strcasecmp(argv[0], "lmr") == 0) { + new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; + } else { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "tap_button_map", + "Expected 'tap_button_map '"); + } + + apply_input_config(new_config); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 867e65d3..9fa5a344 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) { new_config->xkb_layout = strdup(argv[0]); - wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s", + wlr_log(WLR_DEBUG, "apply-xkb_layout for device: %s layout: %s", current_input_config->identifier, new_config->xkb_layout); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index e8c8e04e..0d082625 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) { new_config->xkb_model = strdup(argv[0]); - wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s", + wlr_log(WLR_DEBUG, "apply-xkb_model for device: %s model: %s", current_input_config->identifier, new_config->xkb_model); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index e9ddd6e3..3059d941 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) { new_config->xkb_options = strdup(argv[0]); - wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s", + wlr_log(WLR_DEBUG, "apply-xkb_options for device: %s options: %s", current_input_config->identifier, new_config->xkb_options); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 926d0ac1..560f088e 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) { new_config->xkb_rules = strdup(argv[0]); - wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s", + wlr_log(WLR_DEBUG, "apply-xkb_rules for device: %s rules: %s", current_input_config->identifier, new_config->xkb_rules); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index 0e3ffd41..0aa03440 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c @@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) { new_config->xkb_variant = strdup(argv[0]); - wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s", + wlr_log(WLR_DEBUG, "apply-xkb_variant for device: %s variant: %s", current_input_config->identifier, new_config->xkb_variant); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 9945fa5c..c446f1f9 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { } container_notify_subtree_changed(parent); - arrange_and_commit(parent); + arrange_windows(parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/mode.c b/sway/commands/mode.c index 00331ccc..b460fcb5 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -26,7 +26,17 @@ struct cmd_results *cmd_mode(int argc, char **argv) { "mode", "Can only be used in config file."); } - const char *mode_name = argv[0]; + bool pango = strcmp(*argv, "--pango_markup") == 0; + if (pango) { + argc--; argv++; + if (argc == 0) { + return cmd_results_new(CMD_FAILURE, "mode", + "Mode name is missing"); + } + } + + char *mode_name = *argv; + strip_quotes(mode_name); struct sway_mode *mode = NULL; // Find mode for (int i = 0; i < config->modes->length; ++i) { @@ -46,6 +56,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->name = strdup(mode_name); mode->keysym_bindings = create_list(); mode->keycode_bindings = create_list(); + mode->pango = pango; list_add(config->modes, mode); } if (!mode) { @@ -54,13 +65,15 @@ struct cmd_results *cmd_mode(int argc, char **argv) { return error; } if ((config->reading && argc > 1) || (!config->reading && argc == 1)) { - wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name); + wlr_log(WLR_DEBUG, "Switching to mode `%s' (pango=%d)", + mode->name, mode->pango); } // Set current mode config->current_mode = mode; if (argc == 1) { // trigger IPC mode event - ipc_event_mode(config->current_mode->name); + ipc_event_mode(config->current_mode->name, + config->current_mode->pango); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index a4fae388..1940043d 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,13 +1,15 @@ #define _XOPEN_SOURCE 500 #include #include +#include #include #include #include #include "sway/commands.h" -#include "sway/desktop/transaction.h" +#include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/output.h" +#include "sway/scratchpad.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" @@ -103,10 +105,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, // TODO: Ideally we would arrange the surviving parent after reaping, // but container_reap_empty does not return it, so we arrange the // workspace instead. - struct sway_transaction *txn = transaction_create(); - arrange_windows(old_ws, txn); - arrange_windows(destination->parent, txn); - transaction_commit(txn); + arrange_windows(old_ws); + arrange_windows(destination->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } else if (strcasecmp(argv[1], "to") == 0 @@ -142,10 +142,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, // TODO: Ideally we would arrange the surviving parent after reaping, // but container_reap_empty does not return it, so we arrange the // workspace instead. - struct sway_transaction *txn = transaction_create(); - arrange_windows(old_ws, txn); - arrange_windows(focus->parent, txn); - transaction_commit(txn); + arrange_windows(old_ws); + arrange_windows(focus->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -175,20 +173,56 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, } container_move_to(current, destination); - struct sway_transaction *txn = transaction_create(); - arrange_windows(source, txn); - arrange_windows(destination, txn); - transaction_commit(txn); + arrange_windows(source); + arrange_windows(destination); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } static struct cmd_results *move_in_direction(struct sway_container *container, - enum movement_direction direction, int move_amt) { + enum movement_direction direction, int argc, char **argv) { + int move_amt = 10; + if (argc > 1) { + char *inv; + move_amt = (int)strtol(argv[1], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Invalid distance specified"); + } + } + if (container->type == C_WORKSPACE) { return cmd_results_new(CMD_FAILURE, "move", "Cannot move workspaces in a direction"); } + if (container_is_floating(container)) { + if (container->type == C_VIEW && container->sway_view->is_fullscreen) { + return cmd_results_new(CMD_FAILURE, "move", + "Cannot move fullscreen floating container"); + } + double lx = container->x; + double ly = container->y; + switch (direction) { + case MOVE_LEFT: + lx -= move_amt; + break; + case MOVE_RIGHT: + lx += move_amt; + break; + case MOVE_UP: + ly -= move_amt; + break; + case MOVE_DOWN: + ly += move_amt; + break; + case MOVE_PARENT: + case MOVE_CHILD: + return cmd_results_new(CMD_FAILURE, "move", + "Cannot move floating container to parent or child"); + } + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } // For simplicity, we'll arrange the entire workspace. The reason for this // is moving the container might reap the old parent, and container_move // does not return a surviving parent. @@ -198,54 +232,112 @@ static struct cmd_results *move_in_direction(struct sway_container *container, container_move(container, direction, move_amt); struct sway_container *new_ws = container_parent(container, C_WORKSPACE); - struct sway_transaction *txn = transaction_create(); - arrange_windows(old_ws, txn); + arrange_windows(old_ws); if (new_ws != old_ws) { - arrange_windows(new_ws, txn); + arrange_windows(new_ws); } - transaction_commit(txn); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static const char* expected_position_syntax = + "Expected 'move [absolute] position ' or " + "'move [absolute] position mouse'"; + +static struct cmd_results *move_to_position(struct sway_container *container, + int argc, char **argv) { + if (!container_is_floating(container)) { + return cmd_results_new(CMD_FAILURE, "move", + "Only floating containers " + "can be moved to an absolute position"); + } + if (!argc) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + if (strcmp(argv[0], "absolute") == 0) { + --argc; + ++argv; + } + if (!argc) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + if (strcmp(argv[0], "position") == 0) { + --argc; + ++argv; + } + if (!argc) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + if (strcmp(argv[0], "mouse") == 0) { + struct sway_seat *seat = config->handler_context.seat; + if (!seat->cursor) { + return cmd_results_new(CMD_FAILURE, "move", "No cursor device"); + } + double lx = seat->cursor->cursor->x - container->width / 2; + double ly = seat->cursor->cursor->y - container->height / 2; + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + if (argc != 2) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + double lx, ly; + char *inv; + lx = (double)strtol(argv[0], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Invalid position specified"); + } + ly = (double)strtol(argv[1], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Invalid position specified"); + } + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +static struct cmd_results *move_to_scratchpad(struct sway_container *con) { + if (con->type != C_CONTAINER && con->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "move", + "Only views and containers can be moved to the scratchpad"); + } + if (con->scratchpad) { + return cmd_results_new(CMD_INVALID, "move", + "Container is already in the scratchpad"); + } + scratchpad_add_container(con); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; - int move_amt = 10; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { return error; } struct sway_container *current = config->handler_context.current_container; - if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) { - char *inv; - move_amt = (int)strtol(argv[1], &inv, 10); - if (*inv != '\0' && strcasecmp(inv, "px") != 0) { - return cmd_results_new(CMD_FAILURE, "move", - "Invalid distance specified"); - } - } - if (strcasecmp(argv[0], "left") == 0) { - return move_in_direction(current, MOVE_LEFT, move_amt); + return move_in_direction(current, MOVE_LEFT, argc, argv); } else if (strcasecmp(argv[0], "right") == 0) { - return move_in_direction(current, MOVE_RIGHT, move_amt); + return move_in_direction(current, MOVE_RIGHT, argc, argv); } else if (strcasecmp(argv[0], "up") == 0) { - return move_in_direction(current, MOVE_UP, move_amt); + return move_in_direction(current, MOVE_UP, argc, argv); } else if (strcasecmp(argv[0], "down") == 0) { - return move_in_direction(current, MOVE_DOWN, move_amt); + return move_in_direction(current, MOVE_DOWN, argc, argv); } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { return cmd_move_container(current, argc, argv); } else if (strcasecmp(argv[0], "workspace") == 0) { return cmd_move_workspace(current, argc, argv); } else if (strcasecmp(argv[0], "scratchpad") == 0 - || (strcasecmp(argv[0], "to") == 0 + || (strcasecmp(argv[0], "to") == 0 && argc == 2 && strcasecmp(argv[1], "scratchpad") == 0)) { - // TODO: scratchpad - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); + return move_to_scratchpad(current); } else if (strcasecmp(argv[0], "position") == 0) { - // TODO: floating - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); + return move_to_position(current, argc, argv); + } else if (strcasecmp(argv[0], "absolute") == 0) { + return move_to_position(current, argc, argv); } else { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c new file mode 100644 index 00000000..61a8de7e --- /dev/null +++ b/sway/commands/no_focus.c @@ -0,0 +1,26 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" +#include "sway/criteria.h" +#include "list.h" +#include "log.h" + +struct cmd_results *cmd_no_focus(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) { + return error; + } + + char *err_str = NULL; + struct criteria *criteria = criteria_parse(argv[0], &err_str); + if (!criteria) { + error = cmd_results_new(CMD_INVALID, "no_focus", err_str); + free(err_str); + return error; + } + + criteria->type = CT_NO_FOCUS; + list_add(config->criteria, criteria); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/output.c b/sway/commands/output.c index f955bf90..ef1b7a69 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -29,7 +29,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { struct output_config *output = new_output_config(argv[0]); if (!output) { - wlr_log(L_ERROR, "Failed to allocate output config"); + wlr_log(WLR_ERROR, "Failed to allocate output config"); return NULL; } argc--; argv++; @@ -60,53 +60,13 @@ struct cmd_results *cmd_output(int argc, char **argv) { config->handler_context.leftovers.argc = 0; config->handler_context.leftovers.argv = NULL; - int i = list_seq_find(config->output_configs, output_name_cmp, output->name); - if (i >= 0) { - // Merge existing config - struct output_config *current = config->output_configs->items[i]; - merge_output_config(current, output); - free_output_config(output); - output = current; - } else { - list_add(config->output_configs, output); - } + output = store_output_config(output); - wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " - "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", - output->name, output->enabled, output->width, output->height, - output->refresh_rate, output->x, output->y, output->scale, - output->transform, output->background, output->background_option, output->dpms_state); - - // Try to find the output container and apply configuration now. If - // this is during startup then there will be no container and config - // will be applied during normal "new output" event from wlroots. - char identifier[128]; - bool all = strcmp(output->name, "*") == 0; - struct sway_output *sway_output; - wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { - output_get_identifier(identifier, sizeof(identifier), sway_output); - wlr_log(L_DEBUG, "Checking identifier %s", identifier); - if (all || strcmp(sway_output->wlr_output->name, output->name) == 0 - || strcmp(identifier, output->name) == 0) { - if (!sway_output->swayc) { - if (!output->enabled) { - if (!all) { - break; - } - continue; - } - - output_enable(sway_output); - } - - apply_output_config(output, sway_output->swayc); - - if (!all) { - // Stop looking if the output config isn't applicable to all - // outputs - break; - } - } + // If reloading, the output configs will be applied after reading the + // entire config and before the deferred commands so that an auto generated + // workspace name is not given to re-enabled outputs. + if (!config->reloading) { + apply_output_config_to_outputs(output); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 55cbdff0..4ed56c2a 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -72,7 +72,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { src = strdup(p.we_wordv[0]); wordfree(&p); if (!src) { - wlr_log(L_ERROR, "Failed to duplicate string"); + wlr_log(WLR_ERROR, "Failed to duplicate string"); return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate resource"); } @@ -80,9 +80,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { if (config->reading && *src != '/') { // src file is inside configuration dir - char *conf = strdup(config->current_config); - if(!conf) { - wlr_log(L_ERROR, "Failed to duplicate string"); + char *conf = strdup(config->current_config_path); + if (!conf) { + wlr_log(WLR_ERROR, "Failed to duplicate string"); + free(src); return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate resources"); } @@ -93,7 +94,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { if (!src) { free(rel_path); free(conf); - wlr_log(L_ERROR, "Unable to allocate memory"); + wlr_log(WLR_ERROR, "Unable to allocate memory"); return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate resources"); } diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c index daec6d44..ef56ae9e 100644 --- a/sway/commands/output/mode.c +++ b/sway/commands/output/mode.c @@ -36,11 +36,11 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { } } else { // Format is 1234 4321 + argc--; argv++; if (!argc) { return cmd_results_new(CMD_INVALID, "output", "Missing mode argument (height)."); } - argc--; argv++; output->height = strtol(*argv, &end, 10); if (*end) { return cmd_results_new(CMD_INVALID, "output", diff --git a/sway/commands/output/position.c b/sway/commands/output/position.c index c2aeb281..449767b1 100644 --- a/sway/commands/output/position.c +++ b/sway/commands/output/position.c @@ -27,11 +27,11 @@ struct cmd_results *output_cmd_position(int argc, char **argv) { } } else { // Format is 1234 4321 (legacy) + argc--; argv++; if (!argc) { return cmd_results_new(CMD_INVALID, "output", "Missing position argument (y)."); } - argc--; argv++; config->handler_context.output_config->y = strtol(*argv, &end, 10); if (*end) { return cmd_results_new(CMD_INVALID, "output", diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 9fc213c4..cea6a94b 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -7,11 +7,11 @@ struct cmd_results *cmd_reload(int argc, char **argv) { if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { return error; } - if (!load_main_config(config->current_config, true)) { + if (!load_main_config(config->current_config_path, true)) { return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); } load_swaybars(); - arrange_and_commit(&root_container); + arrange_windows(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 104a3392..a380ff9c 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -68,7 +68,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { "Workspace already exists"); } - wlr_log(L_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); + wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); free(workspace->name); workspace->name = new_name; diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 6357343e..e657864c 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,6 +8,7 @@ #include #include "sway/commands.h" #include "sway/tree/arrange.h" +#include "sway/tree/view.h" #include "log.h" static const int MIN_SANE_W = 100, MIN_SANE_H = 60; @@ -21,9 +23,18 @@ enum resize_unit { enum resize_axis { RESIZE_AXIS_HORIZONTAL, RESIZE_AXIS_VERTICAL, + RESIZE_AXIS_UP, + RESIZE_AXIS_DOWN, + RESIZE_AXIS_LEFT, + RESIZE_AXIS_RIGHT, RESIZE_AXIS_INVALID, }; +struct resize_amount { + int amount; + enum resize_unit unit; +}; + static enum resize_unit parse_resize_unit(const char *unit) { if (strcasecmp(unit, "px") == 0) { return RESIZE_UNIT_PX; @@ -37,6 +48,69 @@ static enum resize_unit parse_resize_unit(const char *unit) { return RESIZE_UNIT_INVALID; } +// Parse arguments such as "10", "10px" or "10 px". +// Returns the number of arguments consumed. +static int parse_resize_amount(int argc, char **argv, + struct resize_amount *amount) { + char *err; + amount->amount = (int)strtol(argv[0], &err, 10); + if (*err) { + // e.g. 10px + amount->unit = parse_resize_unit(err); + return 1; + } + if (argc == 1) { + amount->unit = RESIZE_UNIT_DEFAULT; + return 1; + } + // Try the second argument + amount->unit = parse_resize_unit(argv[1]); + if (amount->unit == RESIZE_UNIT_INVALID) { + amount->unit = RESIZE_UNIT_DEFAULT; + return 1; + } + return 2; +} + +static void calculate_constraints(int *min_width, int *max_width, + int *min_height, int *max_height) { + struct sway_container *con = config->handler_context.current_container; + + if (config->floating_minimum_width == -1) { // no minimum + *min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + *min_width = 75; + } else { + *min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + *min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + *min_height = 50; + } else { + *min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + *max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_width = ws->width; + } else { + *max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + *max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_height = ws->height; + } else { + *max_height = config->floating_maximum_height; + } +} + static enum resize_axis parse_resize_axis(const char *axis) { if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { return RESIZE_AXIS_HORIZONTAL; @@ -44,6 +118,18 @@ static enum resize_axis parse_resize_axis(const char *axis) { if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) { return RESIZE_AXIS_VERTICAL; } + if (strcasecmp(axis, "up") == 0) { + return RESIZE_AXIS_UP; + } + if (strcasecmp(axis, "down") == 0) { + return RESIZE_AXIS_DOWN; + } + if (strcasecmp(axis, "left") == 0) { + return RESIZE_AXIS_LEFT; + } + if (strcasecmp(axis, "right") == 0) { + return RESIZE_AXIS_RIGHT; + } return RESIZE_AXIS_INVALID; } @@ -95,7 +181,7 @@ static void resize_tiled(int amount, enum resize_axis axis) { return; } - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "Found the proper parent: %p. It has %d l conts, and %d r conts", parent->parent, minor_weight, major_weight); @@ -182,32 +268,286 @@ static void resize_tiled(int amount, enum resize_axis axis) { } } - arrange_and_commit(parent->parent); + arrange_windows(parent->parent); } -static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { - struct sway_container *current = config->handler_context.current_container; - if (unit == RESIZE_UNIT_DEFAULT) { - // Default for tiling; TODO floating should be px - unit = RESIZE_UNIT_PPT; +/** + * Implement `resize ` for a floating container. + */ +static struct cmd_results *resize_adjust_floating(enum resize_axis axis, + struct resize_amount *amount) { + struct sway_container *con = config->handler_context.current_container; + int grow_width = 0, grow_height = 0; + switch (axis) { + case RESIZE_AXIS_HORIZONTAL: + case RESIZE_AXIS_LEFT: + case RESIZE_AXIS_RIGHT: + grow_width = amount->amount; + break; + case RESIZE_AXIS_VERTICAL: + case RESIZE_AXIS_UP: + case RESIZE_AXIS_DOWN: + grow_height = amount->amount; + break; + case RESIZE_AXIS_INVALID: + return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); + } + // Make sure we're not adjusting beyond floating min/max size + int min_width, max_width, min_height, max_height; + calculate_constraints(&min_width, &max_width, &min_height, &max_height); + if (con->width + grow_width < min_width) { + grow_width = min_width - con->width; + } else if (con->width + grow_width > max_width) { + grow_width = max_width - con->width; + } + if (con->height + grow_height < min_height) { + grow_height = min_height - con->height; + } else if (con->height + grow_height > max_height) { + grow_height = max_height - con->height; + } + int grow_x = 0, grow_y = 0; + switch (axis) { + case RESIZE_AXIS_HORIZONTAL: + grow_x = -grow_width / 2; + break; + case RESIZE_AXIS_VERTICAL: + grow_y = -grow_height / 2; + break; + case RESIZE_AXIS_UP: + grow_y = -grow_height; + break; + case RESIZE_AXIS_LEFT: + grow_x = -grow_width; + break; + case RESIZE_AXIS_DOWN: + case RESIZE_AXIS_RIGHT: + break; + case RESIZE_AXIS_INVALID: + return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); + } + con->x += grow_x; + con->y += grow_y; + con->width += grow_width; + con->height += grow_height; + + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->x += grow_x; + view->y += grow_y; + view->width += grow_width; + view->height += grow_height; } - if (unit == RESIZE_UNIT_PPT) { - float pct = amount / 100.0f; + arrange_windows(con); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +/** + * Implement `resize ` for a tiled container. + */ +static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, + struct resize_amount *amount) { + struct sway_container *current = config->handler_context.current_container; + + if (amount->unit == RESIZE_UNIT_DEFAULT) { + amount->unit = RESIZE_UNIT_PPT; + } + if (amount->unit == RESIZE_UNIT_PPT) { + float pct = amount->amount / 100.0f; + // TODO: Make left/right/up/down resize in that direction? switch (axis) { + case RESIZE_AXIS_LEFT: + case RESIZE_AXIS_RIGHT: case RESIZE_AXIS_HORIZONTAL: - amount = (float)current->width * pct; + amount->amount = (float)current->width * pct; break; + case RESIZE_AXIS_UP: + case RESIZE_AXIS_DOWN: case RESIZE_AXIS_VERTICAL: - amount = (float)current->height * pct; + amount->amount = (float)current->height * pct; break; - default: - sway_assert(0, "invalid resize axis"); - return; + case RESIZE_AXIS_INVALID: + return cmd_results_new(CMD_INVALID, "resize", + "Invalid resize axis/direction"); } } - return resize_tiled(amount, axis); + resize_tiled(amount->amount, axis); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +/** + * Implement `resize set` for a tiled container. + */ +static struct cmd_results *resize_set_tiled(struct sway_container *con, + struct resize_amount *width, struct resize_amount *height) { + return cmd_results_new(CMD_INVALID, "resize", + "'resize set' is not implemented for tiled views"); +} + +/** + * Implement `resize set` for a floating container. + */ +static struct cmd_results *resize_set_floating(struct sway_container *con, + struct resize_amount *width, struct resize_amount *height) { + int min_width, max_width, min_height, max_height; + calculate_constraints(&min_width, &max_width, &min_height, &max_height); + width->amount = fmax(min_width, fmin(width->amount, max_width)); + height->amount = fmax(min_height, fmin(height->amount, max_height)); + int grow_width = width->amount - con->width; + int grow_height = height->amount - con->height; + con->x -= grow_width / 2; + con->y -= grow_height / 2; + con->width = width->amount; + con->height = height->amount; + + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->x -= grow_width / 2; + view->y -= grow_height / 2; + view->width += grow_width; + view->height += grow_height; + } + + arrange_windows(con); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +/** + * resize set + * + * args: [px|ppt] [px|ppt] + */ +static struct cmd_results *cmd_resize_set(int argc, char **argv) { + struct cmd_results *error; + if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { + return error; + } + const char *usage = "Expected 'resize set '"; + + // Width + struct resize_amount width; + int num_consumed_args = parse_resize_amount(argc, argv, &width); + argc -= num_consumed_args; + argv += num_consumed_args; + if (width.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + if (!argc) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + + // Height + struct resize_amount height; + num_consumed_args = parse_resize_amount(argc, argv, &height); + argc -= num_consumed_args; + argv += num_consumed_args; + if (height.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + + // If 0, don't resize that dimension + struct sway_container *con = config->handler_context.current_container; + if (width.amount <= 0) { + width.amount = con->width; + } + if (height.amount <= 0) { + height.amount = con->height; + } + + if (container_is_floating(con)) { + return resize_set_floating(con, &width, &height); + } + return resize_set_tiled(con, &width, &height); +} + +/** + * resize + * + * args: + * args: + * args: or + */ +static struct cmd_results *cmd_resize_adjust(int argc, char **argv, + int multiplier) { + const char *usage = "Expected 'resize grow|shrink " + "[ px|ppt [or px|ppt]]'"; + enum resize_axis axis = parse_resize_axis(*argv); + if (axis == RESIZE_AXIS_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + --argc; ++argv; + + // First amount + struct resize_amount first_amount; + if (argc) { + int num_consumed_args = parse_resize_amount(argc, argv, &first_amount); + argc -= num_consumed_args; + argv += num_consumed_args; + if (first_amount.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + } else { + first_amount.amount = 10; + first_amount.unit = RESIZE_UNIT_DEFAULT; + } + + // "or" + if (argc) { + if (strcmp(*argv, "or") != 0) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + --argc; ++argv; + } + + // Second amount + struct resize_amount second_amount; + if (argc) { + int num_consumed_args = parse_resize_amount(argc, argv, &second_amount); + argc -= num_consumed_args; + argv += num_consumed_args; + if (second_amount.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + } else { + second_amount.unit = RESIZE_UNIT_INVALID; + } + + first_amount.amount *= multiplier; + second_amount.amount *= multiplier; + + struct sway_container *con = config->handler_context.current_container; + if (container_is_floating(con)) { + // Floating containers can only resize in px. Choose an amount which + // uses px, with fallback to an amount that specified no unit. + if (first_amount.unit == RESIZE_UNIT_PX) { + return resize_adjust_floating(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_PX) { + return resize_adjust_floating(axis, &second_amount); + } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_floating(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_floating(axis, &second_amount); + } else { + return cmd_results_new(CMD_INVALID, "resize", + "Floating containers cannot use ppt measurements"); + } + } + + // For tiling, prefer ppt -> default -> px + if (first_amount.unit == RESIZE_UNIT_PPT) { + return resize_adjust_tiled(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_PPT) { + return resize_adjust_tiled(axis, &second_amount); + } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_tiled(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_tiled(axis, &second_amount); + } else { + return resize_adjust_tiled(axis, &first_amount); + } } struct cmd_results *cmd_resize(int argc, char **argv) { @@ -226,61 +566,17 @@ struct cmd_results *cmd_resize(int argc, char **argv) { } if (strcasecmp(argv[0], "set") == 0) { - // TODO - //return cmd_resize_set(argc - 1, &argv[1]); - return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented"); + return cmd_resize_set(argc - 1, &argv[1]); + } + if (strcasecmp(argv[0], "grow") == 0) { + return cmd_resize_adjust(argc - 1, &argv[1], 1); + } + if (strcasecmp(argv[0], "shrink") == 0) { + return cmd_resize_adjust(argc - 1, &argv[1], -1); } - - // TODO: resize grow|shrink left|right|up|down const char *usage = "Expected 'resize " - " [] [px|ppt]'"; + " [] [px|ppt]'"; - int multiplier = 0; - if (strcasecmp(*argv, "grow") == 0) { - multiplier = 1; - } else if (strcasecmp(*argv, "shrink") == 0) { - multiplier = -1; - } else { - return cmd_results_new(CMD_INVALID, "resize", usage); - } - --argc; ++argv; - - enum resize_axis axis = parse_resize_axis(*argv); - if (axis == RESIZE_AXIS_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); - } - --argc; ++argv; - - int amount = 10; // Default amount - enum resize_unit unit = RESIZE_UNIT_DEFAULT; - - if (argc) { - char *err; - amount = (int)strtol(*argv, &err, 10); - if (*err) { - // e.g. `resize grow width 10px` - unit = parse_resize_unit(err); - if (unit == RESIZE_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); - } - } - --argc; ++argv; - } - - if (argc) { - unit = parse_resize_unit(*argv); - if (unit == RESIZE_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); - } - --argc; ++argv; - } - - if (argc) { - // Provied too many args, the bastard - return cmd_results_new(CMD_INVALID, "resize", usage); - } - - resize(amount * multiplier, axis, unit); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return cmd_results_new(CMD_INVALID, "resize", usage); } diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c new file mode 100644 index 00000000..ccc07c87 --- /dev/null +++ b/sway/commands/scratchpad.c @@ -0,0 +1,36 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/scratchpad.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_scratchpad(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) { + return error; + } + if (strcmp(argv[0], "show") != 0) { + return cmd_results_new(CMD_INVALID, "scratchpad", + "Expected 'scratchpad show'"); + } + if (!root_container.sway_root->scratchpad->length) { + return cmd_results_new(CMD_INVALID, "scratchpad", + "Scratchpad is empty"); + } + + if (config->handler_context.using_criteria) { + // If using criteria, this command is executed for every container which + // matches the criteria. If this container isn't in the scratchpad, + // we'll just silently return a success. + struct sway_container *con = config->handler_context.current_container; + wlr_log(WLR_INFO, "cmd_scratchpad(%s)", con->name); + if (!con->scratchpad) { + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + scratchpad_toggle_container(con); + } else { + scratchpad_toggle_auto(); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/set.c b/sway/commands/set.c index 84e9b792..ea388d3b 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -32,7 +32,7 @@ struct cmd_results *cmd_set(int argc, char **argv) { } if (argv[0][0] != '$') { - wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); + wlr_log(WLR_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); size_t size = snprintf(NULL, 0, "$%s", argv[0]); tmp = malloc(size + 1); diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index f687e78e..7d27e571 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { "Expected 'smart_gaps ' "); } - arrange_and_commit(&root_container); + arrange_windows(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/split.c b/sway/commands/split.c index c40f4d9f..313799da 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) { } struct sway_container *parent = container_split(con, layout); container_create_notify(parent); - arrange_and_commit(parent->parent); + arrange_windows(parent->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e052058f..2fc88308 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,7 +1,6 @@ #include #include #include "sway/commands.h" -#include "sway/desktop/transaction.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" @@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) { container_swap(current, other); - struct sway_transaction *txn = transaction_create(); - arrange_windows(current->parent, txn); - + arrange_windows(current->parent); if (other->parent != current->parent) { - arrange_windows(other->parent, txn); + arrange_windows(other->parent); } - transaction_commit(txn); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/swaybg_command.c b/sway/commands/swaybg_command.c index 770d4821..36f7fdcd 100644 --- a/sway/commands/swaybg_command.c +++ b/sway/commands/swaybg_command.c @@ -13,7 +13,7 @@ struct cmd_results *cmd_swaybg_command(int argc, char **argv) { free(config->swaybg_command); } config->swaybg_command = join_args(argv, argc); - wlr_log(L_DEBUG, "Using custom swaybg command: %s", + wlr_log(WLR_DEBUG, "Using custom swaybg command: %s", config->swaybg_command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c new file mode 100644 index 00000000..d199858a --- /dev/null +++ b/sway/commands/urgent.c @@ -0,0 +1,36 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/layout.h" + +struct cmd_results *cmd_urgent(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { + return error; + } + struct sway_container *container = + config->handler_context.current_container; + if (container->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "urgent", + "Only views can be urgent"); + } + struct sway_view *view = container->sway_view; + + if (strcmp(argv[0], "enable") == 0) { + view_set_urgent(view, true); + } else if (strcmp(argv[0], "disable") == 0) { + view_set_urgent(view, false); + } else if (strcmp(argv[0], "allow") == 0) { + view->allow_request_urgent = true; + } else if (strcmp(argv[0], "deny") == 0) { + view->allow_request_urgent = false; + } else { + return cmd_results_new(CMD_INVALID, "urgent", + "Expected 'urgent '"); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index d15be571..e8b37182 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -51,7 +51,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { free(old); // workspaces can only be assigned to a single output list_del(config->workspace_outputs, i); } - wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); + wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); list_add(config->workspace_outputs, wso); } else { if (config->reading || !config->active) { diff --git a/sway/config.c b/sway/config.c index 512cab31..5f6dd7ad 100644 --- a/sway/config.c +++ b/sway/config.c @@ -24,6 +24,7 @@ #include "sway/input/seat.h" #include "sway/commands.h" #include "sway/config.h" +#include "sway/criteria.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/workspace.h" @@ -86,7 +87,12 @@ void free_config(struct sway_config *config) { } list_free(config->cmd_queue); list_free(config->workspace_outputs); - list_free(config->output_configs); + if (config->output_configs) { + for (int i = 0; i < config->output_configs->length; i++) { + free_output_config(config->output_configs->items[i]); + } + list_free(config->output_configs); + } if (config->input_configs) { for (int i = 0; i < config->input_configs->length; i++) { free_input_config(config->input_configs->items[i]); @@ -99,7 +105,12 @@ void free_config(struct sway_config *config) { } list_free(config->seat_configs); } - list_free(config->criteria); + if (config->criteria) { + for (int i = 0; i < config->criteria->length; ++i) { + criteria_destroy(config->criteria->items[i]); + } + list_free(config->criteria); + } list_free(config->no_focus); list_free(config->active_bar_modifiers); list_free(config->config_chain); @@ -111,6 +122,7 @@ void free_config(struct sway_config *config) { free(config->floating_scroll_left_cmd); free(config->floating_scroll_right_cmd); free(config->font); + free((char *)config->current_config_path); free((char *)config->current_config); free(config); } @@ -172,6 +184,7 @@ static void config_defaults(struct sway_config *config) { config->default_orientation = L_NONE; if (!(config->font = strdup("monospace 10"))) goto cleanup; config->font_height = 17; // height of monospace 10 + config->urgent_timeout = 500; // floating view config->floating_maximum_width = 0; @@ -198,6 +211,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->active_bar_modifiers = create_list())) goto cleanup; if (!(config->config_chain = create_list())) goto cleanup; + config->current_config_path = NULL; config->current_config = NULL; // borders @@ -269,12 +283,12 @@ static char *get_config_path(void) { char *home = getenv("HOME"); char *config_home = malloc(strlen(home) + strlen("/.config") + 1); if (!config_home) { - wlr_log(L_ERROR, "Unable to allocate $HOME/.config"); + wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); } else { strcpy(config_home, home); strcat(config_home, "/.config"); setenv("XDG_CONFIG_HOME", config_home, 1); - wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); + wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); free(config_home); } } @@ -297,25 +311,22 @@ static char *get_config_path(void) { return NULL; // Not reached } -const char *current_config_path; - static bool load_config(const char *path, struct sway_config *config) { - wlr_log(L_INFO, "Loading config from %s", path); - current_config_path = path; + if (path == NULL) { + wlr_log(WLR_ERROR, "Unable to find a config file!"); + return false; + } + + wlr_log(WLR_INFO, "Loading config from %s", path); struct stat sb; if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { return false; } - if (path == NULL) { - wlr_log(L_ERROR, "Unable to find a config file!"); - return false; - } - FILE *f = fopen(path, "r"); if (!f) { - wlr_log(L_ERROR, "Unable to open %s for reading", path); + wlr_log(WLR_ERROR, "Unable to open %s for reading", path); return false; } @@ -323,10 +334,9 @@ static bool load_config(const char *path, struct sway_config *config) { fclose(f); if (!config_load_success) { - wlr_log(L_ERROR, "Error(s) loading config!"); + wlr_log(WLR_ERROR, "Error(s) loading config!"); } - current_config_path = NULL; return true; } @@ -346,12 +356,13 @@ bool load_main_config(const char *file, bool is_active) { config_defaults(config); if (is_active) { - wlr_log(L_DEBUG, "Performing configuration file reload"); + wlr_log(WLR_DEBUG, "Performing configuration file reload"); config->reloading = true; config->active = true; + create_default_output_configs(); } - config->current_config = path; + config->current_config_path = path; list_add(config->config_chain, path); config->reading = true; @@ -362,7 +373,7 @@ bool load_main_config(const char *file, bool is_active) { /* DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); if (!dir) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "%s does not exist, sway will have no security configuration" " and will probably be broken", SYSCONFDIR "/sway/security.d"); } else { @@ -391,7 +402,7 @@ bool load_main_config(const char *file, bool is_active) { if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "Refusing to load %s - it must be owned by root " "and mode 644 or 444", _path); success = false; @@ -407,6 +418,9 @@ bool load_main_config(const char *file, bool is_active) { success = success && load_config(path, config); if (is_active) { + for (int i = 0; i < config->output_configs->length; i++) { + apply_output_config_to_outputs(config->output_configs->items[i]); + } config->reloading = false; } @@ -421,26 +435,28 @@ bool load_main_config(const char *file, bool is_active) { static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) { // save parent config - const char *parent_config = config->current_config; + const char *parent_config = config->current_config_path; - char *full_path = strdup(path); + char *full_path; int len = strlen(path); if (len >= 1 && path[0] != '/') { len = len + strlen(parent_dir) + 2; full_path = malloc(len * sizeof(char)); if (!full_path) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "Unable to allocate full path to included config"); return false; } snprintf(full_path, len, "%s/%s", parent_dir, path); + } else { + full_path = strdup(path); } char *real_path = realpath(full_path, NULL); free(full_path); if (real_path == NULL) { - wlr_log(L_DEBUG, "%s not found.", path); + wlr_log(WLR_DEBUG, "%s not found.", path); return false; } @@ -449,7 +465,7 @@ static bool load_include_config(const char *path, const char *parent_dir, for (j = 0; j < config->config_chain->length; ++j) { char *old_path = config->config_chain->items[j]; if (strcmp(real_path, old_path) == 0) { - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "%s already included once, won't be included again.", real_path); free(real_path); @@ -457,25 +473,25 @@ static bool load_include_config(const char *path, const char *parent_dir, } } - config->current_config = real_path; + config->current_config_path = real_path; list_add(config->config_chain, real_path); int index = config->config_chain->length - 1; if (!load_config(real_path, config)) { free(real_path); - config->current_config = parent_config; + config->current_config_path = parent_config; list_del(config->config_chain, index); return false; } - // restore current_config - config->current_config = parent_config; + // restore current_config_path + config->current_config_path = parent_config; return true; } bool load_include_configs(const char *path, struct sway_config *config) { char *wd = getcwd(NULL, 0); - char *parent_path = strdup(config->current_config); + char *parent_path = strdup(config->current_config_path); const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { @@ -503,7 +519,7 @@ bool load_include_configs(const char *path, struct sway_config *config) { // restore wd if (chdir(wd) < 0) { free(wd); - wlr_log(L_ERROR, "failed to restore working directory"); + wlr_log(WLR_ERROR, "failed to restore working directory"); return false; } @@ -518,13 +534,13 @@ static int detect_brace_on_following_line(FILE *file, char *line, char *peeked = NULL; long position = 0; do { - wlr_log(L_DEBUG, "Peeking line %d", line_number + lines + 1); + wlr_log(WLR_DEBUG, "Peeking line %d", line_number + lines + 1); free(peeked); peeked = peek_line(file, lines, &position); if (peeked) { peeked = strip_whitespace(peeked); } - wlr_log(L_DEBUG, "Peeked line: `%s`", peeked); + wlr_log(WLR_DEBUG, "Peeked line: `%s`", peeked); lines++; } while (peeked && strlen(peeked) == 0); @@ -543,7 +559,7 @@ static char *expand_line(const char *block, const char *line, bool add_brace) { + (add_brace ? 2 : 0) + 1; char *expanded = calloc(1, size); if (!expanded) { - wlr_log(L_ERROR, "Cannot allocate expanded line buffer"); + wlr_log(WLR_ERROR, "Cannot allocate expanded line buffer"); return NULL; } snprintf(expanded, size, "%s%s%s%s", block ? block : "", @@ -552,10 +568,33 @@ static char *expand_line(const char *block, const char *line, bool add_brace) { } bool read_config(FILE *file, struct sway_config *config) { + bool reading_main_config = false; + char *this_config = NULL; + size_t config_size = 0; + if (config->current_config == NULL) { + reading_main_config = true; + + int ret_seek = fseek(file, 0, SEEK_END); + long ret_tell = ftell(file); + if (ret_seek == -1 || ret_tell == -1) { + wlr_log(WLR_ERROR, "Unable to get size of config file"); + return false; + } + config_size = ret_tell; + rewind(file); + + config->current_config = this_config = calloc(1, config_size + 1); + if (this_config == NULL) { + wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents"); + return false; + } + } + bool success = true; int line_number = 0; char *line; list_t *stack = create_list(); + size_t read = 0; while (!feof(file)) { char *block = stack->length ? stack->items[0] : NULL; line = read_line(file); @@ -563,7 +602,26 @@ bool read_config(FILE *file, struct sway_config *config) { continue; } line_number++; - wlr_log(L_DEBUG, "Read line %d: %s", line_number, line); + wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line); + + if (reading_main_config) { + size_t length = strlen(line); + + if (read + length > config_size) { + wlr_log(WLR_ERROR, "Config file changed during reading"); + list_foreach(stack, free); + list_free(stack); + free(line); + return false; + } + + strcpy(this_config + read, line); + if (line_number != 1) { + this_config[read - 1] = '\n'; + } + read += length + 1; + } + line = strip_whitespace(line); if (line[0] == '#') { free(line); @@ -577,13 +635,16 @@ bool read_config(FILE *file, struct sway_config *config) { line_number); if (brace_detected > 0) { line_number += brace_detected; - wlr_log(L_DEBUG, "Detected open brace on line %d", line_number); + wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number); } char *expanded = expand_line(block, line, brace_detected > 0); if (!expanded) { + list_foreach(stack, free); + list_free(stack); + free(line); return false; } - wlr_log(L_DEBUG, "Expanded line: %s", expanded); + wlr_log(WLR_DEBUG, "Expanded line: %s", expanded); struct cmd_results *res; if (block && strcmp(block, "") == 0) { // Special case @@ -591,27 +652,26 @@ bool read_config(FILE *file, struct sway_config *config) { } else { res = config_command(expanded); } - free(expanded); switch(res->status) { case CMD_FAILURE: case CMD_INVALID: - wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, - line, res->error, config->current_config); + wlr_log(WLR_ERROR, "Error on line %i '%s': %s (%s)", line_number, + line, res->error, config->current_config_path); success = false; break; case CMD_DEFER: - wlr_log(L_DEBUG, "Deferring command `%s'", line); - list_add(config->cmd_queue, strdup(line)); + wlr_log(WLR_DEBUG, "Deferring command `%s'", line); + list_add(config->cmd_queue, strdup(expanded)); break; case CMD_BLOCK_COMMANDS: - wlr_log(L_DEBUG, "Entering commands block"); + wlr_log(WLR_DEBUG, "Entering commands block"); list_insert(stack, 0, ""); break; case CMD_BLOCK: - wlr_log(L_DEBUG, "Entering block '%s'", res->input); + wlr_log(WLR_DEBUG, "Entering block '%s'", res->input); list_insert(stack, 0, strdup(res->input)); if (strcmp(res->input, "bar") == 0) { config->current_bar = NULL; @@ -620,7 +680,7 @@ bool read_config(FILE *file, struct sway_config *config) { case CMD_BLOCK_END: if (!block) { - wlr_log(L_DEBUG, "Unmatched '}' on line %i", line_number); + wlr_log(WLR_DEBUG, "Unmatched '}' on line %i", line_number); success = false; break; } @@ -628,13 +688,14 @@ bool read_config(FILE *file, struct sway_config *config) { config->current_bar = NULL; } - wlr_log(L_DEBUG, "Exiting block '%s'", block); + wlr_log(WLR_DEBUG, "Exiting block '%s'", block); list_del(stack, 0); free(block); memset(&config->handler_context, 0, sizeof(config->handler_context)); default:; } + free(expanded); free(line); free_cmd_results(res); } @@ -671,7 +732,7 @@ char *do_var_replacement(char *str) { int vvlen = strlen(var->value); char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); if (!newstr) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "Unable to allocate replacement " "during variable expansion"); break; @@ -733,6 +794,6 @@ void config_update_font_height(bool recalculate) { } if (config->font_height != prev_max_height) { - arrange_and_commit(&root_container); + arrange_windows(&root_container); } } diff --git a/sway/config/bar.c b/sway/config/bar.c index 5a97c3cc..3a74331e 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -16,10 +16,10 @@ #include "log.h" static void terminate_swaybar(pid_t pid) { - wlr_log(L_DEBUG, "Terminating swaybar %d", pid); + wlr_log(WLR_DEBUG, "Terminating swaybar %d", pid); int ret = kill(-pid, SIGTERM); if (ret != 0) { - wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); + wlr_log_errno(WLR_ERROR, "Unable to terminate swaybar %d", pid); } else { int status; waitpid(pid, &status, 0); @@ -30,6 +30,7 @@ void free_bar_config(struct bar_config *bar) { if (!bar) { return; } + free(bar->id); free(bar->mode); free(bar->position); free(bar->hidden_state); @@ -70,16 +71,12 @@ void free_bar_config(struct bar_config *bar) { struct bar_config *default_bar_config(void) { struct bar_config *bar = NULL; - bar = malloc(sizeof(struct bar_config)); + bar = calloc(1, sizeof(struct bar_config)); if (!bar) { return NULL; } - if (!(bar->mode = strdup("dock"))) goto cleanup; - if (!(bar->hidden_state = strdup("hide"))) goto cleanup; bar->outputs = NULL; bar->position = strdup("bottom"); - if (!(bar->bindings = create_list())) goto cleanup; - if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup; bar->pango_markup = false; bar->swaybar_command = NULL; bar->font = NULL; @@ -91,6 +88,19 @@ struct bar_config *default_bar_config(void) { bar->binding_mode_indicator = true; bar->verbose = false; bar->pid = 0; + if (!(bar->mode = strdup("dock"))) { + goto cleanup; + } + if (!(bar->hidden_state = strdup("hide"))) { + goto cleanup; + } + if (!(bar->bindings = create_list())) { + goto cleanup; + } + if (!(bar->status_command = + strdup("while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done"))) { + goto cleanup; + } // set default colors if (!(bar->colors.background = strndup("#000000ff", 9))) { goto cleanup; @@ -157,7 +167,7 @@ void invoke_swaybar(struct bar_config *bar) { // Pipe to communicate errors int filedes[2]; if (pipe(filedes) == -1) { - wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar"); + wlr_log(WLR_ERROR, "Pipe setup failed! Cannot fork into bar"); return; } @@ -174,7 +184,7 @@ void invoke_swaybar(struct bar_config *bar) { if (!command) { const char msg[] = "Unable to allocate swaybar command string"; size_t msg_len = sizeof(msg); - if (write(filedes[1], &msg_len, sizeof(int))) {}; + if (write(filedes[1], &msg_len, sizeof(size_t))) {}; if (write(filedes[1], msg, msg_len)) {}; close(filedes[1]); exit(1); @@ -187,17 +197,17 @@ void invoke_swaybar(struct bar_config *bar) { execvp(cmd[0], cmd); exit(1); } - wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid); + wlr_log(WLR_DEBUG, "Spawned swaybar %d", bar->pid); close(filedes[0]); - ssize_t len; - if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) { + size_t len; + if (read(filedes[1], &len, sizeof(size_t)) == sizeof(size_t)) { char *buf = malloc(len); if(!buf) { - wlr_log(L_ERROR, "Cannot allocate error string"); + wlr_log(WLR_ERROR, "Cannot allocate error string"); return; } if (read(filedes[1], buf, len)) { - wlr_log(L_ERROR, "%s", buf); + wlr_log(WLR_ERROR, "%s", buf); } free(buf); } @@ -234,7 +244,7 @@ void load_swaybars() { if (bar->pid != 0) { terminate_swaybar(bar->pid); } - wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); + wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); invoke_swaybar(bar); } } diff --git a/sway/config/input.c b/sway/config/input.c index 17303ccc..8d687a6d 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -8,17 +8,18 @@ struct input_config *new_input_config(const char* identifier) { struct input_config *input = calloc(1, sizeof(struct input_config)); if (!input) { - wlr_log(L_DEBUG, "Unable to allocate input config"); + wlr_log(WLR_DEBUG, "Unable to allocate input config"); return NULL; } - wlr_log(L_DEBUG, "new_input_config(%s)", identifier); + wlr_log(WLR_DEBUG, "new_input_config(%s)", identifier); if (!(input->identifier = strdup(identifier))) { free(input); - wlr_log(L_DEBUG, "Unable to allocate input config"); + wlr_log(WLR_DEBUG, "Unable to allocate input config"); return NULL; } input->tap = INT_MIN; + input->tap_button_map = INT_MIN; input->drag_lock = INT_MIN; input->dwt = INT_MIN; input->send_events = INT_MIN; @@ -27,6 +28,7 @@ struct input_config *new_input_config(const char* identifier) { input->natural_scroll = INT_MIN; input->accel_profile = INT_MIN; input->pointer_accel = FLT_MIN; + input->scroll_button = INT_MIN; input->scroll_method = INT_MIN; input->left_handed = INT_MIN; input->repeat_delay = INT_MIN; @@ -70,12 +72,18 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->scroll_method != INT_MIN) { dst->scroll_method = src->scroll_method; } + if (src->scroll_button != INT_MIN) { + dst->scroll_button = src->scroll_button; + } if (src->send_events != INT_MIN) { dst->send_events = src->send_events; } if (src->tap != INT_MIN) { dst->tap = src->tap; } + if (src->tap_button_map != INT_MIN) { + dst->tap_button_map = src->tap_button_map; + } if (src->xkb_layout) { free(dst->xkb_layout); dst->xkb_layout = strdup(src->xkb_layout); @@ -112,7 +120,7 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { struct input_config *copy_input_config(struct input_config *ic) { struct input_config *copy = calloc(1, sizeof(struct input_config)); if (copy == NULL) { - wlr_log(L_ERROR, "could not allocate input config"); + wlr_log(WLR_ERROR, "could not allocate input config"); return NULL; } merge_input_config(copy, ic); diff --git a/sway/config/output.c b/sway/config/output.c index 648ded27..504c48c6 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -45,10 +45,6 @@ struct output_config *new_output_config(const char *name) { } void merge_output_config(struct output_config *dst, struct output_config *src) { - if (src->name) { - free(dst->name); - dst->name = strdup(src->name); - } if (src->enabled != -1) { dst->enabled = src->enabled; } @@ -86,11 +82,61 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { } } +static void merge_wildcard_on_all(struct output_config *wildcard) { + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *oc = config->output_configs->items[i]; + if (strcmp(wildcard->name, oc->name) != 0) { + wlr_log(WLR_DEBUG, "Merging output * config on %s", oc->name); + merge_output_config(oc, wildcard); + } + } +} + +struct output_config *store_output_config(struct output_config *oc) { + bool wildcard = strcmp(oc->name, "*") == 0; + if (wildcard) { + merge_wildcard_on_all(oc); + } + + int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); + if (i >= 0) { + wlr_log(WLR_DEBUG, "Merging on top of existing output config"); + struct output_config *current = config->output_configs->items[i]; + merge_output_config(current, oc); + free_output_config(oc); + oc = current; + } else if (!wildcard) { + wlr_log(WLR_DEBUG, "Adding non-wildcard output config"); + i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + wlr_log(WLR_DEBUG, "Merging on top of output * config"); + struct output_config *current = new_output_config(oc->name); + merge_output_config(current, config->output_configs->items[i]); + merge_output_config(current, oc); + free_output_config(oc); + oc = current; + } + list_add(config->output_configs, oc); + } else { + // New wildcard config. Just add it + wlr_log(WLR_DEBUG, "Adding output * config"); + list_add(config->output_configs, oc); + } + + wlr_log(WLR_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " + "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", + oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, + oc->x, oc->y, oc->scale, oc->transform, oc->background, + oc->background_option, oc->dpms_state); + + return oc; +} + static void set_mode(struct wlr_output *output, int width, int height, float refresh_rate) { int mhz = (int)(refresh_rate * 1000); if (wl_list_empty(&output->modes)) { - wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name); + wlr_log(WLR_DEBUG, "Assigning custom mode to %s", output->name); wlr_output_set_custom_mode(output, width, height, mhz); return; } @@ -106,9 +152,9 @@ static void set_mode(struct wlr_output *output, int width, int height, } } if (!best) { - wlr_log(L_ERROR, "Configured mode for %s not available", output->name); + wlr_log(WLR_ERROR, "Configured mode for %s not available", output->name); } else { - wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name); + wlr_log(WLR_DEBUG, "Assigning configured mode to %s", output->name); wlr_output_set_mode(output, best); } } @@ -116,7 +162,7 @@ static void set_mode(struct wlr_output *output, int width, int height, void terminate_swaybg(pid_t pid) { int ret = kill(pid, SIGTERM); if (ret != 0) { - wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid); + wlr_log(WLR_ERROR, "Unable to terminate swaybg [pid: %d]", pid); } else { int status; waitpid(pid, &status, 0); @@ -144,37 +190,27 @@ void apply_output_config(struct output_config *oc, struct sway_container *output } if (oc && oc->width > 0 && oc->height > 0) { - wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, + wlr_log(WLR_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, oc->height, oc->refresh_rate); set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); } if (oc && oc->scale > 0) { - wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale); + wlr_log(WLR_DEBUG, "Set %s scale to %f", oc->name, oc->scale); wlr_output_set_scale(wlr_output, oc->scale); } if (oc && oc->transform >= 0) { - wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform); + wlr_log(WLR_DEBUG, "Set %s transform to %d", oc->name, oc->transform); wlr_output_set_transform(wlr_output, oc->transform); } // Find position for it if (oc && (oc->x != -1 || oc->y != -1)) { - wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); + wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); } else { wlr_output_layout_add_auto(output_layout, wlr_output); } - if (!oc || !oc->background) { - // Look for a * config for background - int i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - oc = config->output_configs->items[i]; - } else { - oc = NULL; - } - } - int output_i; for (output_i = 0; output_i < root_container.children->length; ++output_i) { if (root_container.children->items[output_i] == output) { @@ -187,7 +223,7 @@ void apply_output_config(struct output_config *oc, struct sway_container *output terminate_swaybg(output->sway_output->bg_pid); } - wlr_log(L_DEBUG, "Setting background for output %d to %s", + wlr_log(WLR_DEBUG, "Setting background for output %d to %s", output_i, oc->background); size_t len = snprintf(NULL, 0, "%s %d %s %s", @@ -195,28 +231,30 @@ void apply_output_config(struct output_config *oc, struct sway_container *output output_i, oc->background, oc->background_option); char *command = malloc(len + 1); if (!command) { - wlr_log(L_DEBUG, "Unable to allocate swaybg command"); + wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); return; } snprintf(command, len + 1, "%s %d %s %s", config->swaybg_command ? config->swaybg_command : "swaybg", output_i, oc->background, oc->background_option); - wlr_log(L_DEBUG, "-> %s", command); + wlr_log(WLR_DEBUG, "-> %s", command); char *const cmd[] = { "sh", "-c", command, NULL }; output->sway_output->bg_pid = fork(); if (output->sway_output->bg_pid == 0) { execvp(cmd[0], cmd); + } else { + free(command); } } if (oc && oc->dpms_state != DPMS_IGNORE) { switch (oc->dpms_state) { case DPMS_ON: - wlr_log(L_DEBUG, "Turning on screen"); + wlr_log(WLR_DEBUG, "Turning on screen"); wlr_output_enable(wlr_output, true); break; case DPMS_OFF: - wlr_log(L_DEBUG, "Turning off screen"); + wlr_log(WLR_DEBUG, "Turning off screen"); wlr_output_enable(wlr_output, false); break; case DPMS_IGNORE: @@ -225,6 +263,60 @@ void apply_output_config(struct output_config *oc, struct sway_container *output } } +static struct output_config *get_output_config(char *name, char *identifier) { + int i = list_seq_find(config->output_configs, output_name_cmp, name); + if (i >= 0) { + return config->output_configs->items[i]; + } + + i = list_seq_find(config->output_configs, output_name_cmp, identifier); + if (i >= 0) { + return config->output_configs->items[i]; + } + + return NULL; +} + +void apply_output_config_to_outputs(struct output_config *oc) { + // Try to find the output container and apply configuration now. If + // this is during startup then there will be no container and config + // will be applied during normal "new output" event from wlroots. + bool wildcard = strcmp(oc->name, "*") == 0; + char id[128]; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { + char *name = sway_output->wlr_output->name; + output_get_identifier(id, sizeof(id), sway_output); + if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { + if (!sway_output->swayc) { + if (!oc->enabled) { + if (!wildcard) { + break; + } + continue; + } + + output_enable(sway_output); + } + + struct output_config *current = oc; + if (wildcard) { + struct output_config *tmp = get_output_config(name, id); + if (tmp) { + current = tmp; + } + } + apply_output_config(current, sway_output->swayc); + + if (!wildcard) { + // Stop looking if the output config isn't applicable to all + // outputs + break; + } + } + } +} + void free_output_config(struct output_config *oc) { if (!oc) { return; @@ -234,3 +326,29 @@ void free_output_config(struct output_config *oc) { free(oc->background_option); free(oc); } + +static void default_output_config(struct output_config *oc, + struct wlr_output *wlr_output) { + oc->enabled = 1; + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + oc->width = mode->width; + oc->height = mode->height; + oc->refresh_rate = mode->refresh; + } + oc->x = oc->y = -1; + oc->scale = 1; + oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; +} + +void create_default_output_configs(void) { + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { + char *name = sway_output->wlr_output->name; + struct output_config *oc = new_output_config(name); + default_output_config(oc, sway_output->wlr_output); + list_add(config->output_configs, oc); + } +} + diff --git a/sway/config/seat.c b/sway/config/seat.c index bd8b45c8..83dac4c0 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -7,11 +7,11 @@ struct seat_config *new_seat_config(const char* name) { struct seat_config *seat = calloc(1, sizeof(struct seat_config)); if (!seat) { - wlr_log(L_DEBUG, "Unable to allocate seat config"); + wlr_log(WLR_DEBUG, "Unable to allocate seat config"); return NULL; } - wlr_log(L_DEBUG, "new_seat_config(%s)", name); + wlr_log(WLR_DEBUG, "new_seat_config(%s)", name); seat->name = strdup(name); if (!sway_assert(seat->name, "could not allocate name for seat")) { free(seat); @@ -34,7 +34,7 @@ struct seat_attachment_config *seat_attachment_config_new() { struct seat_attachment_config *attachment = calloc(1, sizeof(struct seat_attachment_config)); if (!attachment) { - wlr_log(L_DEBUG, "cannot allocate attachment config"); + wlr_log(WLR_DEBUG, "cannot allocate attachment config"); return NULL; } return attachment; diff --git a/sway/criteria.c b/sway/criteria.c index d9f09ecc..c2e9c07e 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -37,7 +37,7 @@ void criteria_destroy(struct criteria *criteria) { pcre_free(criteria->con_mark); pcre_free(criteria->window_role); free(criteria->workspace); - + free(criteria->cmdlist); free(criteria->raw); free(criteria); } @@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) { return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); } +static int cmp_urgent(const void *_a, const void *_b) { + struct sway_view *a = *(void **)_a; + struct sway_view *b = *(void **)_b; + + if (a->urgent.tv_sec < b->urgent.tv_sec) { + return -1; + } else if (a->urgent.tv_sec > b->urgent.tv_sec) { + return 1; + } + if (a->urgent.tv_nsec < b->urgent.tv_nsec) { + return -1; + } else if (a->urgent.tv_nsec > b->urgent.tv_nsec) { + return 1; + } + return 0; +} + +static void find_urgent_iterator(struct sway_container *swayc, void *data) { + if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { + return; + } + list_t *urgent_views = data; + list_add(urgent_views, swayc->sway_view); +} + static bool criteria_matches_view(struct criteria *criteria, struct sway_view *view) { if (criteria->title) { @@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria, } if (criteria->urgent) { - // TODO - return false; + if (!view_is_urgent(view)) { + return false; + } + list_t *urgent_views = create_list(); + container_for_each_descendant_dfs(&root_container, + find_urgent_iterator, urgent_views); + list_stable_sort(urgent_views, cmp_urgent); + struct sway_view *target; + if (criteria->urgent == 'o') { // oldest + target = urgent_views->items[0]; + } else { // latest + target = urgent_views->items[urgent_views->length - 1]; + } + list_free(urgent_views); + if (view != target) { + return false; + } } if (criteria->workspace) { @@ -185,6 +225,15 @@ list_t *criteria_get_views(struct criteria *criteria) { }; container_for_each_descendant_dfs(&root_container, criteria_get_views_iterator, &data); + + // Scratchpad items which are hidden are not in the tree. + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *con = + root_container.sway_root->scratchpad->items[i]; + if (!con->parent) { + criteria_get_views_iterator(con, &data); + } + } return matches; } @@ -507,7 +556,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } unescape(value); } - wlr_log(L_DEBUG, "Found pair: %s=%s", name, value); + wlr_log(WLR_DEBUG, "Found pair: %s=%s", name, value); if (!parse_token(criteria, name, value)) { *error_arg = error; goto cleanup; diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index e495790c..6575519d 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, } } } + +void desktop_damage_whole_container(struct sway_container *con) { + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *cont = root_container.children->items[i]; + if (cont->type == C_OUTPUT) { + output_damage_whole_container(cont->sway_output, con); + } + } +} diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c new file mode 100644 index 00000000..da17d0f2 --- /dev/null +++ b/sway/desktop/idle_inhibit_v1.c @@ -0,0 +1,80 @@ +#include +#include +#include "log.h" +#include "sway/desktop/idle_inhibit_v1.h" +#include "sway/tree/view.h" +#include "sway/server.h" + + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_idle_inhibitor_v1 *inhibitor = + wl_container_of(listener, inhibitor, destroy); + wlr_log(WLR_DEBUG, "Sway idle inhibitor destroyed"); + wl_list_remove(&inhibitor->link); + wl_list_remove(&inhibitor->destroy.link); + idle_inhibit_v1_check_active(inhibitor->manager); + free(inhibitor); +} + +void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { + struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data; + struct sway_idle_inhibit_manager_v1 *manager = + wl_container_of(listener, manager, new_idle_inhibitor_v1); + wlr_log(WLR_DEBUG, "New sway idle inhibitor"); + + struct sway_idle_inhibitor_v1 *inhibitor = + calloc(1, sizeof(struct sway_idle_inhibitor_v1)); + if (!inhibitor) { + return; + } + + inhibitor->manager = manager; + inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); + wl_list_insert(&manager->inhibitors, &inhibitor->link); + + + inhibitor->destroy.notify = handle_destroy; + wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); + + idle_inhibit_v1_check_active(manager); +} + +void idle_inhibit_v1_check_active( + struct sway_idle_inhibit_manager_v1 *manager) { + struct sway_idle_inhibitor_v1 *inhibitor; + bool inhibited = false; + wl_list_for_each(inhibitor, &manager->inhibitors, link) { + if (!inhibitor->view) { + /* Cannot guess if view is visible so assume it is */ + inhibited = true; + break; + } + if (view_is_visible(inhibitor->view)) { + inhibited = true; + break; + } + } + wlr_idle_set_enabled(manager->idle, NULL, !inhibited); +} + +struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( + struct wl_display *wl_display, struct wlr_idle *idle) { + struct sway_idle_inhibit_manager_v1 *manager = + calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); + if (!manager) { + return NULL; + } + + manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); + if (!manager->wlr_manager) { + free(manager); + return NULL; + } + manager->idle = idle; + wl_signal_add(&manager->wlr_manager->events.new_inhibitor, + &manager->new_idle_inhibitor_v1); + manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; + wl_list_init(&manager->inhibitors); + + return manager; +} diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index de1fe349..a7d96717 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -12,7 +12,6 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "log.h" @@ -174,9 +173,9 @@ void arrange_layers(struct sway_output *output) { if (memcmp(&usable_area, &output->usable_area, sizeof(struct wlr_box)) != 0) { - wlr_log(L_DEBUG, "Usable area changed, rearranging output"); + wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - arrange_and_commit(output->swayc); + container_set_dirty(output->swayc); } // Arrange non-exlusive surfaces from top->bottom @@ -269,7 +268,7 @@ static void unmap(struct sway_layer_surface *sway_layer) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of(listener, sway_layer, destroy); - wlr_log(L_DEBUG, "Layer surface destroyed (%s)", + wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)", sway_layer->layer_surface->namespace); if (sway_layer->layer_surface->mapped) { unmap(sway_layer); @@ -316,7 +315,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct wlr_layer_surface *layer_surface = data; struct sway_server *server = wl_container_of(listener, server, layer_shell_surface); - wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " + 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, @@ -326,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->client_pending.margin.bottom, layer_surface->client_pending.margin.left); - struct sway_layer_surface *sway_layer = - calloc(1, sizeof(struct sway_layer_surface)); - if (!sway_layer) { - return; - } - if (!layer_surface->output) { // Assign last active output struct sway_container *output = NULL; @@ -353,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->output = output->sway_output->wlr_output; } + struct sway_layer_surface *sway_layer = + calloc(1, sizeof(struct sway_layer_surface)); + if (!sway_layer) { + return; + } + sway_layer->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, &sway_layer->surface_commit); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1e7494b3..a206ac6b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -56,33 +56,15 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, *sy = ry + ph/2 - sh/2; } -/** - * Contains a surface's root geometry information. For instance, when rendering - * a popup, this will contain the parent view's position and size. - */ -struct root_geometry { - double x, y; - int width, height; - float rotation; -}; - -struct render_data { - struct root_geometry root_geo; - struct sway_output *output; - pixman_region32_t *damage; - struct sway_view *view; - float alpha; -}; - -static bool get_surface_box(struct root_geometry *geo, +bool output_get_surface_box(struct root_geometry *geo, struct sway_output *output, struct wlr_surface *surface, int sx, int sy, struct wlr_box *surface_box) { if (!wlr_surface_has_buffer(surface)) { return false; } - int sw = surface->current->width; - int sh = surface->current->height; + int sw = surface->current.width; + int sh = surface->current.height; double _sx = sx, _sy = sy; rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, @@ -110,24 +92,23 @@ static bool get_surface_box(struct root_geometry *geo, return wlr_box_intersection(&output_box, &rotated_box, &intersection); } -static void surface_for_each_surface(struct wlr_surface *surface, +void output_surface_for_each_surface(struct wlr_surface *surface, double ox, double oy, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { geo->x = ox; geo->y = oy; - geo->width = surface->current->width; - geo->height = surface->current->height; + geo->width = surface->current.width; + geo->height = surface->current.height; geo->rotation = 0; wlr_surface_for_each_surface(surface, iterator, user_data); } -static void output_view_for_each_surface(struct sway_view *view, - struct root_geometry *geo, wlr_surface_iterator_func_t iterator, - void *user_data) { - struct render_data *data = user_data; - geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; - geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y; +void output_view_for_each_surface(struct sway_view *view, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data) { + geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x; + geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y; geo->width = view->swayc->current.view_width; geo->height = view->swayc->current.view_height; geo->rotation = 0; // TODO @@ -135,20 +116,20 @@ static void output_view_for_each_surface(struct sway_view *view, view_for_each_surface(view, iterator, user_data); } -static void layer_for_each_surface(struct wl_list *layer_surfaces, +void output_layer_for_each_surface(struct wl_list *layer_surfaces, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct sway_layer_surface *layer_surface; wl_list_for_each(layer_surface, layer_surfaces, link) { struct wlr_layer_surface *wlr_layer_surface = layer_surface->layer_surface; - surface_for_each_surface(wlr_layer_surface->surface, + output_surface_for_each_surface(wlr_layer_surface->surface, layer_surface->geo.x, layer_surface->geo.y, geo, iterator, user_data); } } -static void unmanaged_for_each_surface(struct wl_list *unmanaged, +void output_unmanaged_for_each_surface(struct wl_list *unmanaged, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct sway_xwayland_unmanaged *unmanaged_surface; @@ -158,12 +139,12 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged, double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; - surface_for_each_surface(xsurface->surface, ox, oy, geo, + output_surface_for_each_surface(xsurface->surface, ox, oy, geo, iterator, user_data); } } -static void drag_icons_for_each_surface(struct wl_list *drag_icons, +void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct sway_drag_icon *drag_icon; @@ -172,7 +153,7 @@ static void drag_icons_for_each_surface(struct wl_list *drag_icons, double oy = drag_icon->y - output->swayc->y; if (drag_icon->wlr_drag_icon->mapped) { - surface_for_each_surface(drag_icon->wlr_drag_icon->surface, + output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface, ox, oy, geo, iterator, user_data); } } @@ -185,722 +166,7 @@ static void scale_box(struct wlr_box *box, float scale) { box->height *= scale; } -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, transform, ow, oh, &box); - - 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 alpha) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, box->x, box->y, - box->width, box->height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto 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); - } - -damage_finish: - pixman_region32_fini(&damage); -} - -static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, - void *_data) { - struct render_data *data = _data; - struct wlr_output *wlr_output = data->output->wlr_output; - float rotation = data->root_geo.rotation; - 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; - bool intersects = get_surface_box(&data->root_geo, data->output, surface, - sx, sy, &box); - if (!intersects) { - return; - } - - 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, alpha); -} - -static void render_layer(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - layer_for_each_surface(layer_surfaces, &data.root_geo, - render_surface_iterator, &data); -} - -static void render_unmanaged(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *unmanaged) { - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - unmanaged_for_each_surface(unmanaged, output, &data.root_geo, - render_surface_iterator, &data); -} - -static void render_drag_icons(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *drag_icons) { - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - drag_icons_for_each_surface(drag_icons, output, &data.root_geo, - render_surface_iterator, &data); -} - -static void render_rect(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, const struct wlr_box *_box, - float color[static 4]) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= wlr_output->lx * wlr_output->scale; - box.y -= wlr_output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto 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_rect(renderer, &box, color, - wlr_output->transform_matrix); - } - -damage_finish: - pixman_region32_fini(&damage); -} - -static void premultiply_alpha(float color[4], float opacity) { - color[3] *= opacity; - color[0] *= color[3]; - color[1] *= color[3]; - color[2] *= color[3]; -} - -static void render_view_surfaces(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct render_data data = { - .output = output, - .damage = damage, - .view = view, - .alpha = alpha, - }; - output_view_for_each_surface( - view, &data.root_geo, render_surface_iterator, &data); -} - -static void render_saved_view(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct wlr_output *wlr_output = output->wlr_output; - - int width, height; - struct wlr_texture *texture = - transaction_get_saved_texture(view, &width, &height); - if (!texture) { - return; - } - struct wlr_box box = { - .x = view->swayc->current.view_x - output->swayc->current.swayc_x, - .y = view->swayc->current.view_y - output->swayc->current.swayc_y, - .width = width, - .height = height, - }; - - struct wlr_box output_box = { - .width = output->swayc->current.swayc_width, - .height = output->swayc->current.swayc_height, - }; - - struct wlr_box intersection; - bool intersects = wlr_box_intersection(&output_box, &box, &intersection); - if (!intersects) { - return; - } - - scale_box(&box, wlr_output->scale); - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); - - render_texture(wlr_output, damage, texture, &box, matrix, alpha); -} - -/** - * Render a view's surface and left/bottom/right borders. - */ -static void render_view(struct sway_output *output, pixman_region32_t *damage, - struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->sway_view; - if (view->swayc->instructions->length) { - render_saved_view(view, output, damage, view->swayc->alpha); - } else { - render_view_surfaces(view, output, damage, view->swayc->alpha); - } - - struct wlr_box box; - float output_scale = output->wlr_output->scale; - float color[4]; - struct sway_container_state *state = &con->current; - - if (state->border != B_NONE) { - if (state->border_left) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; - box.y = state->view_y; - box.width = state->border_thickness; - box.height = state->view_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - - if (state->border_right) { - if (state->parent->current.children->length == 1 - && state->parent->current.layout == L_HORIZ) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = state->view_x + state->view_width; - box.y = state->view_y; - box.width = state->border_thickness; - box.height = state->view_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - - if (state->border_bottom) { - if (state->parent->current.children->length == 1 - && con->current.parent->current.layout == L_VERT) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; - box.y = state->view_y + state->view_height; - box.width = state->swayc_width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - } -} - -/** - * Render a titlebar. - * - * Care must be taken not to render over the same pixel multiple times, - * otherwise the colors will be incorrect when using opacity. - * - * The height is: 1px border, 3px padding, font height, 3px padding, 1px border - * The left side for L_TABBED is: 1px border, 2px padding, title - * The left side for other layouts is: 3px padding, title - */ -static void render_titlebar(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, - int x, int y, int width, - struct border_colors *colors, struct wlr_texture *title_texture, - struct wlr_texture *marks_texture) { - struct wlr_box box; - float color[4]; - struct sway_container_state *state = &con->current; - float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = state->parent->current.layout; - list_t *children = state->parent->current.children; - bool is_last_child = children->items[children->length - 1] == con; - double output_x = output->swayc->current.swayc_x; - double output_y = output->swayc->current.swayc_y; - - // Single pixel bar above title - memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x; - box.y = y; - box.width = width; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Single pixel bar below title - size_t left_offset = 0, right_offset = 0; - bool connects_sides = false; - if (layout == L_HORIZ || layout == L_VERT || - (layout == L_STACKED && is_last_child)) { - if (con->type == C_VIEW) { - left_offset = state->border_left * state->border_thickness; - right_offset = state->border_right * state->border_thickness; - connects_sides = true; - } - } - box.x = x + left_offset; - box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = width - left_offset - right_offset; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - if (layout == L_TABBED) { - // Single pixel left edge - box.x = x; - box.y = y + TITLEBAR_BORDER_THICKNESS; - box.width = TITLEBAR_BORDER_THICKNESS; - box.height = - container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Single pixel right edge - box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; - render_rect(output->wlr_output, output_damage, &box, color); - } - - size_t inner_width = width - TITLEBAR_H_PADDING * 2; - - // Marks - size_t marks_width = 0; - if (config->show_marks && marks_texture) { - struct wlr_box texture_box; - wlr_texture_get_size(marks_texture, - &texture_box.width, &texture_box.height); - texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) - * output_scale - texture_box.width; - texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (inner_width * output_scale < texture_box.width) { - texture_box.width = inner_width * output_scale; - } - render_texture(output->wlr_output, output_damage, marks_texture, - &texture_box, matrix, con->alpha); - marks_width = texture_box.width; - } - - // Title text - size_t title_width = 0; - if (title_texture) { - struct wlr_box texture_box; - wlr_texture_get_size(title_texture, - &texture_box.width, &texture_box.height); - texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; - texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (inner_width * output_scale - marks_width < texture_box.width) { - texture_box.width = inner_width * output_scale - marks_width; - } - render_texture(output->wlr_output, output_damage, title_texture, - &texture_box, matrix, con->alpha); - title_width = texture_box.width; - } - - // Padding above title - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; - box.y = y + TITLEBAR_BORDER_THICKNESS; - box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; - box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Padding below title - box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; - render_rect(output->wlr_output, output_damage, &box, color); - - // Filler between title and marks - box.width = inner_width * output_scale - title_width - marks_width; - if (box.width > 0) { - box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width; - box.y = (y + TITLEBAR_V_PADDING) * output_scale; - box.height = config->font_height * output_scale; - render_rect(output->wlr_output, output_damage, &box, color); - } - - // Padding left of title - left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; - box.x = x + left_offset; - box.y = y + TITLEBAR_V_PADDING; - box.width = TITLEBAR_H_PADDING - left_offset; - box.height = config->font_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Padding right of marks - right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; - box.x = x + width - TITLEBAR_H_PADDING; - box.y = y + TITLEBAR_V_PADDING; - box.width = TITLEBAR_H_PADDING - right_offset; - box.height = config->font_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - if (connects_sides) { - // Left pixel in line with bottom bar - box.x = x; - box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = state->border_thickness * state->border_left; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Right pixel in line with bottom bar - box.x = x + width - state->border_thickness * state->border_right; - box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = state->border_thickness * state->border_right; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } -} - -/** - * Render the top border line for a view using "border pixel". - */ -static void render_top_border(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, - struct border_colors *colors) { - struct sway_container_state *state = &con->current; - if (!state->border_top) { - return; - } - struct wlr_box box; - float color[4]; - float output_scale = output->wlr_output->scale; - - // Child border - top edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; - box.y = state->swayc_y; - box.width = state->swayc_width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); -} - -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool parent_focused); - -/** - * Render a container's children using a L_HORIZ or L_VERT layout. - * - * Wrap child views in borders and leave child containers borderless because - * they'll apply their own borders to their children. - */ -static void render_container_simple(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - - for (int i = 0; i < con->current.children->length; ++i) { - struct sway_container *child = con->current.children->items[i]; - - if (child->type == C_VIEW) { - struct sway_view *view = child->sway_view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - struct sway_container_state *state = &child->current; - - if (focus == child || parent_focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = view->marks_focused; - } else if (seat_get_focus_inactive(seat, con) == child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = view->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = view->marks_unfocused; - } - - if (state->border == B_NORMAL) { - render_titlebar(output, damage, child, state->swayc_x, - state->swayc_y, state->swayc_width, colors, - title_texture, marks_texture); - } else { - render_top_border(output, damage, child, colors); - } - render_view(output, damage, child, colors); - } else { - render_container(output, damage, child, - parent_focused || focus == child); - } - } -} - -/** - * Render a container's children using the L_TABBED layout. - */ -static void render_container_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - if (!con->current.children->length) { - return; - } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = &config->border_colors.unfocused; - struct sway_container_state *pstate = &con->current; - - // Render tabs - for (int i = 0; i < con->current.children->length; ++i) { - struct sway_container *child = con->current.children->items[i]; - struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (focus == child || parent_focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = view ? view->marks_focused : NULL; - } else if (child == current) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = view ? view->marks_focused_inactive : NULL; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = view ? view->marks_unfocused : NULL; - } - - int tab_width = pstate->swayc_width / pstate->children->length; - int x = pstate->swayc_x + tab_width * i; - // Make last tab use the remaining width of the parent - if (i == pstate->children->length - 1) { - tab_width = pstate->swayc_width - tab_width * i; - } - - render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, - colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current) { - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); - } - } -} - -/** - * Render a container's children using the L_STACKED layout. - */ -static void render_container_stacked(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - if (!con->current.children->length) { - return; - } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = &config->border_colors.unfocused; - struct sway_container_state *pstate = &con->current; - - // Render titles - for (int i = 0; i < con->current.children->length; ++i) { - struct sway_container *child = con->current.children->items[i]; - struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (focus == child || parent_focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = view ? view->marks_focused : NULL; - } else if (child == current) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = view ? view->marks_focused_inactive : NULL; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = view ? view->marks_unfocused : NULL; - } - - int y = pstate->swayc_y + container_titlebar_height() * i; - render_titlebar(output, damage, child, cstate->swayc_x, y, - cstate->swayc_width, colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current) { - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); - } - } -} - -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - switch (con->current.layout) { - case L_NONE: - case L_HORIZ: - case L_VERT: - render_container_simple(output, damage, con, parent_focused); - break; - case L_STACKED: - render_container_stacked(output, damage, con, parent_focused); - break; - case L_TABBED: - render_container_tabbed(output, damage, con, parent_focused); - break; - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); - } -} - -static void render_floating_container(struct sway_output *soutput, - pixman_region32_t *damage, struct sway_container *con) { - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (focus == con) { - colors = &config->border_colors.focused; - title_texture = con->title_focused; - marks_texture = view->marks_focused; - } else { - colors = &config->border_colors.unfocused; - title_texture = con->title_unfocused; - marks_texture = view->marks_unfocused; - } - - if (con->current.border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->current.swayc_x, - con->current.swayc_y, con->current.swayc_width, colors, - title_texture, marks_texture); - } else if (con->current.border != B_NONE) { - render_top_border(soutput, damage, con, colors); - } - render_view(soutput, damage, con, colors); - } else { - render_container(soutput, damage, con, false); - } -} - -static void render_floating(struct sway_output *soutput, - pixman_region32_t *damage) { - for (int i = 0; i < root_container.current.children->length; ++i) { - struct sway_container *output = - root_container.current.children->items[i]; - for (int j = 0; j < output->current.children->length; ++j) { - struct sway_container *ws = output->current.children->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - list_t *floating = - ws->current.ws_floating->current.children; - for (int k = 0; k < floating->length; ++k) { - struct sway_container *floater = floating->items[k]; - render_floating_container(soutput, damage, floater); - } - } - } -} - -static struct sway_container *output_get_active_workspace( - struct sway_output *output) { +struct sway_container *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus_inactive(seat, output->swayc); @@ -915,117 +181,58 @@ static struct sway_container *output_get_active_workspace( return workspace; } -static void render_output(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); - if (!sway_assert(renderer != NULL, - "expected the output backend to have a renderer")) { - return; +bool output_has_opaque_lockscreen(struct sway_output *output, + struct sway_seat *seat) { + if (!seat->exclusive_client) { + return false; } - wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); - - bool damage_whole_before_swap = false; - if (!pixman_region32_not_empty(damage)) { - // Output isn't damaged but needs buffer swap - goto renderer_end; - } - - const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG"); - if (damage_debug != NULL) { - if (strcmp(damage_debug, "highlight") == 0) { - wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); - damage_whole_before_swap = true; - } else if (strcmp(damage_debug, "rerender") == 0) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(damage, damage, 0, 0, width, height); + struct wlr_layer_surface *wlr_layer_surface; + wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) { + if (wlr_layer_surface->output != output->wlr_output) { + continue; + } + struct wlr_surface *wlr_surface = wlr_layer_surface->surface; + if (wlr_surface->resource->client != seat->exclusive_client) { + continue; + } + struct sway_layer_surface *sway_layer_surface = + layer_from_wlr_layer_surface(wlr_layer_surface); + pixman_box32_t output_box = { + .x2 = output->swayc->current.swayc_width, + .y2 = output->swayc->current.swayc_height, + }; + pixman_region32_t surface_opaque_box; + pixman_region32_init(&surface_opaque_box); + pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); + pixman_region32_translate(&surface_opaque_box, + sway_layer_surface->geo.x, sway_layer_surface->geo.y); + bool contains = pixman_region32_contains_rectangle(&surface_opaque_box, + &output_box); + pixman_region32_fini(&surface_opaque_box); + if (contains) { + return true; } } - - struct sway_container *workspace = output_get_active_workspace(output); - struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; - - if (fullscreen_view) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - 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_renderer_clear(renderer, clear_color); - } - - // TODO: handle views smaller than the output - render_view_surfaces(fullscreen_view, output, damage, 1.0f); - - if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { - render_unmanaged(output, damage, - &root_container.sway_root->xwayland_unmanaged); - } - } else { - float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; - - 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_renderer_clear(renderer, clear_color); - } - - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - render_container(output, damage, workspace, focus == workspace); - render_floating(output, damage); - - render_unmanaged(output, damage, - &root_container.sway_root->xwayland_unmanaged); - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - } - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(output, damage, &root_container.sway_root->drag_icons); - -renderer_end: - if (root_container.sway_root->debug_tree) { - wlr_render_texture(renderer, root_container.sway_root->debug_tree, - wlr_output->transform_matrix, 0, 0, 1); - } - - if (damage_whole_before_swap || root_container.sway_root->debug_tree) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(damage, damage, 0, 0, width, height); - } - - wlr_renderer_scissor(renderer, NULL); - wlr_renderer_end(renderer); - if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { - return; - } - output->last_frame = *when; + return false; } struct send_frame_done_data { struct root_geometry root_geo; struct sway_output *output; struct timespec *when; + struct wl_client *exclusive_client; }; static void send_frame_done_iterator(struct wlr_surface *surface, int sx, int sy, void *_data) { struct send_frame_done_data *data = _data; + if (data->exclusive_client && + data->exclusive_client != surface->resource->client) { + return; + } - bool intersects = get_surface_box(&data->root_geo, data->output, surface, + bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, sx, sy, NULL); if (intersects) { wlr_surface_send_frame_done(surface, data->when); @@ -1034,19 +241,19 @@ static void send_frame_done_iterator(struct wlr_surface *surface, static void send_frame_done_layer(struct send_frame_done_data *data, struct wl_list *layer_surfaces) { - layer_for_each_surface(layer_surfaces, &data->root_geo, + output_layer_for_each_surface(layer_surfaces, &data->root_geo, send_frame_done_iterator, data); } static void send_frame_done_unmanaged(struct send_frame_done_data *data, struct wl_list *unmanaged) { - unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, + output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, send_frame_done_iterator, data); } static void send_frame_done_drag_icons(struct send_frame_done_data *data, struct wl_list *drag_icons) { - drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, + output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, send_frame_done_iterator, data); } @@ -1061,7 +268,7 @@ static void send_frame_done_container_iterator(struct sway_container *con, return; } - output_view_for_each_surface(con->sway_view, &data->root_geo, + output_view_for_each_surface(con->sway_view, data->output, &data->root_geo, send_frame_done_iterator, data); } @@ -1072,9 +279,12 @@ static void send_frame_done_container(struct send_frame_done_data *data, } static void send_frame_done(struct sway_output *output, struct timespec *when) { + struct sway_seat *seat = input_manager_current_seat(input_manager); struct send_frame_done_data data = { .output = output, .when = when, + .exclusive_client = output_has_opaque_lockscreen(output, seat) ? + seat->exclusive_client : NULL, }; struct sway_container *workspace = output_get_active_workspace(output); @@ -1125,7 +335,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) { } if (needs_swap) { - render_output(output, &now, &damage); + output_render(output, &now, &damage); } pixman_region32_fini(&damage); @@ -1152,7 +362,7 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, bool whole = data->whole; struct wlr_box box; - bool intersects = get_surface_box(&data->root_geo, data->output, surface, + bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, sx, sy, &box); if (!intersects) { return; @@ -1163,16 +373,22 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, int center_x = box.x + box.width/2; int center_y = box.y + box.height/2; - if (pixman_region32_not_empty(&surface->current->surface_damage)) { + if (pixman_region32_not_empty(&surface->buffer_damage)) { + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + pixman_region32_t damage; pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->surface_damage); - wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceil(output->wlr_output->scale) > surface->current->scale) { + pixman_region32_copy(&damage, &surface->buffer_damage); + wlr_region_transform(&damage, &damage, transform, + surface->current.buffer_width, surface->current.buffer_height); + wlr_region_scale(&damage, &damage, + output->wlr_output->scale / (float)surface->current.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); + ceil(output->wlr_output->scale) - surface->current.scale); } pixman_region32_translate(&damage, box.x, box.y); wlr_region_rotated_bounds(&damage, &damage, rotation, @@ -1196,7 +412,7 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, .whole = whole, }; - surface_for_each_surface(surface, ox, oy, &data.root_geo, + output_surface_for_each_surface(surface, ox, oy, &data.root_geo, damage_surface_iterator, &data); } @@ -1215,7 +431,7 @@ static void output_damage_view(struct sway_output *output, .whole = whole, }; - output_view_for_each_surface(view, &data.root_geo, + output_view_for_each_surface(view, output, &data.root_geo, damage_surface_iterator, &data); } @@ -1247,11 +463,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { + // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.swayc_x - output->wlr_output->lx, - .y = con->current.swayc_y - output->wlr_output->ly, - .width = con->current.swayc_width, - .height = con->current.swayc_height, + .x = con->current.swayc_x - output->wlr_output->lx - 1, + .y = con->current.swayc_y - output->wlr_output->ly - 1, + .width = con->current.swayc_width + 2, + .height = con->current.swayc_height + 2, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); @@ -1276,19 +493,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) { output->wlr_output->data = NULL; free(output); - arrange_and_commit(&root_container); + arrange_windows(&root_container); } static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); arrange_layers(output); - arrange_and_commit(output->swayc); + arrange_windows(output->swayc); + transaction_commit_dirty(); } static void handle_transform(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, transform); arrange_layers(output); - arrange_and_commit(output->swayc); + arrange_windows(output->swayc); + transaction_commit_dirty(); } static void handle_scale_iterator(struct sway_container *view, void *data) { @@ -1299,7 +518,8 @@ static void handle_scale(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, scale); arrange_layers(output); container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); - arrange_and_commit(output->swayc); + arrange_windows(output->swayc); + transaction_commit_dirty(); } struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { @@ -1309,7 +529,7 @@ struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { void handle_new_output(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; - wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); + wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); struct sway_output *output = calloc(1, sizeof(struct sway_output)); if (!output) { @@ -1368,5 +588,6 @@ void output_enable(struct sway_output *output) { output->damage_destroy.notify = damage_handle_destroy; arrange_layers(output); - arrange_and_commit(&root_container); + arrange_windows(&root_container); + transaction_commit_dirty(); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c new file mode 100644 index 00000000..7da54594 --- /dev/null +++ b/sway/desktop/render.c @@ -0,0 +1,919 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "sway/config.h" +#include "sway/debug.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" +#include "sway/layers.h" +#include "sway/output.h" +#include "sway/server.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/layout.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" + +struct render_data { + struct root_geometry root_geo; + struct sway_output *output; + pixman_region32_t *damage; + struct sway_view *view; + float alpha; +}; + +static void scale_box(struct wlr_box *box, float scale) { + box->x *= scale; + box->y *= scale; + box->width *= scale; + box->height *= scale; +} + +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, transform, ow, oh, &box); + + 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 alpha) { + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect(&damage, &damage, box->x, box->y, + box->width, box->height); + pixman_region32_intersect(&damage, &damage, output_damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto 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); + } + +damage_finish: + pixman_region32_fini(&damage); +} + +static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, + void *_data) { + struct render_data *data = _data; + struct wlr_output *wlr_output = data->output->wlr_output; + float rotation = data->root_geo.rotation; + 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; + bool intersects = output_get_surface_box(&data->root_geo, data->output, + surface, sx, sy, &box); + if (!intersects) { + return; + } + + 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, alpha); +} + +static void render_layer(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *layer_surfaces) { + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_layer_for_each_surface(layer_surfaces, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_unmanaged(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *unmanaged) { + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_drag_icons(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *drag_icons) { + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_rect(struct wlr_output *wlr_output, + pixman_region32_t *output_damage, const struct wlr_box *_box, + float color[static 4]) { + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + + struct wlr_box box; + memcpy(&box, _box, sizeof(struct wlr_box)); + box.x -= wlr_output->lx * wlr_output->scale; + box.y -= wlr_output->ly * wlr_output->scale; + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect(&damage, &damage, box.x, box.y, + box.width, box.height); + pixman_region32_intersect(&damage, &damage, output_damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto 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_rect(renderer, &box, color, + wlr_output->transform_matrix); + } + +damage_finish: + pixman_region32_fini(&damage); +} + +static void premultiply_alpha(float color[4], float opacity) { + color[3] *= opacity; + color[0] *= color[3]; + color[1] *= color[3]; + color[2] *= color[3]; +} + +static void render_view_surfaces(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct render_data data = { + .output = output, + .damage = damage, + .view = view, + .alpha = alpha, + }; + output_view_for_each_surface(view, output, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_saved_view(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct wlr_output *wlr_output = output->wlr_output; + + int width, height; + struct wlr_texture *texture = + transaction_get_saved_texture(view, &width, &height); + if (!texture) { + return; + } + struct wlr_box box = { + .x = view->swayc->current.view_x - output->swayc->current.swayc_x, + .y = view->swayc->current.view_y - output->swayc->current.swayc_y, + .width = width, + .height = height, + }; + + struct wlr_box output_box = { + .width = output->swayc->current.swayc_width, + .height = output->swayc->current.swayc_height, + }; + + struct wlr_box intersection; + bool intersects = wlr_box_intersection(&output_box, &box, &intersection); + if (!intersects) { + return; + } + + scale_box(&box, wlr_output->scale); + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + wlr_output->transform_matrix); + + render_texture(wlr_output, damage, texture, &box, matrix, alpha); +} + +/** + * Render a view's surface and left/bottom/right borders. + */ +static void render_view(struct sway_output *output, pixman_region32_t *damage, + struct sway_container *con, struct border_colors *colors) { + struct sway_view *view = con->sway_view; + if (view->swayc->instructions->length) { + render_saved_view(view, output, damage, view->swayc->alpha); + } else { + render_view_surfaces(view, output, damage, view->swayc->alpha); + } + + if (view->using_csd) { + return; + } + + struct wlr_box box; + float output_scale = output->wlr_output->scale; + float color[4]; + struct sway_container_state *state = &con->current; + + if (state->border != B_NONE) { + if (state->border_left) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = state->swayc_x; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (state->border_right) { + if (state->parent->current.children->length == 1 + && state->parent->current.layout == L_HORIZ) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = state->view_x + state->view_width; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (state->border_bottom) { + if (state->parent->current.children->length == 1 + && con->current.parent->current.layout == L_VERT) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = state->swayc_x; + box.y = state->view_y + state->view_height; + box.width = state->swayc_width; + box.height = state->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + } +} + +/** + * Render a titlebar. + * + * Care must be taken not to render over the same pixel multiple times, + * otherwise the colors will be incorrect when using opacity. + * + * The height is: 1px border, 3px padding, font height, 3px padding, 1px border + * The left side for L_TABBED is: 1px border, 2px padding, title + * The left side for other layouts is: 3px padding, title + */ +static void render_titlebar(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + int x, int y, int width, + struct border_colors *colors, struct wlr_texture *title_texture, + struct wlr_texture *marks_texture) { + struct wlr_box box; + float color[4]; + struct sway_container_state *state = &con->current; + float output_scale = output->wlr_output->scale; + enum sway_container_layout layout = state->parent->current.layout; + list_t *children = state->parent->current.children; + bool is_last_child = children->items[children->length - 1] == con; + double output_x = output->swayc->current.swayc_x; + double output_y = output->swayc->current.swayc_y; + + // Single pixel bar above title + memcpy(&color, colors->border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = x; + box.y = y; + box.width = width; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel bar below title + size_t left_offset = 0, right_offset = 0; + bool connects_sides = false; + if (layout == L_HORIZ || layout == L_VERT || + (layout == L_STACKED && is_last_child)) { + if (con->type == C_VIEW) { + left_offset = state->border_left * state->border_thickness; + right_offset = state->border_right * state->border_thickness; + connects_sides = true; + } + } + box.x = x + left_offset; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; + box.width = width - left_offset - right_offset; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + if (layout == L_TABBED) { + // Single pixel left edge + box.x = x; + box.y = y + TITLEBAR_BORDER_THICKNESS; + box.width = TITLEBAR_BORDER_THICKNESS; + box.height = + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel right edge + box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; + render_rect(output->wlr_output, output_damage, &box, color); + } + + size_t inner_width = width - TITLEBAR_H_PADDING * 2; + + // Marks + size_t marks_ob_width = 0; // output-buffer-local + if (config->show_marks && marks_texture) { + struct wlr_box texture_box; + wlr_texture_get_size(marks_texture, + &texture_box.width, &texture_box.height); + texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) + * output_scale - texture_box.width; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + if (inner_width * output_scale < texture_box.width) { + texture_box.width = inner_width * output_scale; + } + render_texture(output->wlr_output, output_damage, marks_texture, + &texture_box, matrix, con->alpha); + marks_ob_width = texture_box.width; + + // Gap between the marks and bottom padding, for when the marks texture + // height is smaller than the config's font height + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x; + box.y = texture_box.y + texture_box.height; + box.width = texture_box.width; + box.height = config->font_height * output_scale - texture_box.height; + if (box.height > 0) { + render_rect(output->wlr_output, output_damage, &box, color); + } + } + + // Title text + size_t title_ob_width = 0; // output-buffer-local + if (title_texture) { + struct wlr_box texture_box; + wlr_texture_get_size(title_texture, + &texture_box.width, &texture_box.height); + texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + if (inner_width * output_scale - marks_ob_width < texture_box.width) { + texture_box.width = inner_width * output_scale - marks_ob_width; + } + render_texture(output->wlr_output, output_damage, title_texture, + &texture_box, matrix, con->alpha); + title_ob_width = texture_box.width; + + // Gap between the title and bottom padding, for when the title texture + // height is smaller than the config's font height + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x; + box.y = texture_box.y + texture_box.height; + box.width = texture_box.width; + box.height = config->font_height * output_scale - texture_box.height; + if (box.height > 0) { + render_rect(output->wlr_output, output_damage, &box, color); + } + } + + // Padding above title + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.y = y + TITLEBAR_BORDER_THICKNESS; + box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; + box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Padding below title + box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; + render_rect(output->wlr_output, output_damage, &box, color); + + // Filler between title and marks + box.width = inner_width * output_scale - title_ob_width - marks_ob_width; + if (box.width > 0) { + box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; + box.y = (y + TITLEBAR_V_PADDING) * output_scale; + box.height = config->font_height * output_scale; + render_rect(output->wlr_output, output_damage, &box, color); + } + + // Padding left of title + left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.x = x + left_offset; + box.y = y + TITLEBAR_V_PADDING; + box.width = TITLEBAR_H_PADDING - left_offset; + box.height = config->font_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Padding right of marks + right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.x = x + width - TITLEBAR_H_PADDING; + box.y = y + TITLEBAR_V_PADDING; + box.width = TITLEBAR_H_PADDING - right_offset; + box.height = config->font_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + if (connects_sides) { + // Left pixel in line with bottom bar + box.x = x; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; + box.width = state->border_thickness * state->border_left; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Right pixel in line with bottom bar + box.x = x + width - state->border_thickness * state->border_right; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; + box.width = state->border_thickness * state->border_right; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + } +} + +/** + * Render the top border line for a view using "border pixel". + */ +static void render_top_border(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + struct border_colors *colors) { + struct sway_container_state *state = &con->current; + if (!state->border_top) { + return; + } + struct wlr_box box; + float color[4]; + float output_scale = output->wlr_output->scale; + + // Child border - top edge + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = state->swayc_x; + box.y = state->swayc_y; + box.width = state->swayc_width; + box.height = state->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); +} + +static void render_container(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, bool parent_focused); + +/** + * Render a container's children using a L_HORIZ or L_VERT layout. + * + * Wrap child views in borders and leave child containers borderless because + * they'll apply their own borders to their children. + */ +static void render_container_simple(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + + if (child->type == C_VIEW) { + struct sway_view *view = child->sway_view; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + struct sway_container_state *state = &child->current; + + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view->marks_urgent; + } else if (state->focused || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = view->marks_focused; + } else if (con->current.focused_inactive_child == child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = view->marks_focused_inactive; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = view->marks_unfocused; + } + + if (!view->using_csd) { + if (state->border == B_NORMAL) { + render_titlebar(output, damage, child, state->swayc_x, + state->swayc_y, state->swayc_width, colors, + title_texture, marks_texture); + } else { + render_top_border(output, damage, child, colors); + } + } + render_view(output, damage, child, colors); + } else { + render_container(output, damage, child, + parent_focused || child->current.focused); + } + } +} + +/** + * Render a container's children using the L_TABBED layout. + */ +static void render_container_tabbed(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + if (!con->current.children->length) { + return; + } + struct sway_container_state *pstate = &con->current; + struct sway_container *current = pstate->focused_inactive_child; + struct border_colors *current_colors = &config->border_colors.unfocused; + + double width_gap_adjustment = 2 * pstate->current_gaps; + int tab_width = + (pstate->swayc_width - width_gap_adjustment) / pstate->children->length; + + // Render tabs + for (int i = 0; i < pstate->children->length; ++i) { + struct sway_container *child = pstate->children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + bool urgent = view ? + view_is_urgent(view) : container_has_urgent_child(child); + + if (urgent) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view ? view->marks_urgent : NULL; + } else if (cstate->focused || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = view ? view->marks_focused : NULL; + } else if (child == pstate->focused_inactive_child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = view ? view->marks_focused_inactive : NULL; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = view ? view->marks_unfocused : NULL; + } + + int x = cstate->swayc_x + tab_width * i; + + // Make last tab use the remaining width of the parent + if (i == pstate->children->length - 1) { + tab_width = + pstate->swayc_width - width_gap_adjustment - tab_width * i; + } + + render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, + colors, title_texture, marks_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current->current.focused); + } +} + +/** + * Render a container's children using the L_STACKED layout. + */ +static void render_container_stacked(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + if (!con->current.children->length) { + return; + } + struct sway_container_state *pstate = &con->current; + struct sway_container *current = pstate->focused_inactive_child; + struct border_colors *current_colors = &config->border_colors.unfocused; + + size_t titlebar_height = container_titlebar_height(); + + // Render titles + for (int i = 0; i < pstate->children->length; ++i) { + struct sway_container *child = pstate->children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + bool urgent = view ? + view_is_urgent(view) : container_has_urgent_child(child); + + if (urgent) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view ? view->marks_urgent : NULL; + } else if (cstate->focused || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = view ? view->marks_focused : NULL; + } else if (child == pstate->focused_inactive_child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = view ? view->marks_focused_inactive : NULL; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = view ? view->marks_unfocused : NULL; + } + + int y = cstate->swayc_y + titlebar_height * i; + render_titlebar(output, damage, child, cstate->swayc_x, y, + cstate->swayc_width, colors, title_texture, marks_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current->current.focused); + } +} + +static void render_container(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + switch (con->current.layout) { + case L_NONE: + case L_HORIZ: + case L_VERT: + render_container_simple(output, damage, con, parent_focused); + break; + case L_STACKED: + render_container_stacked(output, damage, con, parent_focused); + break; + case L_TABBED: + render_container_tabbed(output, damage, con, parent_focused); + break; + case L_FLOATING: + sway_assert(false, "Didn't expect to see floating here"); + } +} + +static void render_floating_container(struct sway_output *soutput, + pixman_region32_t *damage, struct sway_container *con) { + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = con->title_urgent; + marks_texture = view->marks_urgent; + } else if (con->current.focused) { + colors = &config->border_colors.focused; + title_texture = con->title_focused; + marks_texture = view->marks_focused; + } else { + colors = &config->border_colors.unfocused; + title_texture = con->title_unfocused; + marks_texture = view->marks_unfocused; + } + + if (!view->using_csd) { + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.swayc_x, + con->current.swayc_y, con->current.swayc_width, colors, + title_texture, marks_texture); + } else if (con->current.border != B_NONE) { + render_top_border(soutput, damage, con, colors); + } + } + render_view(soutput, damage, con, colors); + } else { + render_container(soutput, damage, con, false); + } +} + +static void render_floating(struct sway_output *soutput, + pixman_region32_t *damage) { + for (int i = 0; i < root_container.current.children->length; ++i) { + struct sway_container *output = + root_container.current.children->items[i]; + for (int j = 0; j < output->current.children->length; ++j) { + struct sway_container *ws = output->current.children->items[j]; + if (!workspace_is_visible(ws)) { + continue; + } + list_t *floating = + ws->current.ws_floating->current.children; + for (int k = 0; k < floating->length; ++k) { + struct sway_container *floater = floating->items[k]; + render_floating_container(soutput, damage, floater); + } + } + } +} + +const char *damage_debug = NULL; + +void output_render(struct sway_output *output, struct timespec *when, + pixman_region32_t *damage) { + struct wlr_output *wlr_output = output->wlr_output; + + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + if (!sway_assert(renderer != NULL, + "expected the output backend to have a renderer")) { + return; + } + + wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); + + bool damage_whole_before_swap = false; + if (!pixman_region32_not_empty(damage)) { + // Output isn't damaged but needs buffer swap + goto renderer_end; + } + + if (damage_debug != NULL) { + if (strcmp(damage_debug, "highlight") == 0) { + wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); + damage_whole_before_swap = true; + } else if (strcmp(damage_debug, "rerender") == 0) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(damage, damage, 0, 0, width, height); + } + } + + struct sway_container *workspace = output_get_active_workspace(output); + struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; + struct sway_seat *seat = input_manager_current_seat(input_manager); + + if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) { + struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; + struct sway_layer_surface *sway_layer_surface = + layer_from_wlr_layer_surface(seat->focused_layer); + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_surface_for_each_surface(wlr_layer_surface->surface, + sway_layer_surface->geo.x, sway_layer_surface->geo.y, + &data.root_geo, render_surface_iterator, &data); + } else if (fullscreen_view) { + float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + + 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_renderer_clear(renderer, clear_color); + } + + // TODO: handle views smaller than the output + if (fullscreen_view->swayc->instructions->length) { + render_saved_view(fullscreen_view, output, damage, 1.0f); + } else { + render_view_surfaces(fullscreen_view, output, damage, 1.0f); + } + + if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { + render_unmanaged(output, damage, + &root_container.sway_root->xwayland_unmanaged); + } + } else { + float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; + + 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_renderer_clear(renderer, clear_color); + } + + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + + render_container(output, damage, workspace, workspace->current.focused); + render_floating(output, damage); + + render_unmanaged(output, damage, + &root_container.sway_root->xwayland_unmanaged); + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + } + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + render_drag_icons(output, damage, &root_container.sway_root->drag_icons); + +renderer_end: + if (root_container.sway_root->debug_tree) { + wlr_render_texture(renderer, root_container.sway_root->debug_tree, + wlr_output->transform_matrix, 0, 0, 1); + } + + if (damage_whole_before_swap || root_container.sway_root->debug_tree) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(damage, damage, 0, 0, width, height); + } + + wlr_renderer_scissor(renderer, NULL); + wlr_renderer_end(renderer); + if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { + return; + } + output->last_frame = *when; +} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d2932c87..2a89880a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -4,8 +4,8 @@ #include #include #include -#include #include "sway/debug.h" +#include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #include "sway/output.h" #include "sway/tree/container.h" @@ -18,14 +18,14 @@ * How long we should wait for views to respond to the configure before giving * up and applying the transaction anyway. */ -#define TIMEOUT_MS 200 +int txn_timeout_ms = 200; /** * If enabled, sway will always wait for the transaction timeout before * applying it, rather than applying it when the views are ready. This allows us * to observe the rendered state while a transaction is in progress. */ -#define TRANSACTION_DEBUG false +bool txn_debug = false; struct sway_transaction { struct wl_event_source *timer; @@ -46,7 +46,7 @@ struct sway_transaction_instruction { bool ready; }; -struct sway_transaction *transaction_create() { +static struct sway_transaction *transaction_create() { struct sway_transaction *transaction = calloc(1, sizeof(struct sway_transaction)); transaction->instructions = create_list(); @@ -72,8 +72,8 @@ static void save_view_buffer(struct sway_view *view, } if (view->surface && wlr_surface_has_buffer(view->surface)) { instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); - instruction->saved_buffer_width = view->surface->current->width; - instruction->saved_buffer_height = view->surface->current->height; + instruction->saved_buffer_width = view->surface->current.width; + instruction->saved_buffer_height = view->surface->current.height; } } @@ -138,25 +138,18 @@ static void copy_pending_state(struct sway_container *container, state->children = create_list(); list_cat(state->children, container->children); } + + struct sway_seat *seat = input_manager_current_seat(input_manager); + state->focused = seat_get_focus(seat) == container; + + if (container->type != C_VIEW) { + state->focused_inactive_child = + seat_get_active_child(seat, container); + } } -static bool transaction_has_container(struct sway_transaction *transaction, +static void transaction_add_container(struct sway_transaction *transaction, struct sway_container *container) { - for (int i = 0; i < transaction->instructions->length; ++i) { - struct sway_transaction_instruction *instruction = - transaction->instructions->items[i]; - if (instruction->container == container) { - return true; - } - } - return false; -} - -void transaction_add_container(struct sway_transaction *transaction, - struct sway_container *container) { - if (transaction_has_container(transaction, container)) { - return; - } struct sway_transaction_instruction *instruction = calloc(1, sizeof(struct sway_transaction_instruction)); instruction->transaction = transaction; @@ -174,7 +167,7 @@ void transaction_add_container(struct sway_transaction *transaction, * Apply a transaction to the "current" state of the tree. */ static void transaction_apply(struct sway_transaction *transaction) { - wlr_log(L_DEBUG, "Applying transaction %p", transaction); + wlr_log(WLR_DEBUG, "Applying transaction %p", transaction); if (server.debug_txn_timings) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -185,9 +178,9 @@ static void transaction_apply(struct sway_transaction *transaction) { float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + (now.tv_nsec - commit->tv_nsec) / 1000000.0; float ms_total = ms_arranging + ms_waiting; - wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " - "%.1fms total (%.1f frames if 60Hz)", transaction, - ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); + wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " + "%.1fms total (%.1f frames if 60Hz)", transaction, + ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60)); } // Apply the instruction state to the container's current state @@ -209,10 +202,12 @@ static void transaction_apply(struct sway_transaction *transaction) { .width = instruction->state.swayc_width, .height = instruction->state.swayc_height, }; - for (int j = 0; j < root_container.children->length; ++j) { - struct sway_container *output = root_container.children->items[j]; - output_damage_box(output->sway_output, &old_box); - output_damage_box(output->sway_output, &new_box); + for (int j = 0; j < root_container.current.children->length; ++j) { + struct sway_container *output = root_container.current.children->items[j]; + if (output->sway_output) { + output_damage_box(output->sway_output, &old_box); + output_damage_box(output->sway_output, &new_box); + } } // There are separate children lists for each instruction state, the @@ -227,29 +222,22 @@ static void transaction_apply(struct sway_transaction *transaction) { } } -/** - * For simplicity, we only progress the queue if it can be completely flushed. - */ static void transaction_progress_queue() { - // We iterate this list in reverse because we're more likely to find a - // waiting transactions at the end of the list. - for (int i = server.transactions->length - 1; i >= 0; --i) { - struct sway_transaction *transaction = server.transactions->items[i]; + while (server.transactions->length) { + struct sway_transaction *transaction = server.transactions->items[0]; if (transaction->num_waiting) { return; } - } - for (int i = 0; i < server.transactions->length; ++i) { - struct sway_transaction *transaction = server.transactions->items[i]; transaction_apply(transaction); transaction_destroy(transaction); + list_del(server.transactions, 0); } - server.transactions->length = 0; + idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); } static int handle_timeout(void *data) { struct sway_transaction *transaction = data; - wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", + wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)", transaction, transaction->num_waiting); transaction->num_waiting = 0; transaction_progress_queue(); @@ -283,8 +271,8 @@ static bool should_configure(struct sway_container *con, return true; } -void transaction_commit(struct sway_transaction *transaction) { - wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", +static void transaction_commit(struct sway_transaction *transaction) { + wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions", transaction, transaction->instructions->length); transaction->num_waiting = 0; for (int i = 0; i < transaction->instructions->length; ++i) { @@ -317,9 +305,10 @@ void transaction_commit(struct sway_transaction *transaction) { } else { // There are no other transactions in progress, and this one has nothing // to wait for, so we can skip the queue. - wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); + wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction); transaction_apply(transaction); transaction_destroy(transaction); + idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); return; } @@ -327,7 +316,7 @@ void transaction_commit(struct sway_transaction *transaction) { // Set up a timer which the views must respond within transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, handle_timeout, transaction); - wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + wl_event_source_timer_update(transaction->timer, txn_timeout_ms); } // The debug tree shows the pending/live tree. Here is a good place to @@ -347,7 +336,7 @@ static void set_instruction_ready( struct timespec *start = &transaction->commit_time; float ms = (now.tv_sec - start->tv_sec) * 1000 + (now.tv_nsec - start->tv_nsec) / 1000000.0; - wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", + wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", transaction, transaction->num_configures - transaction->num_waiting + 1, transaction->num_configures, ms, @@ -358,11 +347,11 @@ static void set_instruction_ready( // If all views are ready, apply the transaction. // If the transaction has timed out then its num_waiting will be 0 already. if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { -#if !TRANSACTION_DEBUG - wlr_log(L_DEBUG, "Transaction %p is ready", transaction); - wl_event_source_timer_update(transaction->timer, 0); - transaction_progress_queue(); -#endif + if (!txn_debug) { + wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); + wl_event_source_timer_update(transaction->timer, 0); + transaction_progress_queue(); + } } } @@ -374,7 +363,9 @@ static void set_instructions_ready(struct sway_view *view, int index) { for (int i = 0; i <= index; ++i) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; - set_instruction_ready(instruction); + if (!instruction->ready) { + set_instruction_ready(instruction); + } } } @@ -413,3 +404,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, *height = instruction->saved_buffer_height; return instruction->saved_buffer->texture; } + +void transaction_commit_dirty(void) { + if (!server.dirty_containers->length) { + return; + } + struct sway_transaction *transaction = transaction_create(); + for (int i = 0; i < server.dirty_containers->length; ++i) { + struct sway_container *container = server.dirty_containers->items[i]; + transaction_add_container(transaction, container); + container->dirty = false; + } + server.dirty_containers->length = 0; + transaction_commit(transaction); +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 47604c31..f3e4fef8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include @@ -45,6 +46,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { view_child_destroy(&popup->child); } +static void popup_unconstrain(struct sway_xdg_popup *popup) { + struct sway_view *view = popup->child.view; + struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; + + struct sway_container *output = container_parent(view->swayc, C_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->x - view->x, + .y = output->y - view->y, + .width = output->width, + .height = output->height, + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} + static struct sway_xdg_popup *popup_create( struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; @@ -55,12 +74,15 @@ static struct sway_xdg_popup *popup_create( return NULL; } view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); + popup->wlr_xdg_surface = xdg_surface; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; + popup_unconstrain(popup); + return popup; } @@ -74,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( return (struct sway_xdg_shell_view *)view; } +static void get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + struct wlr_xdg_toplevel_state *state = + &view->wlr_xdg_surface->toplevel->current; + *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; + *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; + *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; + *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; +} + static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_view_from_view(view) == NULL) { return NULL; @@ -167,6 +199,7 @@ static void destroy(struct sway_view *view) { } static const struct sway_view_impl view_impl = { + .get_constraints = get_constraints, .get_string_prop = get_string_prop, .configure = configure, .set_activated = set_activated, @@ -192,10 +225,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { transaction_notify_view_ready(view, xdg_surface->configure_serial); } - view_update_title(view, false); view_damage_from(view); } +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, set_title); + struct sway_view *view = &xdg_shell_view->view; + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_app_id(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, set_app_id); + struct sway_view *view = &xdg_shell_view->view; + view_execute_criteria(view); +} + static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); @@ -222,8 +269,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + arrange_windows(output); + transaction_commit_dirty(); +} + +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_move); + struct sway_view *view = &xdg_shell_view->view; + if (!container_is_floating(view->swayc)) { + return; + } + struct wlr_xdg_toplevel_move_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + if (e->serial == seat->last_button_serial) { + seat_begin_move(seat, view->swayc, seat->last_button); + } +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_resize); + struct sway_view *view = &xdg_shell_view->view; + if (!container_is_floating(view->swayc)) { + return; + } + struct wlr_xdg_toplevel_resize_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + if (e->serial == seat->last_button_serial) { + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + } } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -240,6 +316,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link); + wl_list_remove(&xdg_shell_view->request_move.link); + wl_list_remove(&xdg_shell_view->request_resize.link); + wl_list_remove(&xdg_shell_view->set_title.link); + wl_list_remove(&xdg_shell_view->set_app_id.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -251,8 +331,8 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_width = view->wlr_xdg_surface->geometry.width; view->natural_height = view->wlr_xdg_surface->geometry.height; if (!view->natural_width && !view->natural_height) { - view->natural_width = view->wlr_xdg_surface->surface->current->width; - view->natural_height = view->wlr_xdg_surface->surface->current->height; + view->natural_width = view->wlr_xdg_surface->surface->current.width; + view->natural_height = view->wlr_xdg_surface->surface->current.height; } view_map(view, view->wlr_xdg_surface->surface); @@ -260,10 +340,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + arrange_windows(ws); } else { - arrange_and_commit(view->swayc->parent); + arrange_windows(view->swayc->parent); } + transaction_commit_dirty(); xdg_shell_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, @@ -276,6 +357,22 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); + + xdg_shell_view->request_move.notify = handle_request_move; + wl_signal_add(&xdg_surface->toplevel->events.request_move, + &xdg_shell_view->request_move); + + xdg_shell_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xdg_surface->toplevel->events.request_resize, + &xdg_shell_view->request_resize); + + xdg_shell_view->set_title.notify = handle_set_title; + wl_signal_add(&xdg_surface->toplevel->events.set_title, + &xdg_shell_view->set_title); + + xdg_shell_view->set_app_id.notify = handle_set_app_id; + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + &xdg_shell_view->set_app_id); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -304,11 +401,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { struct wlr_xdg_surface *xdg_surface = data; if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - wlr_log(L_DEBUG, "New xdg_shell popup"); + wlr_log(WLR_DEBUG, "New xdg_shell popup"); return; } - wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", + wlr_log(WLR_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); wlr_xdg_surface_ping(xdg_surface); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index b28c4b9c..46fd4769 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include @@ -44,6 +45,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { view_child_destroy(&popup->child); } +static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { + struct sway_view *view = popup->child.view; + struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; + + struct sway_container *output = container_parent(view->swayc, C_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->x - view->x, + .y = output->y - view->y, + .width = output->width, + .height = output->height, + }; + + wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} + static struct sway_xdg_popup_v6 *popup_create( struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; @@ -54,12 +73,15 @@ static struct sway_xdg_popup_v6 *popup_create( return NULL; } view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); + popup->wlr_xdg_surface_v6 = xdg_surface; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; + popup_unconstrain(popup); + return popup; } @@ -73,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( return (struct sway_xdg_shell_v6_view *)view; } +static void get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + struct wlr_xdg_toplevel_v6_state *state = + &view->wlr_xdg_surface_v6->toplevel->current; + *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; + *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; + *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; + *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; +} + static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_v6_view_from_view(view) == NULL) { return NULL; @@ -163,6 +195,7 @@ static void destroy(struct sway_view *view) { } static const struct sway_view_impl view_impl = { + .get_constraints = get_constraints, .get_string_prop = get_string_prop, .configure = configure, .set_activated = set_activated, @@ -187,10 +220,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); } - view_update_title(view, false); view_damage_from(view); } +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_title); + struct sway_view *view = &xdg_shell_v6_view->view; + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_app_id(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_app_id); + struct sway_view *view = &xdg_shell_v6_view->view; + view_execute_criteria(view); +} + static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, new_popup); @@ -217,8 +264,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + arrange_windows(output); + transaction_commit_dirty(); +} + +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_move); + struct sway_view *view = &xdg_shell_v6_view->view; + if (!container_is_floating(view->swayc)) { + return; + } + struct wlr_xdg_toplevel_v6_move_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + if (e->serial == seat->last_button_serial) { + seat_begin_move(seat, view->swayc, seat->last_button); + } +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_resize); + struct sway_view *view = &xdg_shell_v6_view->view; + if (!container_is_floating(view->swayc)) { + return; + } + struct wlr_xdg_toplevel_v6_resize_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + if (e->serial == seat->last_button_serial) { + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + } } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -235,6 +311,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); + wl_list_remove(&xdg_shell_v6_view->request_move.link); + wl_list_remove(&xdg_shell_v6_view->request_resize.link); + wl_list_remove(&xdg_shell_v6_view->set_title.link); + wl_list_remove(&xdg_shell_v6_view->set_app_id.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -246,8 +326,8 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_width = view->wlr_xdg_surface_v6->geometry.width; view->natural_height = view->wlr_xdg_surface_v6->geometry.height; if (!view->natural_width && !view->natural_height) { - view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; - view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; + view->natural_width = view->wlr_xdg_surface_v6->surface->current.width; + view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; } view_map(view, view->wlr_xdg_surface_v6->surface); @@ -255,10 +335,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + arrange_windows(ws); } else { - arrange_and_commit(view->swayc->parent); + arrange_windows(view->swayc->parent); } + transaction_commit_dirty(); xdg_shell_v6_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, @@ -271,6 +352,22 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_v6_view->request_fullscreen); + + xdg_shell_v6_view->request_move.notify = handle_request_move; + wl_signal_add(&xdg_surface->toplevel->events.request_move, + &xdg_shell_v6_view->request_move); + + xdg_shell_v6_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xdg_surface->toplevel->events.request_resize, + &xdg_shell_v6_view->request_resize); + + xdg_shell_v6_view->set_title.notify = handle_set_title; + wl_signal_add(&xdg_surface->toplevel->events.set_title, + &xdg_shell_v6_view->set_title); + + xdg_shell_v6_view->set_app_id.notify = handle_set_app_id; + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + &xdg_shell_v6_view->set_app_id); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -295,11 +392,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { struct wlr_xdg_surface_v6 *xdg_surface = data; if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { - wlr_log(L_DEBUG, "New xdg_shell_v6 popup"); + wlr_log(WLR_DEBUG, "New xdg_shell_v6 popup"); return; } - wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", + wlr_log(WLR_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); wlr_xdg_surface_v6_ping(xdg_surface); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b3b1473d..65d4fcd4 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -69,16 +69,11 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { surface->ly = xsurface->y; desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); - if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct wlr_xwayland *xwayland = - seat->input->server->xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface); - } - - // TODO: we don't send surface enter/leave events to xwayland unmanaged - // surfaces, but xwayland doesn't support HiDPI anyway + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct wlr_xwayland *xwayland = + seat->input->server->xwayland.wlr_xwayland; + wlr_xwayland_set_seat(xwayland, seat->wlr_seat); + seat_set_focus_surface(seat, xsurface->surface, false); } static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { @@ -89,18 +84,16 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&surface->link); wl_list_remove(&surface->commit.link); - if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - if (seat->wlr_seat->keyboard_state.focused_surface == - xsurface->surface) { - // Restore focus - struct sway_container *previous = - seat_get_focus_inactive(seat, &root_container); - if (previous) { - // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, previous->parent); - seat_set_focus(seat, previous); - } + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (seat->wlr_seat->keyboard_state.focused_surface == + xsurface->surface) { + // Restore focus + struct sway_container *previous = + seat_get_focus_inactive(seat, &root_container); + if (previous) { + // Hack to get seat to re-focus the return value of get_focus + seat_set_focus(seat, previous->parent); + seat_set_focus(seat, previous); } } } @@ -119,7 +112,7 @@ static struct sway_xwayland_unmanaged *create_unmanaged( struct sway_xwayland_unmanaged *surface = calloc(1, sizeof(struct sway_xwayland_unmanaged)); if (surface == NULL) { - wlr_log(L_ERROR, "Allocation failed"); + wlr_log(WLR_ERROR, "Allocation failed"); return NULL; } @@ -246,6 +239,14 @@ static bool wants_floating(struct sway_view *view) { return false; } +static bool has_client_side_decorations(struct sway_view *view) { + if (xwayland_view_from_view(view) == NULL) { + return false; + } + struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; + return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; +} + static void _close(struct sway_view *view) { if (xwayland_view_from_view(view) == NULL) { return; @@ -269,6 +270,7 @@ static const struct sway_view_impl view_impl = { .set_tiled = set_tiled, .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, + .has_client_side_decorations = has_client_side_decorations, .close = _close, .destroy = destroy, }; @@ -278,15 +280,42 @@ static void handle_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, commit); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - struct wlr_surface_state *surface_state = xsurface->surface->current; + struct wlr_surface_state *surface_state = &xsurface->surface->current; if (view->swayc->instructions->length) { transaction_notify_view_ready_by_size(view, surface_state->width, surface_state->height); + } else if (container_is_floating(view->swayc)) { + view_update_size(view, surface_state->width, surface_state->height); } + view_damage_from(view); } +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, destroy); + struct sway_view *view = &xwayland_view->view; + + if (view->surface) { + view_unmap(view); + wl_list_remove(&xwayland_view->commit.link); + } + + wl_list_remove(&xwayland_view->destroy.link); + wl_list_remove(&xwayland_view->request_configure.link); + wl_list_remove(&xwayland_view->request_fullscreen.link); + wl_list_remove(&xwayland_view->request_move.link); + wl_list_remove(&xwayland_view->request_resize.link); + wl_list_remove(&xwayland_view->set_title.link); + wl_list_remove(&xwayland_view->set_class.link); + wl_list_remove(&xwayland_view->set_window_type.link); + wl_list_remove(&xwayland_view->set_hints.link); + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); + view_destroy(&xwayland_view->view); +} + static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap); @@ -307,6 +336,15 @@ static void handle_map(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = data; struct sway_view *view = &xwayland_view->view; + if (xsurface->override_redirect) { + // This window used not to have the override redirect flag and has it + // now. Switch to unmanaged. + handle_destroy(&xwayland_view->destroy, view); + struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); + unmanaged_handle_map(&unmanaged->map, xsurface); + return; + } + view->natural_width = xsurface->width; view->natural_height = xsurface->height; @@ -321,31 +359,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + arrange_windows(ws); } else { - arrange_and_commit(view->swayc->parent); + arrange_windows(view->swayc->parent); } -} - -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_xwayland_view *xwayland_view = - wl_container_of(listener, xwayland_view, destroy); - struct sway_view *view = &xwayland_view->view; - - if (view->surface) { - view_unmap(view); - wl_list_remove(&xwayland_view->commit.link); - } - - wl_list_remove(&xwayland_view->destroy.link); - wl_list_remove(&xwayland_view->request_configure.link); - wl_list_remove(&xwayland_view->request_fullscreen.link); - wl_list_remove(&xwayland_view->set_title.link); - wl_list_remove(&xwayland_view->set_class.link); - wl_list_remove(&xwayland_view->set_window_type.link); - wl_list_remove(&xwayland_view->map.link); - wl_list_remove(&xwayland_view->unmap.link); - view_destroy(&xwayland_view->view); + transaction_commit_dirty(); } static void handle_request_configure(struct wl_listener *listener, void *data) { @@ -379,8 +397,40 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) } view_set_fullscreen(view, xsurface->fullscreen); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + arrange_windows(output); + transaction_commit_dirty(); +} + +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_move); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface->mapped) { + return; + } + if (!container_is_floating(view->swayc)) { + return; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + seat_begin_move(seat, view->swayc, seat->last_button); +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_resize); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface->mapped) { + return; + } + if (!container_is_floating(view->swayc)) { + return; + } + struct wlr_xwayland_resize_event *e = data; + struct sway_seat *seat = input_manager_current_seat(input_manager); + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); } static void handle_set_title(struct wl_listener *listener, void *data) { @@ -417,6 +467,25 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { view_execute_criteria(view); } +static void handle_set_hints(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_hints); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface->mapped) { + return; + } + if (!xsurface->hints_urgency && view->urgent_timer) { + // The view is is in the timeout period. We'll ignore the request to + // unset urgency so that the view remains urgent until the timer clears + // it. + return; + } + if (view->allow_request_urgent) { + view_set_urgent(view, (bool)xsurface->hints_urgency); + } +} + struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface) { return xsurface->data; @@ -427,14 +496,13 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { xwayland_surface); struct wlr_xwayland_surface *xsurface = data; - if (wlr_xwayland_surface_is_unmanaged(xsurface) || - xsurface->override_redirect) { - wlr_log(L_DEBUG, "New xwayland unmanaged surface"); + if (xsurface->override_redirect) { + wlr_log(WLR_DEBUG, "New xwayland unmanaged surface"); create_unmanaged(xsurface); return; } - wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", + wlr_log(WLR_DEBUG, "New xwayland surface title='%s' class='%s'", xsurface->title, xsurface->class); struct sway_xwayland_view *xwayland_view = @@ -457,6 +525,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { &xwayland_view->request_fullscreen); xwayland_view->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&xsurface->events.request_move, + &xwayland_view->request_move); + xwayland_view->request_move.notify = handle_request_move; + + wl_signal_add(&xsurface->events.request_resize, + &xwayland_view->request_resize); + xwayland_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); xwayland_view->set_title.notify = handle_set_title; @@ -467,6 +543,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { &xwayland_view->set_window_type); xwayland_view->set_window_type.notify = handle_set_window_type; + wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints); + xwayland_view->set_hints.notify = handle_set_hints; + wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); xwayland_view->unmap.notify = handle_unmap; @@ -484,7 +563,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); int err = xcb_connection_has_error(xcb_conn); if (err) { - wlr_log(L_ERROR, "XCB connect failed: %d", err); + wlr_log(WLR_ERROR, "XCB connect failed: %d", err); return; } @@ -503,7 +582,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { free(reply); if (error != NULL) { - wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d", + wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d", atom_map[i], error->error_code); free(error); break; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 944e35aa..65d04cac 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -5,14 +5,19 @@ #elif __FreeBSD__ #include #endif +#include #include #include #include #include "list.h" #include "log.h" +#include "sway/desktop.h" +#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" +#include "sway/input/keyboard.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -126,7 +131,7 @@ static struct sway_container *container_at_coords( return ws; } - c = seat_get_focus_inactive(seat, output->swayc); + c = seat_get_active_child(seat, output->swayc); if (c) { return c; } @@ -138,6 +143,171 @@ static struct sway_container *container_at_coords( return output->swayc; } +static enum wlr_edges find_resize_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + if (cont->type != C_VIEW) { + return WLR_EDGE_NONE; + } + struct sway_view *view = cont->sway_view; + if (view->border == B_NONE || !view->border_thickness || view->using_csd) { + return WLR_EDGE_NONE; + } + + enum wlr_edges edge = 0; + if (cursor->cursor->x < cont->x + view->border_thickness) { + edge |= WLR_EDGE_LEFT; + } + if (cursor->cursor->y < cont->y + view->border_thickness) { + edge |= WLR_EDGE_TOP; + } + if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { + edge |= WLR_EDGE_RIGHT; + } + if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { + edge |= WLR_EDGE_BOTTOM; + } + return edge; +} + +static void handle_move_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + struct sway_container *con = seat->op_container; + desktop_damage_whole_container(con); + container_floating_translate(con, + cursor->cursor->x - cursor->previous.x, + cursor->cursor->y - cursor->previous.y); + desktop_damage_whole_container(con); +} + +static void calculate_floating_constraints(struct sway_container *con, + int *min_width, int *max_width, int *min_height, int *max_height) { + if (config->floating_minimum_width == -1) { // no minimum + *min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + *min_width = 75; + } else { + *min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + *min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + *min_height = 50; + } else { + *min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + *max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_width = ws->width; + } else { + *max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + *max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_height = ws->height; + } else { + *max_height = config->floating_maximum_height; + } +} + +static void handle_resize_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + struct sway_container *con = seat->op_container; + enum wlr_edges edge = seat->op_resize_edge; + + // The amount the mouse has moved since the start of the resize operation + // Positive is down/right + double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; + double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; + + if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { + mouse_move_x = 0; + } + if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { + mouse_move_y = 0; + } + + double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; + double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; + + if (seat->op_resize_preserve_ratio) { + double x_multiplier = grow_width / seat->op_ref_width; + double y_multiplier = grow_height / seat->op_ref_height; + double max_multiplier = fmax(x_multiplier, y_multiplier); + grow_width = seat->op_ref_width * max_multiplier; + grow_height = seat->op_ref_height * max_multiplier; + } + + // Determine new width/height, and accommodate for floating min/max values + double width = seat->op_ref_width + grow_width; + double height = seat->op_ref_height + grow_height; + int min_width, max_width, min_height, max_height; + calculate_floating_constraints(con, &min_width, &max_width, + &min_height, &max_height); + width = fmax(min_width, fmin(width, max_width)); + height = fmax(min_height, fmin(height, max_height)); + + // Apply the view's min/max size + if (con->type == C_VIEW) { + double view_min_width, view_max_width, view_min_height, view_max_height; + view_get_constraints(con->sway_view, &view_min_width, &view_max_width, + &view_min_height, &view_max_height); + width = fmax(view_min_width, fmin(width, view_max_width)); + height = fmax(view_min_height, fmin(height, view_max_height)); + } + + // Recalculate these, in case we hit a min/max limit + grow_width = width - seat->op_ref_width; + grow_height = height - seat->op_ref_height; + + // Determine grow x/y values - these are relative to the container's x/y at + // the start of the resize operation. + double grow_x = 0, grow_y = 0; + if (edge & WLR_EDGE_LEFT) { + grow_x = -grow_width; + } else if (edge & WLR_EDGE_RIGHT) { + grow_x = 0; + } else { + grow_x = -grow_width / 2; + } + if (edge & WLR_EDGE_TOP) { + grow_y = -grow_height; + } else if (edge & WLR_EDGE_BOTTOM) { + grow_y = 0; + } else { + grow_y = -grow_height / 2; + } + + // Determine the amounts we need to bump everything relative to the current + // size. + int relative_grow_width = width - con->width; + int relative_grow_height = height - con->height; + int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; + int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; + + // Actually resize stuff + con->x += relative_grow_x; + con->y += relative_grow_y; + con->width += relative_grow_width; + con->height += relative_grow_height; + + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->x += relative_grow_x; + view->y += relative_grow_y; + view->width += relative_grow_width; + view->height += relative_grow_height; + } + + arrange_windows(con); +} + void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, bool allow_refocusing) { if (time_msec == 0) { @@ -145,6 +315,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } struct sway_seat *seat = cursor->seat; + + if (seat->operation != OP_NONE) { + if (seat->operation == OP_MOVE) { + handle_move_motion(seat, cursor); + } else { + handle_resize_motion(seat, cursor); + } + cursor->previous.x = cursor->cursor->x; + cursor->previous.y = cursor->cursor->y; + return; + } + struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_surface *surface = NULL; double sx, sy; @@ -193,15 +375,21 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } } - // reset cursor if switching between clients - struct wl_client *client = NULL; - if (surface != NULL) { - client = wl_resource_get_client(surface->resource); - } - if (client != cursor->image_client) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - cursor->image_client = client; + // Handle cursor image + if (surface) { + // Reset cursor if switching between clients + struct wl_client *client = wl_resource_get_client(surface->resource); + if (client != cursor->image_client) { + cursor_set_image(cursor, "left_ptr", client); + } + } else if (c && container_is_floating(c)) { + // Try a floating container's resize edge + enum wlr_edges edge = find_resize_edge(c, cursor); + const char *image = edge == WLR_EDGE_NONE ? + "left_ptr" : wlr_xcursor_get_resize_name(edge); + cursor_set_image(cursor, image, NULL); + } else { + cursor_set_image(cursor, "left_ptr", NULL); } // send pointer enter/leave @@ -228,6 +416,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y); cursor_send_pointer_motion(cursor, event->time_msec, true); + transaction_commit_dirty(); } static void handle_cursor_motion_absolute( @@ -238,10 +427,56 @@ static void handle_cursor_motion_absolute( struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); cursor_send_pointer_motion(cursor, event->time_msec, true); + transaction_commit_dirty(); +} + +static void dispatch_cursor_button_floating(struct sway_cursor *cursor, + uint32_t time_msec, uint32_t button, enum wlr_button_state state, + struct wlr_surface *surface, double sx, double sy, + struct sway_container *cont) { + struct sway_seat *seat = cursor->seat; + + // Deny moving or resizing a fullscreen view + if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + bool mod_pressed = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); + enum wlr_edges edge = find_resize_edge(cont, cursor); + bool over_title = edge == WLR_EDGE_NONE && !surface; + + // Check for beginning move + if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && + (mod_pressed || over_title)) { + seat_begin_move(seat, cont, BTN_LEFT); + return; + } + + // Check for beginning resize + bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; + bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; + if ((resizing_via_border || resizing_via_mod) && + state == WLR_BUTTON_PRESSED) { + seat_begin_resize(seat, cont, button, edge); + return; + } + + // Send event to surface + seat_set_focus(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); } void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { + if (cursor->seat->operation != OP_NONE && + button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { + seat_end_mouse_operation(cursor->seat); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); + return; + } if (time_msec == 0) { time_msec = get_current_time_msec(); } @@ -255,14 +490,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor, wlr_layer_surface_from_wlr_surface(surface); if (layer->current.keyboard_interactive) { seat_set_focus_layer(cursor->seat, layer); - return; } - } - // Avoid moving keyboard focus from a surface that accepts it to one - // that does not unless the change would move us to a new workspace. - // - // This prevents, for example, losing focus when clicking on swaybar. - if (surface && cont && cont->type != C_VIEW) { + seat_pointer_notify_button(cursor->seat, time_msec, button, state); + } else if (cont && container_is_floating(cont)) { + dispatch_cursor_button_floating(cursor, time_msec, button, state, + surface, sx, sy, cont); + } else if (surface && cont && cont->type != C_VIEW) { + // Avoid moving keyboard focus from a surface that accepts it to one + // that does not unless the change would move us to a new workspace. + // + // This prevents, for example, losing focus when clicking on swaybar. struct sway_container *new_ws = cont; if (new_ws && new_ws->type != C_WORKSPACE) { new_ws = container_parent(new_ws, C_WORKSPACE); @@ -274,12 +511,15 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (new_ws != old_ws) { seat_set_focus(cursor->seat, cont); } + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else if (cont) { seat_set_focus(cursor->seat, cont); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); + } else { + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + transaction_commit_dirty(); } static void handle_cursor_button(struct wl_listener *listener, void *data) { @@ -425,6 +665,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y); cursor_send_pointer_motion(cursor, event->time_msec, true); + transaction_commit_dirty(); } static void handle_tool_tip(struct wl_listener *listener, void *data) { @@ -464,6 +705,9 @@ static void handle_request_set_cursor(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, request_set_cursor); + if (cursor->seat->operation != OP_NONE) { + return; + } struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wl_client *focused_client = NULL; @@ -476,15 +720,26 @@ static void handle_request_set_cursor(struct wl_listener *listener, // TODO: check cursor mode if (focused_client == NULL || event->seat_client->client != focused_client) { - wlr_log(L_DEBUG, "denying request to set cursor from unfocused client"); + 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->image = NULL; cursor->image_client = focused_client; } +void cursor_set_image(struct sway_cursor *cursor, const char *image, + struct wl_client *client) { + if (!cursor->image || strcmp(cursor->image, image) != 0) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, + cursor->cursor); + cursor->image = image; + } + cursor->image_client = client; +} + void sway_cursor_destroy(struct sway_cursor *cursor) { if (!cursor) { return; diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 29b47a7b..0b7cb766 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -61,7 +61,7 @@ static char *get_device_identifier(struct wlr_input_device *device) { int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; char *identifier = malloc(len); if (!identifier) { - wlr_log(L_ERROR, "Unable to allocate unique input device name"); + wlr_log(WLR_ERROR, "Unable to allocate unique input device name"); return NULL; } @@ -104,77 +104,89 @@ static void input_manager_libinput_config_pointer( } libinput_device = wlr_libinput_get_device_handle(wlr_device); - wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)", + wlr_log(WLR_DEBUG, "input_manager_libinput_config_pointer(%s)", ic->identifier); if (ic->accel_profile != INT_MIN) { - wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", ic->identifier, ic->accel_profile); libinput_device_config_accel_set_profile(libinput_device, ic->accel_profile); } if (ic->click_method != INT_MIN) { - wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", ic->identifier, ic->click_method); libinput_device_config_click_set_method(libinput_device, ic->click_method); } if (ic->drag_lock != INT_MIN) { - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", ic->identifier, ic->click_method); libinput_device_config_tap_set_drag_lock_enabled(libinput_device, ic->drag_lock); } if (ic->dwt != INT_MIN) { - wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", ic->identifier, ic->dwt); libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt); } if (ic->left_handed != INT_MIN) { - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) left_handed_set_enabled(%d)", ic->identifier, ic->left_handed); libinput_device_config_left_handed_set(libinput_device, ic->left_handed); } if (ic->middle_emulation != INT_MIN) { - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)", ic->identifier, ic->middle_emulation); libinput_device_config_middle_emulation_set_enabled(libinput_device, ic->middle_emulation); } if (ic->natural_scroll != INT_MIN) { - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)", ic->identifier, ic->natural_scroll); libinput_device_config_scroll_set_natural_scroll_enabled( libinput_device, ic->natural_scroll); } if (ic->pointer_accel != FLT_MIN) { - wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", ic->identifier, ic->pointer_accel); libinput_device_config_accel_set_speed(libinput_device, ic->pointer_accel); } + if (ic->scroll_button != INT_MIN) { + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_button(%d)", + ic->identifier, ic->scroll_button); + libinput_device_config_scroll_set_button(libinput_device, + ic->scroll_button); + } if (ic->scroll_method != INT_MIN) { - wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", ic->identifier, ic->scroll_method); libinput_device_config_scroll_set_method(libinput_device, ic->scroll_method); } if (ic->send_events != INT_MIN) { - wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", ic->identifier, ic->send_events); libinput_device_config_send_events_set_mode(libinput_device, ic->send_events); } if (ic->tap != INT_MIN) { - wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", ic->identifier, ic->tap); libinput_device_config_tap_set_enabled(libinput_device, ic->tap); } + if (ic->tap_button_map != INT_MIN) { + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", + ic->identifier, ic->tap); + libinput_device_config_tap_set_button_map(libinput_device, + ic->tap_button_map); + } } static void handle_device_destroy(struct wl_listener *listener, void *data) { @@ -187,7 +199,7 @@ static void handle_device_destroy(struct wl_listener *listener, void *data) { return; } - wlr_log(L_DEBUG, "removing device: '%s'", + wlr_log(WLR_DEBUG, "removing device: '%s'", input_device->identifier); struct sway_seat *seat = NULL; @@ -217,16 +229,19 @@ static void handle_new_input(struct wl_listener *listener, void *data) { input_device->identifier = get_device_identifier(device); wl_list_insert(&input->devices, &input_device->link); - wlr_log(L_DEBUG, "adding device: '%s'", + wlr_log(WLR_DEBUG, "adding device: '%s'", input_device->identifier); if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { input_manager_libinput_config_pointer(input_device); } + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = handle_device_destroy; + struct sway_seat *seat = NULL; if (!input_has_seat_configuration(input)) { - wlr_log(L_DEBUG, "no seat configuration, using default seat"); + wlr_log(WLR_DEBUG, "no seat configuration, using default seat"); seat = input_manager_get_seat(input, default_seat); seat_add_device(seat, input_device); return; @@ -256,13 +271,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) { } if (!added) { - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "device '%s' is not configured on any seats", input_device->identifier); } - - wl_signal_add(&device->events.destroy, &input_device->device_destroy); - input_device->device_destroy.notify = handle_device_destroy; } static void handle_inhibit_activate(struct wl_listener *listener, void *data) { @@ -282,7 +294,7 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) seat_set_exclusive_client(seat, NULL); struct sway_container *previous = seat_get_focus(seat); if (previous) { - wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, + wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous, container_type_to_str(previous->type), previous->name); // Hack to get seat to re-focus the return value of get_focus seat_set_focus(seat, previous->parent); @@ -359,7 +371,7 @@ void input_manager_apply_input_config(struct sway_input_manager *input, void input_manager_apply_seat_config(struct sway_input_manager *input, struct seat_config *seat_config) { - wlr_log(L_DEBUG, "applying new seat config for seat %s", + wlr_log(WLR_DEBUG, "applying new seat config for seat %s", seat_config->name); struct sway_seat *seat = input_manager_get_seat(input, seat_config->name); if (!seat) { diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ec149d06..ede38519 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,6 +3,7 @@ #include #include #include +#include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/input/keyboard.h" #include "sway/input/input-manager.h" @@ -108,7 +109,7 @@ static void get_active_binding(const struct sway_shortcut_state *state, } if (*current_binding && *current_binding != binding) { - wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", + wlr_log(WLR_DEBUG, "encountered duplicate bindings %d and %d", (*current_binding)->order, binding->order); } else { *current_binding = binding; @@ -122,12 +123,13 @@ static void get_active_binding(const struct sway_shortcut_state *state, */ static void keyboard_execute_command(struct sway_keyboard *keyboard, struct sway_binding *binding) { - wlr_log(L_DEBUG, "running command for binding: %s", + wlr_log(WLR_DEBUG, "running command for binding: %s", binding->command); config->handler_context.seat = keyboard->seat_device->sway_seat; struct cmd_results *results = execute_command(binding->command, NULL); + transaction_commit_dirty(); if (results->status != CMD_SUCCESS) { - wlr_log(L_DEBUG, "could not run command for binding: %s (%s)", + wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", binding->command, results->error); } free_cmd_results(results); @@ -386,7 +388,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { - wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist"); + wlr_log(WLR_DEBUG, "cannot configure keyboard: keymap does not exist"); xkb_context_unref(context); return; } @@ -420,6 +422,9 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { if (!keyboard) { return; } + if (keyboard->keymap) { + xkb_keymap_unref(keyboard->keymap); + } wl_list_remove(&keyboard->keyboard_key.link); wl_list_remove(&keyboard->keyboard_modifiers.link); free(keyboard); diff --git a/sway/input/seat.c b/sway/input/seat.c index 2c2087da..fc9e54b6 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,6 +1,11 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 199309L #include +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -75,7 +80,7 @@ static void seat_send_activate(struct sway_container *con, struct sway_seat *seat) { if (con->type == C_VIEW) { if (!seat_is_input_allowed(seat, con->sway_view->surface)) { - wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); + wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited"); return; } view_set_activated(con->sway_view, true); @@ -219,7 +224,7 @@ static struct sway_seat_container *seat_container_from_container( seat_con = calloc(1, sizeof(struct sway_seat_container)); if (seat_con == NULL) { - wlr_log(L_ERROR, "could not allocate seat container"); + wlr_log(WLR_ERROR, "could not allocate seat container"); return NULL; } @@ -301,7 +306,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) { struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); if (icon == NULL) { - wlr_log(L_ERROR, "Allocation failed"); + wlr_log(WLR_ERROR, "Allocation failed"); return; } icon->seat = seat; @@ -348,6 +353,7 @@ struct sway_seat *seat_create(struct sway_input_manager *input, free(seat); return NULL; } + seat->wlr_seat->data = seat; seat->cursor = sway_cursor_create(seat); if (!seat->cursor) { @@ -391,7 +397,7 @@ static void seat_apply_input_config(struct sway_seat *seat, struct input_config *ic = input_device_get_config( sway_device->input_device); if (ic != NULL) { - wlr_log(L_DEBUG, "Applying input config to %s", + wlr_log(WLR_DEBUG, "Applying input config to %s", sway_device->input_device->identifier); mapped_to_output = ic->mapped_to_output; @@ -401,7 +407,7 @@ static void seat_apply_input_config(struct sway_seat *seat, mapped_to_output = sway_device->input_device->wlr_device->output_name; } if (mapped_to_output != NULL) { - wlr_log(L_DEBUG, "Mapping input device %s to output %s", + wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", sway_device->input_device->identifier, mapped_to_output); struct sway_container *output = NULL; for (int i = 0; i < root_container.children->length; ++i) { @@ -415,7 +421,7 @@ static void seat_apply_input_config(struct sway_seat *seat, wlr_cursor_map_input_to_output(seat->cursor->cursor, sway_device->input_device->wlr_device, output->sway_output->wlr_output); - wlr_log(L_DEBUG, "Mapped to output %s", output->name); + wlr_log(WLR_DEBUG, "Mapped to output %s", output->name); } } } @@ -495,7 +501,7 @@ void seat_configure_device(struct sway_seat *seat, seat_configure_tablet_tool(seat, seat_device); break; case WLR_INPUT_DEVICE_TABLET_PAD: - wlr_log(L_DEBUG, "TODO: configure tablet pad"); + wlr_log(WLR_DEBUG, "TODO: configure tablet pad"); break; } } @@ -510,11 +516,11 @@ void seat_add_device(struct sway_seat *seat, struct sway_seat_device *seat_device = calloc(1, sizeof(struct sway_seat_device)); if (!seat_device) { - wlr_log(L_DEBUG, "could not allocate seat device"); + wlr_log(WLR_DEBUG, "could not allocate seat device"); return; } - wlr_log(L_DEBUG, "adding device %s to seat %s", + wlr_log(WLR_DEBUG, "adding device %s to seat %s", input_device->identifier, seat->wlr_seat->name); seat_device->sway_seat = seat; @@ -533,7 +539,7 @@ void seat_remove_device(struct sway_seat *seat, return; } - wlr_log(L_DEBUG, "removing device %s from seat %s", + wlr_log(WLR_DEBUG, "removing device %s from seat %s", input_device->identifier, seat->wlr_seat->name); seat_device_destroy(seat_device); @@ -594,6 +600,12 @@ static void seat_send_unfocus(struct sway_container *container, } } +static int handle_urgent_timeout(void *data) { + struct sway_view *view = data; + view_set_urgent(view, false); + return 0; +} + void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp) { if (seat->focused_layer) { @@ -616,6 +628,7 @@ void seat_set_focus_warp(struct sway_seat *seat, if (last_workspace && last_workspace == new_workspace && last_workspace->sway_workspace->fullscreen + && container && container->type == C_VIEW && !container->sway_view->is_fullscreen) { return; } @@ -649,6 +662,7 @@ void seat_set_focus_warp(struct sway_seat *seat, while (parent) { wl_list_remove(&parent->link); wl_list_insert(&seat->focus_stack, &parent->link); + container_set_dirty(parent->container); parent = seat_container_from_container(seat, @@ -661,9 +675,39 @@ void seat_set_focus_warp(struct sway_seat *seat, if (last_focus) { seat_send_unfocus(last_focus, seat); } - seat_send_focus(container, seat); - container_damage_whole(container); + + container_set_dirty(container); + container_set_dirty(container->parent); // for focused_inactive_child + if (last_focus) { + container_set_dirty(last_focus); + } + } + + // If urgent, either unset the urgency or start a timer to unset it + if (container && container->type == C_VIEW && + view_is_urgent(container->sway_view) && + !container->sway_view->urgent_timer) { + struct sway_view *view = container->sway_view; + if (last_workspace && last_workspace != new_workspace && + config->urgent_timeout > 0) { + view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_urgent_timeout, view); + wl_event_source_timer_update(view->urgent_timer, + config->urgent_timeout); + } else { + view_set_urgent(view, false); + } + } + + // If we've focused a floating container, bring it to the front. + // We do this by putting it at the end of the floating list. + // This must happen for both the pending and current children lists. + if (container && container_is_floating(container)) { + list_move_to_end(container->parent->children, container); + if (container_has_ancestor(container, container->current.parent)) { + list_move_to_end(container->parent->current.children, container); + } } // clean up unfocused empty workspace on new output @@ -707,11 +751,7 @@ void seat_set_focus_warp(struct sway_seat *seat, } } - if (last_focus) { - container_damage_whole(last_focus); - } - - if (last_workspace && last_workspace != new_workspace) { + if (last_focus != NULL) { cursor_send_pointer_motion(seat->cursor, 0, true); } @@ -726,11 +766,11 @@ void seat_set_focus(struct sway_seat *seat, } void seat_set_focus_surface(struct sway_seat *seat, - struct wlr_surface *surface) { + struct wlr_surface *surface, bool unfocus) { if (seat->focused_layer != NULL) { return; } - if (seat->has_focus) { + if (seat->has_focus && unfocus) { struct sway_container *focus = seat_get_focus(seat); seat_send_unfocus(focus, seat); seat->has_focus = false; @@ -752,7 +792,7 @@ void seat_set_focus_layer(struct sway_seat *seat, struct sway_container *previous = seat_get_focus_inactive(seat, &root_container); if (previous) { - wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, + wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous, container_type_to_str(previous->type), previous->name); // Hack to get seat to re-focus the return value of get_focus seat_set_focus(seat, previous->parent); @@ -762,7 +802,7 @@ void seat_set_focus_layer(struct sway_seat *seat, } else if (!layer || seat->focused_layer == layer) { return; } - seat_set_focus_surface(seat, layer->surface); + seat_set_focus_surface(seat, layer->surface, true); if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { seat->focused_layer = layer; } @@ -830,18 +870,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat, return NULL; } -struct sway_container *seat_get_active_current_child(struct sway_seat *seat, - struct sway_container *container) { - struct sway_container *child = seat_get_active_child(seat, container); - if (child) { - return child; - } - if (container->current.children->length == 1) { - return container->current.children->items[0]; - } - return NULL; -} - struct sway_container *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; @@ -873,3 +901,68 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { return NULL; } + +void seat_begin_move(struct sway_seat *seat, struct sway_container *con, + uint32_t button) { + if (!seat->cursor) { + wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); + return; + } + seat->operation = OP_MOVE; + seat->op_container = con; + seat->op_button = button; + cursor_set_image(seat->cursor, "grab", NULL); +} + +void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, + uint32_t button, enum wlr_edges edge) { + if (!seat->cursor) { + wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device"); + return; + } + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + seat->operation = OP_RESIZE; + seat->op_container = con; + seat->op_resize_preserve_ratio = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); + seat->op_resize_edge = edge == WLR_EDGE_NONE ? + RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; + seat->op_button = button; + seat->op_ref_lx = seat->cursor->cursor->x; + seat->op_ref_ly = seat->cursor->cursor->y; + seat->op_ref_con_lx = con->x; + seat->op_ref_con_ly = con->y; + seat->op_ref_width = con->width; + seat->op_ref_height = con->height; + + const char *image = edge == WLR_EDGE_NONE ? + "se-resize" : wlr_xcursor_get_resize_name(edge); + cursor_set_image(seat->cursor, image, NULL); +} + +void seat_end_mouse_operation(struct sway_seat *seat) { + switch (seat->operation) { + case OP_MOVE: + { + // We "move" the container to its own location so it discovers its + // output again. + struct sway_container *con = seat->op_container; + container_floating_move_to(con, con->x, con->y); + } + case OP_RESIZE: + // Don't need to do anything here. + break; + case OP_NONE: + break; + } + seat->operation = OP_NONE; + seat->op_container = NULL; + cursor_set_image(seat->cursor, "left_ptr", NULL); +} + +void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, + uint32_t button, enum wlr_button_state state) { + seat->last_button = button; + seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, + time_msec, button, state); +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index b9289e25..c49ea47e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -2,6 +2,7 @@ #include #include #include "log.h" +#include "sway/config.h" #include "sway/ipc-json.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" @@ -41,6 +42,7 @@ json_object *ipc_json_get_version() { json_object_object_add(version, "major", json_object_new_int(major)); json_object_object_add(version, "minor", json_object_new_int(minor)); json_object_object_add(version, "patch", json_object_new_int(patch)); + json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config_path)); return version; } @@ -168,7 +170,8 @@ static void ipc_json_describe_workspace(struct sway_container *workspace, json_object_object_add(object, "output", workspace->parent ? json_object_new_string(workspace->parent->name) : NULL); json_object_object_add(object, "type", json_object_new_string("workspace")); - json_object_object_add(object, "urgent", json_object_new_boolean(false)); + json_object_object_add(object, "urgent", + json_object_new_boolean(workspace->sway_workspace->urgent)); json_object_object_add(object, "representation", workspace->formatted_title ? json_object_new_string(workspace->formatted_title) : NULL); @@ -194,6 +197,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "layout", json_object_new_string(ipc_json_layout_description(c->layout))); } + + bool urgent = c->type == C_VIEW ? + view_is_urgent(c->sway_view) : container_has_urgent_child(c); + json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); } static void focus_inactive_children_iterator(struct sway_container *c, void *data) { diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 241fe742..be703915 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -17,6 +17,8 @@ #include #include #include "sway/commands.h" +#include "sway/config.h" +#include "sway/desktop/transaction.h" #include "sway/ipc-json.h" #include "sway/ipc-server.h" #include "sway/output.h" @@ -31,6 +33,7 @@ static int ipc_socket = -1; static struct wl_event_source *ipc_event_source = NULL; static struct sockaddr_un *ipc_sockaddr = NULL; static list_t *ipc_client_list = NULL; +static struct wl_listener ipc_display_destroy; static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; @@ -56,6 +59,26 @@ void ipc_client_disconnect(struct ipc_client *client); void ipc_client_handle_command(struct ipc_client *client); bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); +static void handle_display_destroy(struct wl_listener *listener, void *data) { + if (ipc_event_source) { + wl_event_source_remove(ipc_event_source); + } + close(ipc_socket); + unlink(ipc_sockaddr->sun_path); + + while (ipc_client_list->length) { + struct ipc_client *client = ipc_client_list->items[0]; + ipc_client_disconnect(client); + } + list_free(ipc_client_list); + + if (ipc_sockaddr) { + free(ipc_sockaddr); + } + + wl_list_remove(&ipc_display_destroy.link); +} + void ipc_init(struct sway_server *server) { ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (ipc_socket == -1) { @@ -85,24 +108,13 @@ void ipc_init(struct sway_server *server) { ipc_client_list = create_list(); + ipc_display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(server->wl_display, &ipc_display_destroy); + ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket, WL_EVENT_READABLE, ipc_handle_connection, server); } -void ipc_terminate(void) { - if (ipc_event_source) { - wl_event_source_remove(ipc_event_source); - } - close(ipc_socket); - unlink(ipc_sockaddr->sun_path); - - list_free(ipc_client_list); - - if (ipc_sockaddr) { - free(ipc_sockaddr); - } -} - struct sockaddr_un *ipc_user_sockaddr(void) { struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un)); if (ipc_sockaddr == NULL) { @@ -128,32 +140,32 @@ struct sockaddr_un *ipc_user_sockaddr(void) { int ipc_handle_connection(int fd, uint32_t mask, void *data) { (void) fd; struct sway_server *server = data; - wlr_log(L_DEBUG, "Event on IPC listening socket"); + wlr_log(WLR_DEBUG, "Event on IPC listening socket"); assert(mask == WL_EVENT_READABLE); int client_fd = accept(ipc_socket, NULL, NULL); if (client_fd == -1) { - wlr_log_errno(L_ERROR, "Unable to accept IPC client connection"); + wlr_log_errno(WLR_ERROR, "Unable to accept IPC client connection"); return 0; } int flags; if ((flags = fcntl(client_fd, F_GETFD)) == -1 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { - wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); + wlr_log_errno(WLR_ERROR, "Unable to set CLOEXEC on IPC client socket"); close(client_fd); return 0; } if ((flags = fcntl(client_fd, F_GETFL)) == -1 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { - wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); + wlr_log_errno(WLR_ERROR, "Unable to set NONBLOCK on IPC client socket"); close(client_fd); return 0; } struct ipc_client *client = malloc(sizeof(struct ipc_client)); if (!client) { - wlr_log(L_ERROR, "Unable to allocate ipc client"); + wlr_log(WLR_ERROR, "Unable to allocate ipc client"); close(client_fd); return 0; } @@ -169,12 +181,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { client->write_buffer_len = 0; client->write_buffer = malloc(client->write_buffer_size); if (!client->write_buffer) { - wlr_log(L_ERROR, "Unable to allocate ipc client write buffer"); + wlr_log(WLR_ERROR, "Unable to allocate ipc client write buffer"); close(client_fd); return 0; } - wlr_log(L_DEBUG, "New client: fd %d", client_fd); + wlr_log(WLR_DEBUG, "New client: fd %d", client_fd); list_add(ipc_client_list, client); return 0; } @@ -185,22 +197,22 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; if (mask & WL_EVENT_ERROR) { - wlr_log(L_ERROR, "IPC Client socket error, removing client"); + wlr_log(WLR_ERROR, "IPC Client socket error, removing client"); ipc_client_disconnect(client); return 0; } if (mask & WL_EVENT_HANGUP) { - wlr_log(L_DEBUG, "Client %d hung up", client->fd); + wlr_log(WLR_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } - wlr_log(L_DEBUG, "Client %d readable", client->fd); + wlr_log(WLR_DEBUG, "Client %d readable", client->fd); int read_available; if (ioctl(client_fd, FIONREAD, &read_available) == -1) { - wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size"); + wlr_log_errno(WLR_INFO, "Unable to read IPC socket buffer size"); ipc_client_disconnect(client); return 0; } @@ -222,13 +234,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { // Should be fully available, because read_available >= ipc_header_size ssize_t received = recv(client_fd, buf, ipc_header_size, 0); if (received == -1) { - wlr_log_errno(L_INFO, "Unable to receive header from IPC client"); + wlr_log_errno(WLR_INFO, "Unable to receive header from IPC client"); ipc_client_disconnect(client); return 0; } if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { - wlr_log(L_DEBUG, "IPC header check failed"); + wlr_log(WLR_DEBUG, "IPC header check failed"); ipc_client_disconnect(client); return 0; } @@ -262,8 +274,11 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) } client->current_command = event; if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { - wlr_log_errno(L_INFO, "Unable to send reply to IPC client"); - ipc_client_disconnect(client); + wlr_log_errno(WLR_INFO, "Unable to send reply to IPC client"); + /* ipc_send_reply destroys client on error, which also + * removes it from the list, so we need to process + * current index again */ + i--; } } } @@ -273,7 +288,7 @@ void ipc_event_workspace(struct sway_container *old, if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { return; } - wlr_log(L_DEBUG, "Sending workspace::%s event", change); + wlr_log(WLR_DEBUG, "Sending workspace::%s event", change); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(change)); if (strcmp("focus", change) == 0) { @@ -301,7 +316,7 @@ void ipc_event_window(struct sway_container *window, const char *change) { if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) { return; } - wlr_log(L_DEBUG, "Sending window::%s event", change); + wlr_log(WLR_DEBUG, "Sending window::%s event", change); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(change)); json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); @@ -315,7 +330,7 @@ void ipc_event_barconfig_update(struct bar_config *bar) { if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) { return; } - wlr_log(L_DEBUG, "Sending barconfig_update event"); + wlr_log(WLR_DEBUG, "Sending barconfig_update event"); json_object *json = ipc_json_describe_bar_config(bar); const char *json_string = json_object_to_json_string(json); @@ -323,13 +338,15 @@ void ipc_event_barconfig_update(struct bar_config *bar) { json_object_put(json); } -void ipc_event_mode(const char *mode) { +void ipc_event_mode(const char *mode, bool pango) { if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { return; } - wlr_log(L_DEBUG, "Sending mode::%s event", mode); + wlr_log(WLR_DEBUG, "Sending mode::%s event", mode); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(mode)); + json_object_object_add(obj, "pango_markup", + json_object_new_boolean(pango)); const char *json_string = json_object_to_json_string(obj); ipc_send_event(json_string, IPC_EVENT_MODE); @@ -340,13 +357,13 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; if (mask & WL_EVENT_ERROR) { - wlr_log(L_ERROR, "IPC Client socket error, removing client"); + wlr_log(WLR_ERROR, "IPC Client socket error, removing client"); ipc_client_disconnect(client); return 0; } if (mask & WL_EVENT_HANGUP) { - wlr_log(L_DEBUG, "Client %d hung up", client->fd); + wlr_log(WLR_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } @@ -355,14 +372,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { return 0; } - wlr_log(L_DEBUG, "Client %d writable", client->fd); + wlr_log(WLR_DEBUG, "Client %d writable", client->fd); ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); if (written == -1 && errno == EAGAIN) { return 0; } else if (written == -1) { - wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client"); + wlr_log_errno(WLR_INFO, "Unable to send data from queue to IPC client"); ipc_client_disconnect(client); return 0; } @@ -383,11 +400,9 @@ void ipc_client_disconnect(struct ipc_client *client) { return; } - if (client->fd != -1) { - shutdown(client->fd, SHUT_RDWR); - } + shutdown(client->fd, SHUT_RDWR); - wlr_log(L_INFO, "IPC Client %d disconnected", client->fd); + wlr_log(WLR_INFO, "IPC Client %d disconnected", client->fd); wl_event_source_remove(client->event_source); if (client->writable_event_source) { wl_event_source_remove(client->writable_event_source); @@ -448,7 +463,7 @@ void ipc_client_handle_command(struct ipc_client *client) { char *buf = malloc(client->payload_length + 1); if (!buf) { - wlr_log_errno(L_INFO, "Unable to allocate IPC payload"); + wlr_log_errno(WLR_INFO, "Unable to allocate IPC payload"); ipc_client_disconnect(client); return; } @@ -457,7 +472,7 @@ void ipc_client_handle_command(struct ipc_client *client) { ssize_t received = recv(client->fd, buf, client->payload_length, 0); if (received == -1) { - wlr_log_errno(L_INFO, "Unable to receive payload from IPC client"); + wlr_log_errno(WLR_INFO, "Unable to receive payload from IPC client"); ipc_client_disconnect(client); free(buf); return; @@ -465,16 +480,16 @@ void ipc_client_handle_command(struct ipc_client *client) { } buf[client->payload_length] = '\0'; - const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }"; - + bool client_valid = true; switch (client->current_command) { case IPC_COMMAND: { struct cmd_results *results = execute_command(buf, NULL); - const char *json = cmd_results_to_json(results); - char reply[256]; - int length = snprintf(reply, sizeof(reply), "%s", json); - ipc_send_reply(client, reply, (uint32_t) length); + transaction_commit_dirty(); + char *json = cmd_results_to_json(results); + int length = strlen(json); + client_valid = ipc_send_reply(client, json, (uint32_t)length); + free(json); free_cmd_results(results); goto exit_cleanup; } @@ -497,7 +512,8 @@ void ipc_client_handle_command(struct ipc_client *client) { } } const char *json_string = json_object_to_json_string(outputs); - ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(outputs); // free goto exit_cleanup; } @@ -508,7 +524,8 @@ void ipc_client_handle_command(struct ipc_client *client) { container_for_each_descendant_dfs(&root_container, ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); - ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(workspaces); // free goto exit_cleanup; } @@ -518,8 +535,8 @@ void ipc_client_handle_command(struct ipc_client *client) { // TODO: Check if they're permitted to use these events struct json_object *request = json_tokener_parse(buf); if (request == NULL) { - ipc_send_reply(client, "{\"success\": false}", 18); - wlr_log_errno(L_INFO, "Failed to read request"); + client_valid = ipc_send_reply(client, "{\"success\": false}", 18); + wlr_log_errno(WLR_INFO, "Failed to read request"); goto exit_cleanup; } @@ -539,15 +556,16 @@ void ipc_client_handle_command(struct ipc_client *client) { } else if (strcmp(event_type, "binding") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_BINDING); } else { - ipc_send_reply(client, "{\"success\": false}", 18); + client_valid = + ipc_send_reply(client, "{\"success\": false}", 18); json_object_put(request); - wlr_log_errno(L_INFO, "Failed to parse request"); + wlr_log_errno(WLR_INFO, "Failed to parse request"); goto exit_cleanup; } } json_object_put(request); - ipc_send_reply(client, "{\"success\": true}", 17); + client_valid = ipc_send_reply(client, "{\"success\": true}", 17); goto exit_cleanup; } @@ -559,7 +577,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(inputs, ipc_json_describe_input(device)); } const char *json_string = json_object_to_json_string(inputs); - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(inputs); // free goto exit_cleanup; } @@ -572,7 +591,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(seats, ipc_json_describe_seat(seat)); } const char *json_string = json_object_to_json_string(seats); - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(seats); // free goto exit_cleanup; } @@ -582,7 +602,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *tree = ipc_json_describe_container_recursive(&root_container); const char *json_string = json_object_to_json_string(tree); - ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(tree); goto exit_cleanup; } @@ -593,7 +614,8 @@ void ipc_client_handle_command(struct ipc_client *client) { container_descendants(&root_container, C_VIEW, ipc_get_marks_callback, marks); const char *json_string = json_object_to_json_string(marks); - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(marks); goto exit_cleanup; } @@ -602,7 +624,8 @@ void ipc_client_handle_command(struct ipc_client *client) { { json_object *version = ipc_json_get_version(); const char *json_string = json_object_to_json_string(version); - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(version); // free goto exit_cleanup; } @@ -617,7 +640,9 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(bars, json_object_new_string(bar->id)); } const char *json_string = json_object_to_json_string(bars); - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, + (uint32_t)strlen(json_string)); json_object_put(bars); // free } else { // Send particular bar's details @@ -631,27 +656,54 @@ void ipc_client_handle_command(struct ipc_client *client) { } if (!bar) { const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; - ipc_send_reply(client, error, (uint32_t)strlen(error)); + client_valid = + ipc_send_reply(client, error, (uint32_t)strlen(error)); goto exit_cleanup; } json_object *json = ipc_json_describe_bar_config(bar); const char *json_string = json_object_to_json_string(json); - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + client_valid = + ipc_send_reply(client, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free } goto exit_cleanup; } - default: - wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); + case IPC_GET_BINDING_MODES: + { + json_object *modes = json_object_new_array(); + for (int i = 0; i < config->modes->length; i++) { + struct sway_mode *mode = config->modes->items[i]; + json_object_array_add(modes, json_object_new_string(mode->name)); + } + const char *json_string = json_object_to_json_string(modes); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + json_object_put(modes); // free goto exit_cleanup; } - ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); - wlr_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); + case IPC_GET_CONFIG: + { + json_object *json = json_object_new_object(); + json_object_object_add(json, "config", json_object_new_string(config->current_config)); + const char *json_string = json_object_to_json_string(json); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + json_object_put(json); // free + goto exit_cleanup; + } + + default: + wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command); + goto exit_cleanup; + } exit_cleanup: - client->payload_length = 0; + if (client_valid) { + client->payload_length = 0; + } free(buf); return; } @@ -672,14 +724,14 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay } if (client->write_buffer_size > 4e6) { // 4 MB - wlr_log(L_ERROR, "Client write buffer too big, disconnecting client"); + wlr_log(WLR_ERROR, "Client write buffer too big, disconnecting client"); ipc_client_disconnect(client); return false; } char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); if (!new_buffer) { - wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer"); + wlr_log(WLR_ERROR, "Unable to reallocate ipc client write buffer"); ipc_client_disconnect(client); return false; } @@ -696,6 +748,6 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay ipc_client_handle_writable, client); } - wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); + wlr_log(WLR_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); return true; } diff --git a/sway/main.c b/sway/main.c index a325dc3a..a20f1dac 100644 --- a/sway/main.c +++ b/sway/main.c @@ -1,6 +1,7 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 200112L #include +#include #include #include #include @@ -19,6 +20,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/debug.h" +#include "sway/desktop/transaction.h" #include "sway/server.h" #include "sway/tree/layout.h" #include "sway/ipc-server.h" @@ -128,7 +130,7 @@ static void log_env() { "SWAYSOCK" }; for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { - wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); + wlr_log(WLR_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); } } @@ -143,14 +145,14 @@ static void log_distro() { for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { FILE *f = fopen(paths[i], "r"); if (f) { - wlr_log(L_INFO, "Contents of %s:", paths[i]); + wlr_log(WLR_INFO, "Contents of %s:", paths[i]); while (!feof(f)) { char *line; if (!(line = read_line(f))) { break; } if (*line) { - wlr_log(L_INFO, "%s", line); + wlr_log(WLR_INFO, "%s", line); } free(line); } @@ -162,7 +164,7 @@ static void log_distro() { static void log_kernel() { FILE *f = popen("uname -a", "r"); if (!f) { - wlr_log(L_INFO, "Unable to determine kernel version"); + wlr_log(WLR_INFO, "Unable to determine kernel version"); return; } while (!feof(f)) { @@ -171,25 +173,25 @@ static void log_kernel() { break; } if (*line) { - wlr_log(L_INFO, "%s", line); + wlr_log(WLR_INFO, "%s", line); } free(line); } - fclose(f); + pclose(f); } static void security_sanity_check() { // TODO: Notify users visually if this has issues struct stat s; if (stat("/proc", &s)) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); } #ifdef __linux__ cap_flag_value_t v; cap_t cap = cap_get_proc(); if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); } if (cap) { @@ -205,13 +207,13 @@ static void executable_sanity_check() { stat(exe, &sb); // We assume that cap_get_file returning NULL implies ENODATA if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "sway executable has both the s(g)uid bit AND file caps set."); - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "This is strongly discouraged (and completely broken)."); - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "Please clear one of them (either the suid bit, or the file caps)."); - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "If unsure, strip the file caps."); exit(EXIT_FAILURE); } @@ -222,16 +224,16 @@ static void executable_sanity_check() { static void drop_permissions(bool keep_caps) { if (getuid() != geteuid() || getgid() != getegid()) { if (setgid(getgid()) != 0) { - wlr_log(L_ERROR, "Unable to drop root"); + wlr_log(WLR_ERROR, "Unable to drop root"); exit(EXIT_FAILURE); } if (setuid(getuid()) != 0) { - wlr_log(L_ERROR, "Unable to drop root"); + wlr_log(WLR_ERROR, "Unable to drop root"); exit(EXIT_FAILURE); } } if (setuid(0) != -1) { - wlr_log(L_ERROR, "Root privileges can be restored."); + wlr_log(WLR_ERROR, "Root privileges can be restored."); exit(EXIT_FAILURE); } #ifdef __linux__ @@ -239,17 +241,29 @@ static void drop_permissions(bool keep_caps) { // Drop every cap except CAP_SYS_PTRACE cap_t caps = cap_init(); cap_value_t keep = CAP_SYS_PTRACE; - wlr_log(L_INFO, "Dropping extra capabilities"); + wlr_log(WLR_INFO, "Dropping extra capabilities"); if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) || cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) || cap_set_proc(caps)) { - wlr_log(L_ERROR, "Failed to drop extra capabilities"); + wlr_log(WLR_ERROR, "Failed to drop extra capabilities"); exit(EXIT_FAILURE); } } #endif } +void enable_debug_flag(const char *flag) { + if (strcmp(flag, "render-tree") == 0) { + enable_debug_tree = true; + } else if (strncmp(flag, "damage=", 7) == 0) { + damage_debug = &flag[7]; + } else if (strcmp(flag, "txn-debug") == 0) { + txn_debug = true; + } else if (strncmp(flag, "txn-timeout=", 12) == 0) { + txn_timeout_ms = atoi(&flag[12]); + } +} + int main(int argc, char **argv) { static int verbose = 0, debug = 0, validate = 0; @@ -289,7 +303,7 @@ int main(int argc, char **argv) { int c; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index); + c = getopt_long(argc, argv, "hCdD:vVc:", long_options, &option_index); if (c == -1) { break; } @@ -308,7 +322,7 @@ int main(int argc, char **argv) { debug = 1; break; case 'D': // extended debug options - enable_debug_tree = true; + enable_debug_flag(optarg); break; case 'v': // version fprintf(stdout, "sway version " SWAY_VERSION "\n"); @@ -334,22 +348,22 @@ int main(int argc, char **argv) { // TODO: switch logging over to wlroots? if (debug) { - wlr_log_init(L_DEBUG, NULL); + wlr_log_init(WLR_DEBUG, NULL); } else if (verbose || validate) { - wlr_log_init(L_INFO, NULL); + wlr_log_init(WLR_INFO, NULL); } else { - wlr_log_init(L_ERROR, NULL); + wlr_log_init(WLR_ERROR, NULL); } if (optind < argc) { // Behave as IPC client if(optind != 1) { - wlr_log(L_ERROR, "Don't use options with the IPC client"); + wlr_log(WLR_ERROR, "Don't use options with the IPC client"); exit(EXIT_FAILURE); } drop_permissions(false); char *socket_path = getenv("SWAYSOCK"); if (!socket_path) { - wlr_log(L_ERROR, "Unable to retrieve socket path"); + wlr_log(WLR_ERROR, "Unable to retrieve socket path"); exit(EXIT_FAILURE); } char *command = join_args(argv + optind, argc - optind); @@ -368,7 +382,7 @@ int main(int argc, char **argv) { if (getuid() != geteuid() || getgid() != getegid()) { // Retain capabilities after setuid() if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { - wlr_log(L_ERROR, "Cannot keep caps after setuid()"); + wlr_log(WLR_ERROR, "Cannot keep caps after setuid()"); exit(EXIT_FAILURE); } suid = true; @@ -389,7 +403,7 @@ int main(int argc, char **argv) { // prevent ipc from crashing sway signal(SIGPIPE, SIG_IGN); - wlr_log(L_INFO, "Starting sway version " SWAY_VERSION); + wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION); layout_init(); @@ -415,32 +429,41 @@ int main(int argc, char **argv) { security_sanity_check(); + setenv("WAYLAND_DISPLAY", server.socket, true); + if (!terminate_request) { + if (!server_start_backend(&server)) { + sway_terminate(EXIT_FAILURE); + } + } + config->active = true; // Execute commands until there are none left + wlr_log(WLR_DEBUG, "Running deferred commands"); while (config->cmd_queue->length) { char *line = config->cmd_queue->items[0]; struct cmd_results *res = execute_command(line, NULL); if (res->status != CMD_SUCCESS) { - wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error); + wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error); } free_cmd_results(res); free(line); list_del(config->cmd_queue, 0); } + transaction_commit_dirty(); if (!terminate_request) { server_run(&server); } - wlr_log(L_INFO, "Shutting down sway"); + wlr_log(WLR_INFO, "Shutting down sway"); server_fini(&server); - ipc_terminate(); - if (config) { free_config(config); } + pango_cairo_font_map_set_default(NULL); + return exit_value; } diff --git a/sway/meson.build b/sway/meson.build index 9ff3f05f..30c848e2 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -7,11 +7,14 @@ sway_sources = files( 'debug-tree.c', 'ipc-json.c', 'ipc-server.c', + 'scratchpad.c', 'security.c', 'desktop/desktop.c', + 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', + 'desktop/render.c', 'desktop/transaction.c', 'desktop/xdg_shell_v6.c', 'desktop/xdg_shell.c', @@ -33,16 +36,20 @@ sway_sources = files( 'commands/border.c', 'commands/client.c', 'commands/default_border.c', + 'commands/default_floating_border.c', 'commands/default_orientation.c', 'commands/exit.c', 'commands/exec.c', 'commands/exec_always.c', 'commands/floating.c', + 'commands/floating_minmax_size.c', + 'commands/floating_modifier.c', 'commands/focus.c', 'commands/focus_follows_mouse.c', 'commands/focus_wrapping.c', 'commands/font.c', 'commands/for_window.c', + 'commands/force_display_urgency_hint.c', 'commands/force_focus_wrapping.c', 'commands/fullscreen.c', 'commands/gaps.c', @@ -56,10 +63,12 @@ sway_sources = files( 'commands/mode.c', 'commands/mouse_warping.c', 'commands/move.c', + 'commands/no_focus.c', 'commands/output.c', 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', + 'commands/scratchpad.c', 'commands/seat.c', 'commands/seat/attach.c', 'commands/seat/cursor.c', @@ -73,6 +82,7 @@ sway_sources = files( 'commands/swap.c', 'commands/title_format.c', 'commands/unmark.c', + 'commands/urgent.c', 'commands/workspace.c', 'commands/workspace_layout.c', 'commands/ws_auto_back_and_forth.c', @@ -115,8 +125,10 @@ sway_sources = files( 'commands/input/pointer_accel.c', 'commands/input/repeat_delay.c', 'commands/input/repeat_rate.c', + 'commands/input/scroll_button.c', 'commands/input/scroll_method.c', 'commands/input/tap.c', + 'commands/input/tap_button_map.c', 'commands/input/xkb_layout.c', 'commands/input/xkb_model.c', 'commands/input/xkb_options.c', diff --git a/sway/scratchpad.c b/sway/scratchpad.c new file mode 100644 index 00000000..1e836e7d --- /dev/null +++ b/sway/scratchpad.c @@ -0,0 +1,175 @@ +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include "sway/scratchpad.h" +#include "sway/input/seat.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" +#include "list.h" +#include "log.h" + +void scratchpad_add_container(struct sway_container *con) { + if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { + return; + } + con->scratchpad = true; + list_add(root_container.sway_root->scratchpad, con); + + struct sway_container *parent = con->parent; + container_set_floating(con, true); + container_remove_child(con); + arrange_windows(parent); + + struct sway_seat *seat = input_manager_current_seat(input_manager); + seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); +} + +void scratchpad_remove_container(struct sway_container *con) { + if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) { + return; + } + con->scratchpad = false; + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + if (root_container.sway_root->scratchpad->items[i] == con) { + list_del(root_container.sway_root->scratchpad, i); + break; + } + } +} + +/** + * Show a single scratchpad container. + * The container might be visible on another workspace already. + */ +static void scratchpad_show(struct sway_container *con) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *ws = seat_get_focus(seat); + if (ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + + // If the current con or any of its parents are in fullscreen mode, we + // first need to disable it before showing the scratchpad con. + if (ws->sway_workspace->fullscreen) { + view_set_fullscreen(ws->sway_workspace->fullscreen, false); + } + + // Show the container + if (con->parent) { + container_remove_child(con); + } + container_add_child(ws->sway_workspace->floating, con); + + // Make sure the container's center point overlaps this workspace + double center_lx = con->x + con->width / 2; + double center_ly = con->y + con->height / 2; + + struct wlr_box workspace_box; + container_get_box(ws, &workspace_box); + if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { + // Maybe resize it + if (con->width > ws->width || con->height > ws->height) { + // TODO: Do this properly once we can float C_CONTAINERs + if (con->type == C_VIEW) { + view_init_floating(con->sway_view); + arrange_windows(con); + } + } + + // Center it + double new_lx = ws->x + (ws->width - con->width) / 2; + double new_ly = ws->y + (ws->height - con->height) / 2; + container_floating_move_to(con, new_lx, new_ly); + } + + seat_set_focus(seat, con); + + container_set_dirty(con->parent); +} + +/** + * Hide a single scratchpad container. + * The container might not be the focused container (eg. when using criteria). + */ +static void scratchpad_hide(struct sway_container *con) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *ws = container_parent(con, C_WORKSPACE); + + container_remove_child(con); + arrange_windows(ws); + if (con == focus) { + seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); + } + list_move_to_end(root_container.sway_root->scratchpad, con); +} + +void scratchpad_toggle_auto(void) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *ws = focus->type == C_WORKSPACE ? + focus : container_parent(focus, C_WORKSPACE); + + // Check if the currently focused window is a scratchpad window and should + // be hidden again. + if (focus->scratchpad) { + wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", + focus->name); + scratchpad_hide(focus); + return; + } + + // Check if there is an unfocused scratchpad window on the current workspace + // and focus it. + for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) { + struct sway_container *floater = + ws->sway_workspace->floating->children->items[i]; + if (floater->scratchpad && focus != floater) { + wlr_log(WLR_DEBUG, + "Focusing other scratchpad window (%s) in this workspace", + floater->name); + scratchpad_show(floater); + return; + } + } + + // Check if there is a visible scratchpad window on another workspace. + // In this case we move it to the current workspace. + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *con = + root_container.sway_root->scratchpad->items[i]; + if (con->parent) { + wlr_log(WLR_DEBUG, + "Moving a visible scratchpad window (%s) to this workspace", + con->name); + scratchpad_show(con); + return; + } + } + + // Take the container at the bottom of the scratchpad list + if (!sway_assert(root_container.sway_root->scratchpad->length, + "Scratchpad is empty")) { + return; + } + struct sway_container *con = root_container.sway_root->scratchpad->items[0]; + wlr_log(WLR_DEBUG, "Showing %s from list", con->name); + scratchpad_show(con); +} + +void scratchpad_toggle_container(struct sway_container *con) { + if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) { + return; + } + + // Check if it matches a currently visible scratchpad window and hide it. + if (con->parent) { + scratchpad_hide(con); + return; + } + + scratchpad_show(con); +} diff --git a/sway/server.c b/sway/server.c index f5700c09..89dfbf8c 100644 --- a/sway/server.c +++ b/sway/server.c @@ -7,13 +7,13 @@ #include #include #include +#include #include #include #include -#include -#include +#include #include -#include +#include #include #include #include @@ -21,26 +21,27 @@ // TODO WLR: make Xwayland optional #include "list.h" #include "sway/config.h" +#include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/input-manager.h" #include "sway/server.h" #include "sway/tree/layout.h" #include "sway/xwayland.h" bool server_privileged_prepare(struct sway_server *server) { - wlr_log(L_DEBUG, "Preparing Wayland server initialization"); + wlr_log(WLR_DEBUG, "Preparing Wayland server initialization"); server->wl_display = wl_display_create(); server->wl_event_loop = wl_display_get_event_loop(server->wl_display); server->backend = wlr_backend_autocreate(server->wl_display, NULL); if (!server->backend) { - wlr_log(L_ERROR, "Unable to create backend"); + wlr_log(WLR_ERROR, "Unable to create backend"); return false; } return true; } bool server_init(struct sway_server *server) { - wlr_log(L_DEBUG, "Initializing Wayland server"); + wlr_log(WLR_DEBUG, "Initializing Wayland server"); struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); assert(renderer); @@ -51,8 +52,6 @@ bool server_init(struct sway_server *server) { server->data_device_manager = wlr_data_device_manager_create(server->wl_display); - server->idle = wlr_idle_create(server->wl_display); - wlr_screenshooter_create(server->wl_display); wlr_gamma_control_manager_create(server->wl_display); wlr_primary_selection_device_manager_create(server->wl_display); @@ -62,6 +61,10 @@ bool server_init(struct sway_server *server) { wlr_xdg_output_manager_create(server->wl_display, root_container.sway_root->output_layout); + server->idle = wlr_idle_create(server->wl_display); + server->idle_inhibit_manager_v1 = + sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); + server->layer_shell = wlr_layer_shell_create(server->wl_display); wl_signal_add(&server->layer_shell->events.new_surface, &server->layer_shell_surface); @@ -105,12 +108,13 @@ bool server_init(struct sway_server *server) { wlr_server_decoration_manager_set_default_mode( deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); - wlr_linux_dmabuf_create(server->wl_display, renderer); + wlr_linux_dmabuf_v1_create(server->wl_display, renderer); wlr_export_dmabuf_manager_v1_create(server->wl_display); + wlr_screencopy_manager_v1_create(server->wl_display); server->socket = wl_display_add_socket_auto(server->wl_display); if (!server->socket) { - wlr_log(L_ERROR, "Unable to open wayland socket"); + wlr_log(WLR_ERROR, "Unable to open wayland socket"); wlr_backend_destroy(server->backend); return false; } @@ -119,8 +123,7 @@ bool server_init(struct sway_server *server) { if (debug != NULL && strcmp(debug, "txn_timings") == 0) { server->debug_txn_timings = true; } - server->destroying_containers = create_list(); - + server->dirty_containers = create_list(); server->transactions = create_list(); input_manager = input_manager_create(server); @@ -130,18 +133,23 @@ bool server_init(struct sway_server *server) { void server_fini(struct sway_server *server) { // TODO: free sway-specific resources wl_display_destroy(server->wl_display); - list_free(server->destroying_containers); + list_free(server->dirty_containers); list_free(server->transactions); } -void server_run(struct sway_server *server) { - wlr_log(L_INFO, "Running compositor on wayland display '%s'", +bool server_start_backend(struct sway_server *server) { + wlr_log(WLR_INFO, "Starting backend on wayland display '%s'", server->socket); - setenv("WAYLAND_DISPLAY", server->socket, true); if (!wlr_backend_start(server->backend)) { - wlr_log(L_ERROR, "Failed to start backend"); + wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(server->backend); - return; + return false; } + return true; +} + +void server_run(struct sway_server *server) { + wlr_log(WLR_INFO, "Running compositor on wayland display '%s'", + server->socket); wl_display_run(server->wl_display); } diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index c07460b1..b6391431 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -67,8 +67,8 @@ For more information on these xkb configuration options, see Enables or disables disable-while-typing for the specified input device. *input* events enabled|disabled|disabled\_on\_external\_mouse - Enables or disables send_events for specified input device. (Disabling - send_events disables the input device) + Enables or disables send\_events for specified input device. (Disabling + send\_events disables the input device) *input* left\_handed enabled|disabled Enables or disables left handed mode for specified input device. @@ -92,9 +92,20 @@ For more information on these xkb configuration options, see *input* scroll\_method none|two\_finger|edge|on\_button\_down Changes the scroll method for the specified input device. +*input* scroll\_button + Sets button used for scroll\_method on\_button\_down. The button identifier + can be obtained from `libinput debug-events`. + If set to 0, it disables the scroll\_button on\_button\_down. + *input* tap enabled|disabled Enables or disables tap for specified input device. +*input* tap_button_map lrm|lmr + Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as + left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ + treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as + right click. + ## SEAT CONFIGURATION Configure options for multiseat mode. sway-seat commands must be used inside a diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 5ce6bf06..d369d7b6 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -92,6 +92,12 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *focus* output Moves focus to the named output. +*focus tiling* + Sets focus to the last focused tiling container. + +*focus floating* + Sets focus to the last focused floating container. + *focus* mode\_toggle Moves focus between the floating and tiled layers. @@ -413,10 +419,12 @@ The default colors are: *mode* Switches to the specified mode. The default mode _default_. -*mode* *{* *}* +*mode* [--pango\_markup] *{* *}* _commands..._ after *{* will be added to the specified mode. A newline is required between *{* and the first command, and *}* must be alone on a line. Only *bindsym* and *bindcode* commands are permitted in mode blocks. + If _--pango\_markup_ is given, then _mode_ will be interpreted as pango + markup. *mouse\_warping* output|none If _output_ is specified, the mouse will be moved to new outputs as you @@ -491,6 +499,11 @@ config after the others, or it will be matched instead of the others. *unmark* will remove _identifier_ from the list of current marks on a window. If _identifier_ is omitted, all marks are removed. +*urgent* enable|disable|allow|deny + Using _enable_ or _disable_ manually sets or unsets the window's urgent + state. Using _allow_ or _deny_ controls the window's ability to set itself + as urgent. By default, windows are allowed to set their own urgency. + *workspace* [number] Switches to the specified workspace. The string "number" is optional and is used to sort workspaces. diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 582b2891..533cf71c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -47,11 +47,11 @@ static void apply_horiz_layout(struct sway_container *parent) { double scale = parent->width / total_width; // Resize windows - wlr_log(L_DEBUG, "Arranging %p horizontally", parent); + wlr_log(WLR_DEBUG, "Arranging %p horizontally", parent); double child_x = parent->x; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, child->width, scale); child->x = child_x; @@ -99,11 +99,11 @@ static void apply_vert_layout(struct sway_container *parent) { double scale = parent_height / total_height; // Resize - wlr_log(L_DEBUG, "Arranging %p vertically", parent); + wlr_log(WLR_DEBUG, "Arranging %p vertically", parent); double child_y = parent->y + parent_offset; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, child->height, scale); child->x = parent->x; @@ -144,42 +144,26 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { } } -/** - * If a container has been deleted from the pending tree state, we must add it - * to the transaction so it can be freed afterwards. To do this, we iterate the - * server's destroying_containers list and add all of them. We may add more than - * what we need to, but this is easy and has no negative consequences. - */ -static void add_deleted_containers(struct sway_transaction *transaction) { - for (int i = 0; i < server.destroying_containers->length; ++i) { - struct sway_container *child = server.destroying_containers->items[i]; - transaction_add_container(transaction, child); - } -} +static void arrange_children_of(struct sway_container *parent); -static void arrange_children_of(struct sway_container *parent, - struct sway_transaction *transaction); - -static void arrange_floating(struct sway_container *floating, - struct sway_transaction *transaction) { +static void arrange_floating(struct sway_container *floating) { for (int i = 0; i < floating->children->length; ++i) { struct sway_container *floater = floating->children->items[i]; if (floater->type == C_VIEW) { view_autoconfigure(floater->sway_view); } else { - arrange_children_of(floater, transaction); + arrange_children_of(floater); } - transaction_add_container(transaction, floater); + container_set_dirty(floater); } - transaction_add_container(transaction, floating); + container_set_dirty(floating); } -static void arrange_children_of(struct sway_container *parent, - struct sway_transaction *transaction) { +static void arrange_children_of(struct sway_container *parent) { if (config->reloading) { return; } - wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, + wlr_log(WLR_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, parent->name, parent->width, parent->height, parent->x, parent->y); // Calculate x, y, width and height of children @@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent, apply_horiz_layout(parent); break; case L_FLOATING: - arrange_floating(parent, transaction); + arrange_floating(parent); break; } @@ -213,20 +197,19 @@ static void arrange_children_of(struct sway_container *parent, if (child->type == C_VIEW) { view_autoconfigure(child->sway_view); } else { - arrange_children_of(child, transaction); + arrange_children_of(child); } - transaction_add_container(transaction, child); + container_set_dirty(child); } } -static void arrange_workspace(struct sway_container *workspace, - struct sway_transaction *transaction) { +static void arrange_workspace(struct sway_container *workspace) { if (config->reloading) { return; } struct sway_container *output = workspace->parent; struct wlr_box *area = &output->sway_output->usable_area; - wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", + wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", area->width, area->height, area->x, area->y); remove_gaps(workspace); workspace->width = area->width; @@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace, workspace->x = output->x + area->x; workspace->y = output->y + area->y; add_gaps(workspace); - transaction_add_container(transaction, workspace); - wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, + container_set_dirty(workspace); + wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, workspace->x, workspace->y); - arrange_floating(workspace->sway_workspace->floating, transaction); - arrange_children_of(workspace, transaction); + arrange_floating(workspace->sway_workspace->floating); + arrange_children_of(workspace); } -static void arrange_output(struct sway_container *output, - struct sway_transaction *transaction) { +static void arrange_output(struct sway_container *output) { if (config->reloading) { return; } @@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output, output->y = output_box->y; output->width = output_box->width; output->height = output_box->height; - transaction_add_container(transaction, output); - wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", + container_set_dirty(output); + wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y); for (int i = 0; i < output->children->length; ++i) { struct sway_container *workspace = output->children->items[i]; - arrange_workspace(workspace, transaction); + arrange_workspace(workspace); } } -static void arrange_root(struct sway_transaction *transaction) { +static void arrange_root() { if (config->reloading) { return; } @@ -274,48 +256,40 @@ static void arrange_root(struct sway_transaction *transaction) { root_container.y = layout_box->y; root_container.width = layout_box->width; root_container.height = layout_box->height; - transaction_add_container(transaction, &root_container); + container_set_dirty(&root_container); for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; - arrange_output(output, transaction); + arrange_output(output); } } -void arrange_windows(struct sway_container *container, - struct sway_transaction *transaction) { +void arrange_windows(struct sway_container *container) { switch (container->type) { case C_ROOT: - arrange_root(transaction); + arrange_root(); break; case C_OUTPUT: - arrange_output(container, transaction); + arrange_output(container); break; case C_WORKSPACE: - arrange_workspace(container, transaction); + arrange_workspace(container); break; case C_CONTAINER: - arrange_children_of(container, transaction); - transaction_add_container(transaction, container); + arrange_children_of(container); + container_set_dirty(container); break; case C_VIEW: view_autoconfigure(container->sway_view); - transaction_add_container(transaction, container); + container_set_dirty(container); break; case C_TYPES: break; } - add_deleted_containers(transaction); -} - -void arrange_and_commit(struct sway_container *container) { - struct sway_transaction *transaction = transaction_create(); - arrange_windows(container, transaction); - transaction_commit(transaction); } void remove_gaps(struct sway_container *c) { if (c->current_gaps == 0) { - wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c); + wlr_log(WLR_DEBUG, "Removing gaps: not gapped: %p", c); return; } @@ -326,12 +300,12 @@ void remove_gaps(struct sway_container *c) { c->current_gaps = 0; - wlr_log(L_DEBUG, "Removing gaps %p", c); + wlr_log(WLR_DEBUG, "Removing gaps %p", c); } void add_gaps(struct sway_container *c) { if (c->current_gaps > 0 || c->type == C_CONTAINER) { - wlr_log(L_DEBUG, "Not adding gaps: %p", c); + wlr_log(WLR_DEBUG, "Not adding gaps: %p", c); return; } @@ -348,5 +322,5 @@ void add_gaps(struct sway_container *c) { c->height -= 2 * gaps; c->current_gaps = gaps; - wlr_log(L_DEBUG, "Adding gaps: %p", c); + wlr_log(WLR_DEBUG, "Adding gaps: %p", c); } diff --git a/sway/tree/container.c b/sway/tree/container.c index 6f6137c4..4f743c40 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -11,11 +11,15 @@ #include "cairo.h" #include "pango.h" #include "sway/config.h" +#include "sway/desktop.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scratchpad.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -28,7 +32,7 @@ static list_t *get_bfs_queue() { if (!bfs_queue) { bfs_queue = create_list(); if (!bfs_queue) { - wlr_log(L_ERROR, "could not allocate list for bfs queue"); + wlr_log(WLR_ERROR, "could not allocate list for bfs queue"); return NULL; } } @@ -151,18 +155,11 @@ void container_free(struct sway_container *cont) { return; } free(cont->name); + free(cont->formatted_title); wlr_texture_destroy(cont->title_focused); wlr_texture_destroy(cont->title_focused_inactive); wlr_texture_destroy(cont->title_unfocused); wlr_texture_destroy(cont->title_urgent); - - for (int i = 0; i < server.destroying_containers->length; ++i) { - if (server.destroying_containers->items[i] == cont) { - list_del(server.destroying_containers, i); - break; - } - } - list_free(cont->instructions); list_free(cont->children); list_free(cont->current.children); @@ -203,16 +200,23 @@ static struct sway_container *container_workspace_destroy( return NULL; } - // Do not destroy this if it's the last workspace on this output struct sway_container *output = container_parent(workspace, C_OUTPUT); - if (output && output->children->length == 1) { + + // If we're destroying the output, it will be NULL here. Return the root so + // that it doesn't appear that the workspace has refused to be destoyed, + // which would leave it in a broken state with no parent. + if (output == NULL) { + return &root_container; + } + + // Do not destroy this if it's the last workspace on this output + if (output->children->length == 1) { return NULL; } - wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); + wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name); - struct sway_container *parent = workspace->parent; - if (!workspace_is_empty(workspace) && output) { + if (!workspace_is_empty(workspace)) { // Move children to a different workspace on this output struct sway_container *new_workspace = NULL; for (int i = 0; i < output->children->length; i++) { @@ -222,7 +226,7 @@ static struct sway_container *container_workspace_destroy( } } - wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", + wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'", workspace->name, new_workspace->name); for (int i = 0; i < workspace->children->length; i++) { container_move_to(workspace->children->items[i], new_workspace); @@ -234,7 +238,7 @@ static struct sway_container *container_workspace_destroy( } } - return parent; + return output; } static struct sway_container *container_output_destroy( @@ -288,7 +292,7 @@ static struct sway_container *container_output_destroy( output->sway_output->swayc = NULL; output->sway_output = NULL; - wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); + wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); return &root_container; } @@ -315,13 +319,19 @@ static struct sway_container *container_destroy_noreaping( // Workspaces will refuse to be destroyed if they're the last workspace // on their output. if (!container_workspace_destroy(con)) { - wlr_log(L_ERROR, "workspace doesn't want to destroy"); + wlr_log(WLR_ERROR, "workspace doesn't want to destroy"); return NULL; } } + container_end_mouse_operation(con); + con->destroying = true; - list_add(server.destroying_containers, con); + container_set_dirty(con); + + if (con->scratchpad) { + scratchpad_remove_container(con); + } if (!con->parent) { return NULL; @@ -342,7 +352,7 @@ bool container_reap_empty(struct sway_container *con) { break; case C_WORKSPACE: if (!workspace_is_visible(con) && workspace_is_empty(con)) { - wlr_log(L_DEBUG, "Destroying workspace via reaper"); + wlr_log(WLR_DEBUG, "Destroying workspace via reaper"); container_destroy_noreaping(con); return true; } @@ -435,7 +445,7 @@ struct sway_container *container_view_create(struct sway_container *sibling, } const char *title = view_get_title(sway_view); struct sway_container *swayc = container_create(C_VIEW); - wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", + wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s", swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); // Setup values swayc->sway_view = sway_view; @@ -657,7 +667,9 @@ struct sway_container *floating_container_at(double lx, double ly, if (!workspace_is_visible(workspace)) { continue; } - for (int k = 0; k < ws->floating->children->length; ++k) { + // Items at the end of the list are on top, so iterate the list in + // reverse. + for (int k = ws->floating->children->length - 1; k >= 0; --k) { struct sway_container *floater = ws->floating->children->items[k]; struct wlr_box box = { @@ -678,16 +690,23 @@ struct sway_container *floating_container_at(double lx, double ly, void container_for_each_descendant_dfs(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { - if (container) { - if (container->children) { - for (int i = 0; i < container->children->length; ++i) { - struct sway_container *child = - container->children->items[i]; - container_for_each_descendant_dfs(child, f, data); - } - } - f(container, data); + if (!container) { + return; } + if (container->children) { + for (int i = 0; i < container->children->length; ++i) { + struct sway_container *child = container->children->items[i]; + container_for_each_descendant_dfs(child, f, data); + } + } + if (container->type == C_WORKSPACE) { + struct sway_container *floating = container->sway_workspace->floating; + for (int i = 0; i < floating->children->length; ++i) { + struct sway_container *child = floating->children->items[i]; + container_for_each_descendant_dfs(child, f, data); + } + } + f(container, data); } void container_for_each_descendant_bfs(struct sway_container *con, @@ -698,7 +717,7 @@ void container_for_each_descendant_bfs(struct sway_container *con, } if (queue == NULL) { - wlr_log(L_ERROR, "could not allocate list"); + wlr_log(WLR_ERROR, "could not allocate list"); return; } @@ -782,7 +801,7 @@ static void update_title_texture(struct sway_container *con, double scale = output->sway_output->wlr_output->scale; int width = 0; - int height = config->font_height * scale; + int height = con->title_height * scale; cairo_t *c = cairo_create(NULL); get_text_size(c, config->font, &width, NULL, scale, config->pango_markup, @@ -935,11 +954,15 @@ void container_set_floating(struct sway_container *container, bool enable) { container_add_child(workspace->sway_workspace->floating, container); if (container->type == C_VIEW) { view_init_floating(container->sway_view); + view_set_tiled(container->sway_view, false); } seat_set_focus(seat, seat_get_focus_inactive(seat, container)); container_reap_empty_recursive(workspace); } else { // Returning to tiled + if (container->scratchpad) { + scratchpad_remove_container(container); + } container_remove_child(container); container_add_child(workspace, container); container->width = container->parent->width; @@ -951,6 +974,8 @@ void container_set_floating(struct sway_container *container, bool enable) { container_reap_empty_recursive(workspace->sway_workspace->floating); } + container_end_mouse_operation(container); + ipc_event_window(container, "floating"); } @@ -963,9 +988,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { return; } struct sway_view *view = con->sway_view; - size_t border_width = view->border_thickness * (view->border != B_NONE); - size_t top = - view->border == B_NORMAL ? container_titlebar_height() : border_width; + size_t border_width = 0; + size_t top = 0; + + if (!view->using_csd) { + border_width = view->border_thickness * (view->border != B_NONE); + top = view->border == B_NORMAL ? + container_titlebar_height() : border_width; + } con->x = view->x - border_width; con->y = view->y - top; @@ -987,3 +1017,112 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { box->width = container->width; box->height = container->height; } + +/** + * Translate the container's position as well as all children. + */ +void container_floating_translate(struct sway_container *con, + double x_amount, double y_amount) { + con->x += x_amount; + con->y += y_amount; + con->current.swayc_x += x_amount; + con->current.swayc_y += y_amount; + if (con->type == C_VIEW) { + con->sway_view->x += x_amount; + con->sway_view->y += y_amount; + con->current.view_x += x_amount; + con->current.view_y += y_amount; + } else { + for (int i = 0; i < con->children->length; ++i) { + struct sway_container *child = con->children->items[i]; + container_floating_translate(child, x_amount, y_amount); + } + } +} + +/** + * Choose an output for the floating container's new position. + * + * If the center of the container intersects an output then we'll choose that + * one, otherwise we'll choose whichever output is closest to the container's + * center. + */ +static struct sway_container *container_floating_find_output( + struct sway_container *con) { + double center_x = con->x + con->width / 2; + double center_y = con->y + con->height / 2; + struct sway_container *closest_output = NULL; + double closest_distance = DBL_MAX; + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + struct wlr_box output_box; + double closest_x, closest_y; + container_get_box(output, &output_box); + wlr_box_closest_point(&output_box, center_x, center_y, + &closest_x, &closest_y); + if (center_x == closest_x && center_y == closest_y) { + // The center of the floating container is on this output + return output; + } + double x_dist = closest_x - center_x; + double y_dist = closest_y - center_y; + double distance = x_dist * x_dist + y_dist * y_dist; + if (distance < closest_distance) { + closest_output = output; + closest_distance = distance; + } + } + return closest_output; +} + +void container_floating_move_to(struct sway_container *con, + double lx, double ly) { + if (!sway_assert(container_is_floating(con), + "Expected a floating container")) { + return; + } + desktop_damage_whole_container(con); + container_floating_translate(con, lx - con->x, ly - con->y); + desktop_damage_whole_container(con); + struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); + struct sway_container *new_output = container_floating_find_output(con); + if (!sway_assert(new_output, "Unable to find any output")) { + return; + } + struct sway_container *new_workspace = + output_get_active_workspace(new_output->sway_output); + if (old_workspace != new_workspace) { + container_remove_child(con); + container_add_child(new_workspace->sway_workspace->floating, con); + arrange_windows(old_workspace); + arrange_windows(new_workspace); + workspace_detect_urgent(old_workspace); + workspace_detect_urgent(new_workspace); + } +} + +void container_set_dirty(struct sway_container *container) { + if (container->dirty) { + return; + } + container->dirty = true; + list_add(server.dirty_containers, container); +} + +static bool find_urgent_iterator(struct sway_container *con, + void *data) { + return con->type == C_VIEW && view_is_urgent(con->sway_view); +} + +bool container_has_urgent_child(struct sway_container *container) { + return container_find(container, find_urgent_iterator, NULL); +} + +void container_end_mouse_operation(struct sway_container *container) { + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + if (seat->op_container == container) { + seat_end_mouse_operation(seat); + } + } +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 14631ad4..a2be0ef3 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -22,7 +22,8 @@ struct sway_container root_container; static void output_layout_handle_change(struct wl_listener *listener, void *data) { - arrange_and_commit(&root_container); + arrange_windows(&root_container); + transaction_commit_dirty(); } void layout_init(void) { @@ -41,6 +42,7 @@ void layout_init(void) { wl_list_init(&root_container.sway_root->xwayland_unmanaged); wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); + root_container.sway_root->scratchpad = create_list(); root_container.sway_root->output_layout_change.notify = output_layout_handle_change; @@ -101,7 +103,7 @@ void container_insert_child(struct sway_container *parent, if (old_parent) { container_remove_child(child); } - wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i); + wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i); list_insert(parent->children, i, child); child->parent = parent; container_handle_fullscreen_reparent(child, old_parent); @@ -127,13 +129,17 @@ struct sway_container *container_add_sibling(struct sway_container *fixed, void container_add_child(struct sway_container *parent, struct sway_container *child) { - wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", + wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type, child->width, child->height, parent, parent->type, parent->width, parent->height); struct sway_container *old_parent = child->parent; list_add(parent->children, child); child->parent = parent; container_handle_fullscreen_reparent(child, old_parent); + if (old_parent) { + container_set_dirty(old_parent); + } + container_set_dirty(child); } struct sway_container *container_remove_child(struct sway_container *child) { @@ -152,6 +158,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { child->parent = NULL; container_notify_subtree_changed(parent); + container_set_dirty(parent); + container_set_dirty(child); + return parent; } @@ -168,7 +177,12 @@ void container_move_to(struct sway_container *container, struct sway_container *old_parent = container_remove_child(container); container->width = container->height = 0; container->saved_width = container->saved_height = 0; - struct sway_container *new_parent; + + struct sway_container *new_parent, *new_parent_focus; + struct sway_seat *seat = input_manager_get_default_seat(input_manager); + + // Get the focus of the destination before we change it. + new_parent_focus = seat_get_focus_inactive(seat, destination); if (destination->type == C_VIEW) { new_parent = container_add_sibling(destination, container); } else { @@ -176,17 +190,20 @@ void container_move_to(struct sway_container *container, container_add_child(destination, container); } wl_signal_emit(&container->events.reparent, old_parent); + if (container->type == C_WORKSPACE) { // If moving a workspace to a new output, maybe create a new workspace // on the previous output - struct sway_seat *seat = input_manager_get_default_seat(input_manager); if (old_parent->children->length == 0) { char *ws_name = workspace_next_name(old_parent->name); - struct sway_container *ws = - workspace_create(old_parent, ws_name); + struct sway_container *ws = workspace_create(old_parent, ws_name); free(ws_name); seat_set_focus(seat, ws); } + + // Try to remove an empty workspace from the destination output. + container_reap_empty_recursive(new_parent_focus); + container_sort_workspaces(new_parent); seat_set_focus(seat, new_parent); workspace_output_raise_priority(container, old_parent, new_parent); @@ -216,6 +233,17 @@ void container_move_to(struct sway_container *container, } } } + // Update workspace urgent state + struct sway_container *old_workspace = old_parent; + if (old_workspace->type != C_WORKSPACE) { + old_workspace = container_parent(old_workspace, C_WORKSPACE); + } + if (new_workspace != old_workspace) { + workspace_detect_urgent(new_workspace); + if (old_workspace) { + workspace_detect_urgent(old_workspace); + } + } } static bool sway_dir_to_wlr(enum movement_direction dir, @@ -311,13 +339,13 @@ static void move_out_of_tabs_stacks(struct sway_container *container, int offs) { if (container->parent == current->parent && current->parent->children->length == 1) { - wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id); + wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); current->parent->layout = move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; return; } - wlr_log(L_DEBUG, "Moving out of tab/stack into a split"); + wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); bool is_workspace = current->parent->type == C_WORKSPACE; struct sway_container *new_parent = container_split(current->parent, move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); @@ -362,7 +390,7 @@ void container_move(struct sway_container *container, } parent = current->parent; - wlr_log(L_DEBUG, "Visiting %p %s '%s'", current, + wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, container_type_to_str(current->type), current->name); int index = index_child(current); @@ -380,12 +408,12 @@ void container_move(struct sway_container *container, root_container.sway_root->output_layout, wlr_dir, current->sway_output->wlr_output, ref_lx, ref_ly); if (!next) { - wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go"); + wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); return; } struct sway_output *next_output = next->data; current = next_output->swayc; - wlr_log(L_DEBUG, "Selected next output (%s)", current->name); + wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name); // Select workspace and get outta here current = seat_get_focus_inactive( config->handler_context.seat, current); @@ -398,20 +426,20 @@ void container_move(struct sway_container *container, case C_WORKSPACE: if (!is_parallel(current->layout, move_dir)) { if (current->children->length >= 2) { - wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)", + wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)", current->children->length); workspace_rejigger(current, container, move_dir); return; } else { - wlr_log(L_DEBUG, "Selecting output"); + wlr_log(WLR_DEBUG, "Selecting output"); current = current->parent; } } else if (current->layout == L_TABBED || current->layout == L_STACKED) { - wlr_log(L_DEBUG, "Rejiggering out of tabs/stacks"); + wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks"); workspace_rejigger(current, container, move_dir); } else { - wlr_log(L_DEBUG, "Selecting output"); + wlr_log(WLR_DEBUG, "Selecting output"); current = current->parent; } break; @@ -427,11 +455,11 @@ void container_move(struct sway_container *container, move_dir, offs); return; } else { - wlr_log(L_DEBUG, "Hit limit, selecting parent"); + wlr_log(WLR_DEBUG, "Hit limit, selecting parent"); current = current->parent; } } else { - wlr_log(L_DEBUG, "Hit limit, " + wlr_log(WLR_DEBUG, "Hit limit, " "promoting descendant to sibling"); // Special case container_insert_child(current->parent, container, @@ -441,14 +469,14 @@ void container_move(struct sway_container *container, } } else { sibling = parent->children->items[index + offs]; - wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id); + wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); } } else if (parent->layout == L_TABBED || parent->layout == L_STACKED) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; } else { - wlr_log(L_DEBUG, "Moving up to find a parallel container"); + wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); current = current->parent; } break; @@ -467,11 +495,11 @@ void container_move(struct sway_container *container, switch (sibling->type) { case C_VIEW: if (sibling->parent == container->parent) { - wlr_log(L_DEBUG, "Swapping siblings"); + wlr_log(WLR_DEBUG, "Swapping siblings"); sibling->parent->children->items[index + offs] = container; sibling->parent->children->items[index] = sibling; } else { - wlr_log(L_DEBUG, "Promoting to sibling of cousin"); + wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); container_insert_child(sibling->parent, container, index_child(sibling) + (offs > 0 ? 0 : 1)); container->width = container->height = 0; @@ -482,31 +510,31 @@ void container_move(struct sway_container *container, case C_CONTAINER: if (is_parallel(sibling->layout, move_dir)) { int limit = container_limit(sibling, invert_movement(move_dir)); - wlr_log(L_DEBUG, "limit: %d", limit); - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "limit: %d", limit); + wlr_log(WLR_DEBUG, "Reparenting container (parallel) to index %d " "(move dir: %d)", limit, move_dir); container_insert_child(sibling, container, limit); container->width = container->height = 0; sibling = NULL; } else { - wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); + wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); struct sway_container *focus_inactive = seat_get_focus_inactive( config->handler_context.seat, sibling); if (focus_inactive && focus_inactive != sibling) { while (focus_inactive->parent != sibling) { focus_inactive = focus_inactive->parent; } - wlr_log(L_DEBUG, "Focus inactive: id:%zd", + wlr_log(WLR_DEBUG, "Focus inactive: id:%zd", focus_inactive->id); sibling = focus_inactive; continue; } else if (sibling->children->length) { - wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily"); + wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily"); container_remove_child(container); container_add_sibling(sibling->children->items[0], container); } else { - wlr_log(L_DEBUG, "No kiddos, adding container alone"); + wlr_log(WLR_DEBUG, "No kiddos, adding container alone"); container_remove_child(container); container_add_child(sibling, container); } @@ -539,7 +567,10 @@ void container_move(struct sway_container *container, } if (last_ws && next_ws && last_ws != next_ws) { ipc_event_workspace(last_ws, container, "focus"); + workspace_detect_urgent(last_ws); + workspace_detect_urgent(next_ws); } + container_end_mouse_operation(container); } enum sway_container_layout container_get_default_layout( @@ -603,7 +634,7 @@ static struct sway_container *get_swayc_in_output_direction( } if (ws == NULL) { - wlr_log(L_ERROR, "got an output without a workspace"); + wlr_log(WLR_ERROR, "got an output without a workspace"); return NULL; } @@ -775,7 +806,7 @@ struct sway_container *container_get_in_direction( } else { struct sway_container *desired_con = parent->children->items[desired]; - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "cont %d-%p dir %i sibling %d: %p", idx, container, dir, desired, desired_con); return seat_get_focus_inactive_view(seat, desired_con); @@ -840,7 +871,7 @@ struct sway_container *container_split(struct sway_container *child, struct sway_container *cont = container_create(C_CONTAINER); - wlr_log(L_DEBUG, "creating container %p around %p", cont, child); + wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child); remove_gaps(child); @@ -888,7 +919,7 @@ struct sway_container *container_split(struct sway_container *child, void container_recursive_resize(struct sway_container *container, double amount, enum resize_edge edge) { bool layout_match = true; - wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); + wlr_log(WLR_DEBUG, "Resizing %p with amount: %f", container, amount); if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { container->width += amount; layout_match = container->layout == L_HORIZ; @@ -978,7 +1009,7 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { return; } - wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); + wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; diff --git a/sway/tree/output.c b/sway/tree/output.c index e2927cdb..da535c18 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -43,11 +43,11 @@ struct sway_container *output_create( if (strcasecmp(name, cur->name) == 0 || strcasecmp(identifier, cur->name) == 0) { - wlr_log(L_DEBUG, "Matched output config for %s", name); + wlr_log(WLR_DEBUG, "Matched output config for %s", name); oc = cur; } if (strcasecmp("*", cur->name) == 0) { - wlr_log(L_DEBUG, "Matched wildcard output config for %s", name); + wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name); all = cur; } @@ -86,7 +86,7 @@ struct sway_container *output_create( if (!output->children->length) { // Create workspace char *ws_name = workspace_next_name(output->name); - wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); + wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); struct sway_container *ws = workspace_create(output, ws_name); // Set each seat's focus if not already set struct sway_seat *seat = NULL; diff --git a/sway/tree/view.c b/sway/tree/view.c index 24fb6864..a55c8a29 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -26,6 +26,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->impl = impl; view->executed_criteria = create_list(); view->marks = create_list(); + view->allow_request_urgent = true; wl_signal_init(&view->events.unmap); } @@ -141,6 +142,19 @@ const char *view_get_shell(struct sway_view *view) { return "unknown"; } +void view_get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + if (view->impl->get_constraints) { + view->impl->get_constraints(view, + min_width, max_width, min_height, max_height); + } else { + *min_width = DBL_MIN; + *max_width = DBL_MAX; + *min_height = DBL_MIN; + *max_height = DBL_MAX; + } +} + uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height) { if (view->impl->configure) { @@ -151,12 +165,43 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, void view_init_floating(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - int max_width = ws->width * 0.6666; - int max_height = ws->height * 0.6666; - view->width = - view->natural_width > max_width ? max_width : view->natural_width; - view->height = - view->natural_height > max_height ? max_height : view->natural_height; + int min_width, min_height; + int max_width, max_height; + + if (config->floating_minimum_width == -1) { // no minimum + min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + min_width = 75; + } else { + min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + min_height = 50; + } else { + min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + max_width = ws->width * 0.6666; + } else { + max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + max_height = ws->height * 0.6666; + } else { + max_height = config->floating_maximum_height; + } + + view->width = fmax(min_width, fmin(view->natural_width, max_width)); + view->height = fmax(min_height, fmin(view->natural_height, max_height)); view->x = ws->x + (ws->width - view->width) / 2; view->y = ws->y + (ws->height - view->height) / 2; @@ -165,9 +210,6 @@ void view_init_floating(struct sway_view *view) { view->border_left = view->border_right = true; container_set_geometry_from_floating_view(view->swayc); - - // Don't maximize floating windows - view_set_tiled(view, false); } void view_autoconfigure(struct sway_view *view) { @@ -279,7 +321,6 @@ void view_autoconfigure(struct sway_view *view) { view->y = y; view->width = width; view->height = height; - view_set_tiled(view, true); } void view_set_activated(struct sway_view *view, bool activated) { @@ -289,7 +330,15 @@ void view_set_activated(struct sway_view *view, bool activated) { } void view_set_tiled(struct sway_view *view, bool tiled) { - view->border = tiled ? config->border : B_NONE; + if (!tiled) { + view->using_csd = true; + if (view->impl->has_client_side_decorations) { + view->using_csd = view->impl->has_client_side_decorations(view); + } + } else { + view->using_csd = false; + } + if (view->impl->set_tiled) { view->impl->set_tiled(view, tiled); } @@ -352,6 +401,8 @@ void view_set_fullscreen(struct sway_view *view, bool fullscreen) { } } + container_end_mouse_operation(view->swayc); + ipc_event_window(view->swayc, "fullscreen_mode"); } @@ -467,27 +518,45 @@ void view_execute_criteria(struct sway_view *view) { list_t *criterias = criteria_for_view(view, CT_COMMAND); for (int i = 0; i < criterias->length; i++) { struct criteria *criteria = criterias->items[i]; - wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); + wlr_log(WLR_DEBUG, "Checking criteria %s", criteria->raw); if (view_has_executed_criteria(view, criteria)) { - wlr_log(L_DEBUG, "Criteria already executed"); + wlr_log(WLR_DEBUG, "Criteria already executed"); continue; } - wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", + wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", criteria->raw, view, criteria->cmdlist); + seat_set_focus(seat, view->swayc); list_add(view->executed_criteria, criteria); struct cmd_results *res = execute_command(criteria->cmdlist, NULL); if (res->status != CMD_SUCCESS) { - wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); + wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); } free_cmd_results(res); - // view must be focused for commands to affect it, - // so always refocus in-between command lists - seat_set_focus(seat, view->swayc); } list_free(criterias); seat_set_focus(seat, prior_focus); } +static bool should_focus(struct sway_view *view) { + // If the view is the only one in the focused workspace, it'll get focus + // regardless of any no_focus criteria. + struct sway_container *parent = view->swayc->parent; + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { + size_t num_children = parent->children->length + + parent->sway_workspace->floating->children->length; + if (num_children == 1) { + return true; + } + } + + // Check no_focus criteria + list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); + size_t len = criterias->length; + list_free(criterias); + return len == 0; +} + void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { return; @@ -524,7 +593,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { prev_focus = target_sibling; target_sibling = seat_get_focus_inactive(seat, workspace); } else { - // TODO: CT_ASSIGN_OUTPUT + // CT_ASSIGN_OUTPUT + struct sway_container *output = output_by_name(criteria->target); + if (output) { + prev_focus = seat_get_focus_inactive(seat, output); + } } } list_free(criterias); @@ -549,8 +622,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->surface = wlr_surface; view->swayc = cont; - view->border = config->border; - view->border_thickness = config->border_thickness; view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -561,11 +632,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->container_reparent.notify = view_handle_container_reparent; if (view->impl->wants_floating && view->impl->wants_floating(view)) { + view->border = config->floating_border; + view->border_thickness = config->floating_border_thickness; container_set_floating(view->swayc, true); + } else { + view->border = config->border; + view->border_thickness = config->border_thickness; + view_set_tiled(view, true); } - if (prev_focus == target_sibling) { + if (should_focus(view) && prev_focus == target_sibling) { input_manager_set_focus(input_manager, cont); + if (workspace) { + workspace_switch(workspace); + } } view_update_title(view, false); @@ -581,16 +661,27 @@ void view_unmap(struct sway_view *view) { wl_list_remove(&view->surface_new_subsurface.link); wl_list_remove(&view->container_reparent.link); - if (view->is_fullscreen) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - ws->sway_workspace->fullscreen = NULL; - container_destroy(view->swayc); - - arrange_and_commit(ws->parent); - } else { - struct sway_container *parent = container_destroy(view->swayc); - arrange_and_commit(parent); + if (view->urgent_timer) { + wl_event_source_remove(view->urgent_timer); + view->urgent_timer = NULL; } + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + + struct sway_container *parent; + if (view->is_fullscreen) { + ws->sway_workspace->fullscreen = NULL; + parent = container_destroy(view->swayc); + + arrange_windows(ws->parent); + } else { + parent = container_destroy(view->swayc); + arrange_windows(parent); + } + if (parent->type >= C_WORKSPACE) { // if the workspace still exists + workspace_detect_urgent(ws); + } + transaction_commit_dirty(); view->surface = NULL; } @@ -601,6 +692,8 @@ void view_update_position(struct sway_view *view, double lx, double ly) { container_damage_whole(view->swayc); view->x = lx; view->y = ly; + view->swayc->current.view_x = lx; + view->swayc->current.view_y = ly; if (container_is_floating(view->swayc)) { container_set_geometry_from_floating_view(view->swayc); } @@ -614,6 +707,8 @@ void view_update_size(struct sway_view *view, int width, int height) { container_damage_whole(view->swayc); view->width = width; view->height = height; + view->swayc->current.view_width = width; + view->swayc->current.view_height = height; if (container_is_floating(view->swayc)) { container_set_geometry_from_floating_view(view->swayc); } @@ -624,7 +719,7 @@ static void view_subsurface_create(struct sway_view *view, struct wlr_subsurface *subsurface) { struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); if (child == NULL) { - wlr_log(L_ERROR, "Allocation failed"); + wlr_log(WLR_ERROR, "Allocation failed"); return; } view_child_init(child, NULL, view, subsurface->surface); @@ -744,8 +839,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { return NULL; } - wlr_log(L_DEBUG, "Surface of unknown type (role %s): %p", - wlr_surface->role, wlr_surface); + const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; + wlr_log(WLR_DEBUG, "Surface of unknown type (role %s): %p", + role, wlr_surface); return NULL; } @@ -812,7 +908,7 @@ static char *escape_title(char *buffer) { char *escaped_title = calloc(length + 1, sizeof(char)); int result = escape_markup_text(buffer, escaped_title, length); if (result != length) { - wlr_log(L_ERROR, "Could not escape title: %s", buffer); + wlr_log(WLR_ERROR, "Could not escape title: %s", buffer); free(escaped_title); return buffer; } @@ -946,7 +1042,7 @@ static void update_marks_texture(struct sway_view *view, double scale = output->sway_output->wlr_output->scale; int width = 0; - int height = config->font_height * scale; + int height = view->swayc->title_height * scale; cairo_t *c = cairo_create(NULL); get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); @@ -995,7 +1091,7 @@ void view_update_marks_textures(struct sway_view *view) { } bool view_is_visible(struct sway_view *view) { - if (!view->swayc || view->swayc->destroying) { + if (!view->swayc || view->swayc->destroying || !view->swayc->parent) { return false; } struct sway_container *workspace = @@ -1033,3 +1129,32 @@ bool view_is_visible(struct sway_view *view) { } return true; } + +void view_set_urgent(struct sway_view *view, bool enable) { + if (view_is_urgent(view) == enable) { + return; + } + if (enable) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (seat_get_focus(seat) == view->swayc) { + return; + } + clock_gettime(CLOCK_MONOTONIC, &view->urgent); + } else { + view->urgent = (struct timespec){ 0 }; + if (view->urgent_timer) { + wl_event_source_remove(view->urgent_timer); + view->urgent_timer = NULL; + } + } + container_damage_whole(view->swayc); + + ipc_event_window(view->swayc, "urgent"); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + workspace_detect_urgent(ws); +} + +bool view_is_urgent(struct sway_view *view) { + return view->urgent.tv_sec || view->urgent.tv_nsec; +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 651cc011..a93d9f44 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -12,6 +12,7 @@ #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" #include "log.h" @@ -50,7 +51,7 @@ struct sway_container *workspace_create(struct sway_container *output, output = get_workspace_initial_output(name); } - wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name); + wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); struct sway_container *workspace = container_create(C_WORKSPACE); workspace->x = output->x; @@ -108,9 +109,8 @@ static bool workspace_valid_on_output(const char *output_name, } char *workspace_next_name(const char *output_name) { - wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", + wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", output_name); - int l = 1; // Scan all workspace bindings to find the next available workspace name, // if none are found/available then default to a number struct sway_mode *mode = config->current_mode; @@ -137,7 +137,7 @@ char *workspace_next_name(const char *output_name) { while (isspace(*_target)) { memmove(_target, _target+1, strlen(_target+1)); } - wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", + wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", _target); // Make sure that the command references an actual workspace @@ -163,7 +163,7 @@ char *workspace_next_name(const char *output_name) { temp[length - 1] = '\0'; free(_target); _target = temp; - wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target); + wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); // Make sure the workspace number doesn't already exist if (workspace_by_number(_target)) { @@ -192,7 +192,9 @@ char *workspace_next_name(const char *output_name) { order = binding->order; free(target); target = _target; - wlr_log(L_DEBUG, "Workspace: Found free name %s", _target); + wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); + } else { + free(_target); } } free(dup); @@ -203,14 +205,9 @@ char *workspace_next_name(const char *output_name) { // As a fall back, get the current number of active workspaces // and return that + 1 for the next workspace's name int ws_num = root_container.children->length; - if (ws_num >= 10) { - l = 2; - } else if (ws_num >= 100) { - l = 3; - } + int l = snprintf(NULL, 0, "%d", ws_num); char *name = malloc(l + 1); - if (!name) { - wlr_log(L_ERROR, "Could not allocate workspace name"); + if (!sway_assert(name, "Cloud not allocate workspace name")) { return NULL; } sprintf(name, "%d", ws_num++); @@ -272,6 +269,9 @@ struct sway_container *workspace_by_name(const char *name) { */ struct sway_container *workspace_output_prev_next_impl( struct sway_container *output, bool next) { + if (!output) { + return NULL; + } if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) { return NULL; @@ -304,6 +304,9 @@ struct sway_container *workspace_output_prev_next_impl( */ struct sway_container *workspace_prev_next_impl( struct sway_container *workspace, bool next) { + if (!workspace) { + return NULL; + } if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) { return NULL; @@ -386,7 +389,7 @@ bool workspace_switch(struct sway_container *workspace) { free(prev_workspace_name); prev_workspace_name = malloc(strlen(active_ws->name) + 1); if (!prev_workspace_name) { - wlr_log(L_ERROR, "Unable to allocate previous workspace name"); + wlr_log(WLR_ERROR, "Unable to allocate previous workspace name"); return false; } strcpy(prev_workspace_name, active_ws->name); @@ -408,7 +411,7 @@ bool workspace_switch(struct sway_container *workspace) { } } - wlr_log(L_DEBUG, "Switching to workspace %p:%s", + wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); struct sway_container *next = seat_get_focus_inactive(seat, workspace); if (next == NULL) { @@ -426,7 +429,7 @@ bool workspace_switch(struct sway_container *workspace) { } seat_set_focus(seat, next); struct sway_container *output = container_parent(workspace, C_OUTPUT); - arrange_and_commit(output); + arrange_windows(output); return true; } @@ -518,6 +521,16 @@ struct sway_container *workspace_output_get_highest_available( return NULL; } +void workspace_detect_urgent(struct sway_container *workspace) { + bool new_urgent = container_has_urgent_child(workspace); + + if (workspace->sway_workspace->urgent != new_urgent) { + workspace->sway_workspace->urgent = new_urgent; + ipc_event_workspace(NULL, workspace, "urgent"); + container_damage_whole(workspace); + } +} + struct pid_workspace { pid_t pid; char *workspace; @@ -540,14 +553,14 @@ struct sway_container *workspace_for_pid(pid_t pid) { struct sway_container *ws = NULL; struct pid_workspace *pw = NULL; - wlr_log(L_DEBUG, "Looking up workspace for pid %d", pid); + wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); do { struct pid_workspace *_pw = NULL; wl_list_for_each(_pw, &pid_workspaces, link) { if (pid == _pw->pid) { pw = _pw; - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace); goto found; @@ -561,7 +574,7 @@ found: ws = workspace_by_name(pw->workspace); if (!ws) { - wlr_log(L_DEBUG, + wlr_log(WLR_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid); ws = workspace_create(pw->output, pw->workspace); @@ -582,7 +595,7 @@ static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { } void workspace_record_pid(pid_t pid) { - wlr_log(L_DEBUG, "Recording workspace for process %d", pid); + wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid); if (!pid_workspaces.prev && !pid_workspaces.next) { wl_list_init(&pid_workspaces); } @@ -594,12 +607,12 @@ void workspace_record_pid(pid_t pid) { ws = container_parent(ws, C_WORKSPACE); } if (!ws) { - wlr_log(L_DEBUG, "Bailing out, no workspace"); + wlr_log(WLR_DEBUG, "Bailing out, no workspace"); return; } struct sway_container *output = ws->parent; if (!output) { - wlr_log(L_DEBUG, "Bailing out, no output"); + wlr_log(WLR_DEBUG, "Bailing out, no output"); return; } diff --git a/swaybar/bar.c b/swaybar/bar.c index 5b8028e5..62a7727e 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -46,7 +46,7 @@ static void swaybar_output_free(struct swaybar_output *output) { if (!output) { return; } - wlr_log(L_DEBUG, "Removing output %s", output->name); + wlr_log(WLR_DEBUG, "Removing output %s", output->name); zwlr_layer_surface_v1_destroy(output->layer_surface); wl_surface_destroy(output->surface); wl_output_destroy(output->output); @@ -146,8 +146,10 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, && y >= hotspot->y && x < hotspot->x + hotspot->width && y < hotspot->y + hotspot->height) { - hotspot->callback(output, pointer->x, pointer->y, - button, hotspot->data); + if (HOTSPOT_IGNORE == hotspot->callback(output, pointer->x, pointer->y, + wl_button_to_x11_button(button), hotspot->data)) { + return; + } } } } @@ -155,11 +157,28 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { struct swaybar *bar = data; + struct swaybar_pointer *pointer = &bar->pointer; struct swaybar_output *output = bar->pointer.current; if (!sway_assert(output, "axis with no active output")) { return; } + struct swaybar_hotspot *hotspot; + wl_list_for_each(hotspot, &output->hotspots, link) { + double x = pointer->x * output->scale; + double y = pointer->y * output->scale; + if (x >= hotspot->x + && y >= hotspot->y + && x < hotspot->x + hotspot->width + && y < hotspot->y + hotspot->height) { + if (HOTSPOT_IGNORE == hotspot->callback( + output, pointer->x, pointer->y, + wl_axis_to_x11_button(axis, value), hotspot->data)) { + return; + } + } + } + double amt = wl_fixed_to_double(value); if (amt == 0.0) { return; diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 141612a6..ae37eeb9 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -31,7 +32,7 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) { status_error(status, "[failed to parse i3bar json]"); return false; } - wlr_log(L_DEBUG, "Got i3bar json: '%s'", text); + wlr_log(WLR_DEBUG, "Got i3bar json: '%s'", text); for (size_t i = 0; i < json_object_array_length(results); ++i) { json_object *full_text, *short_text, *color, *min_width, *align, *urgent; json_object *name, *instance, *separator, *separator_block_width; @@ -191,11 +192,11 @@ bool i3bar_handle_readable(struct status_line *status) { return redraw; } -void i3bar_block_send_click(struct status_line *status, - struct i3bar_block *block, int x, int y, uint32_t button) { - wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); +enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, + struct i3bar_block *block, int x, int y, enum x11_button button) { + wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); if (!block->name || !status->i3bar_state.click_events) { - return; + return HOTSPOT_PROCESS; } struct json_object *event_json = json_object_new_object(); @@ -214,4 +215,34 @@ void i3bar_block_send_click(struct status_line *status, status_error(status, "[failed to write click event]"); } json_object_put(event_json); + return HOTSPOT_IGNORE; +} + +enum x11_button wl_button_to_x11_button(uint32_t button) { + switch (button) { + case BTN_LEFT: + return LEFT; + case BTN_MIDDLE: + return MIDDLE; + case BTN_RIGHT: + return RIGHT; + case BTN_SIDE: + return BACK; + case BTN_EXTRA: + return FORWARD; + default: + return NONE; + } +} + +enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value) { + switch (axis) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + return wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + return wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT; + default: + wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll"); + return NONE; + } } diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 959fa095..c2d05920 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -115,6 +115,18 @@ static void ipc_parse_colors( config->colors.inactive_workspace.text = parse_color( json_object_get_string(inactive_workspace_text)); } + if (urgent_workspace_border) { + config->colors.urgent_workspace.border = parse_color( + json_object_get_string(urgent_workspace_border)); + } + if (urgent_workspace_bg) { + config->colors.urgent_workspace.background = parse_color( + json_object_get_string(urgent_workspace_bg)); + } + if (urgent_workspace_text) { + config->colors.urgent_workspace.text = parse_color( + json_object_get_string(urgent_workspace_text)); + } if (binding_mode_border) { config->colors.binding_mode.border = parse_color( json_object_get_string(binding_mode_border)); @@ -327,7 +339,7 @@ bool handle_ipc_readable(struct swaybar *bar) { json_object *result = json_tokener_parse(resp->payload); if (!result) { free_ipc_response(resp); - wlr_log(L_ERROR, "failed to parse payload as json"); + wlr_log(WLR_ERROR, "failed to parse payload as json"); return false; } json_object *json_change, *json_pango_markup; @@ -340,7 +352,7 @@ bool handle_ipc_readable(struct swaybar *bar) { bar->config->mode = strdup(change); } } else { - wlr_log(L_ERROR, "failed to parse response"); + wlr_log(WLR_ERROR, "failed to parse response"); json_object_put(result); free_ipc_response(resp); return false; diff --git a/swaybar/main.c b/swaybar/main.c index c897e1c9..60e4b37c 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -75,13 +75,13 @@ int main(int argc, char **argv) { } if (debug) { - wlr_log_init(L_DEBUG, NULL); + wlr_log_init(WLR_DEBUG, NULL); } else { - wlr_log_init(L_ERROR, NULL); + wlr_log_init(WLR_ERROR, NULL); } if (!bar_id) { - wlr_log(L_ERROR, "No bar_id passed. " + wlr_log(WLR_ERROR, "No bar_id passed. " "Provide --bar_id or let sway start swaybar"); return 1; } @@ -89,7 +89,7 @@ int main(int argc, char **argv) { if (!socket_path) { socket_path = get_socketpath(); if (!socket_path) { - wlr_log(L_ERROR, "Unable to retrieve socket path"); + wlr_log(WLR_ERROR, "Unable to retrieve socket path"); return 1; } } diff --git a/swaybar/render.c b/swaybar/render.c index 327a6f5f..6f370077 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -108,11 +108,11 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, } } -static void block_hotspot_callback(struct swaybar_output *output, - int x, int y, uint32_t button, void *data) { +static enum hotspot_event_handling block_hotspot_callback(struct swaybar_output *output, + int x, int y, enum x11_button button, void *data) { struct i3bar_block *block = data; struct status_line *status = output->bar->status; - i3bar_block_send_click(status, block, x, y, button); + return i3bar_block_send_click(status, block, x, y, button); } static uint32_t render_status_block(cairo_t *cairo, @@ -298,7 +298,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - output->scale, config->pango_markup, "%s", mode); + output->scale, config->mode_pango_markup, + "%s", mode); int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; @@ -329,7 +330,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, config->colors.binding_mode.text); cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); - pango_printf(cairo, config->font, output->scale, config->pango_markup, + pango_printf(cairo, config->font, output->scale, config->mode_pango_markup, "%s", mode); return surface_height; } @@ -347,9 +348,13 @@ static const char *strip_workspace_number(const char *ws_name) { return ws_name; } -static void workspace_hotspot_callback(struct swaybar_output *output, - int x, int y, uint32_t button, void *data) { +static enum hotspot_event_handling workspace_hotspot_callback(struct swaybar_output *output, + int x, int y, enum x11_button button, void *data) { + if (button != LEFT) { + return HOTSPOT_PROCESS; + } ipc_send_workspace_command(output->bar, (const char *)data); + return HOTSPOT_IGNORE; } static uint32_t render_workspace_button(cairo_t *cairo, @@ -496,12 +501,15 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { // different height than what we asked for wl_surface_commit(output->surface); wl_display_roundtrip(bar->display); - } else { + } else if (height > 0) { // Replay recording into shm and send it off output->current_buffer = get_next_buffer(bar->shm, output->buffers, output->width * output->scale, output->height * output->scale); + if (!output->current_buffer) { + return; + } cairo_t *shm = output->current_buffer->cairo; cairo_save(shm); diff --git a/swaybar/status_line.c b/swaybar/status_line.c index e0e7414a..bc47580b 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -49,14 +49,14 @@ bool status_handle_readable(struct status_line *status) { json_object *version; if (json_object_object_get_ex(proto, "version", &version) && json_object_get_int(version) == 1) { - wlr_log(L_DEBUG, "Switched to i3bar protocol."); + wlr_log(WLR_DEBUG, "Switched to i3bar protocol."); status->protocol = PROTOCOL_I3BAR; } json_object *click_events; if (json_object_object_get_ex( proto, "click_events", &click_events) && json_object_get_boolean(click_events)) { - wlr_log(L_DEBUG, "Enabled click events."); + wlr_log(WLR_DEBUG, "Enabled click events."); status->i3bar_state.click_events = true; const char *events_array = "[\n"; ssize_t len = strlen(events_array); @@ -91,7 +91,7 @@ struct status_line *status_line_init(char *cmd) { int pipe_read_fd[2]; int pipe_write_fd[2]; if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) { - wlr_log(L_ERROR, "Unable to create pipes for status_command fork"); + wlr_log(WLR_ERROR, "Unable to create pipes for status_command fork"); exit(1); } diff --git a/swaybg/main.c b/swaybg/main.c index 5b6c378c..f8e7e7ef 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -48,7 +48,7 @@ struct swaybg_state { bool is_valid_color(const char *color) { int len = strlen(color); if (len != 7 || color[0] != '#') { - wlr_log(L_ERROR, "%s is not a valid color for swaybg. " + wlr_log(WLR_ERROR, "%s is not a valid color for swaybg. " "Color should be specified as #rrggbb (no alpha).", color); return false; } @@ -68,6 +68,9 @@ static void render_frame(struct swaybg_state *state) { buffer_height = state->height * state->scale; state->current_buffer = get_next_buffer(state->shm, state->buffers, buffer_width, buffer_height); + if (!state->current_buffer) { + return; + } cairo_t *cairo = state->current_buffer->cairo; if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->context.color); @@ -185,10 +188,10 @@ int main(int argc, const char **argv) { struct swaybg_args args = {0}; struct swaybg_state state = {0}; state.args = &args; - wlr_log_init(L_DEBUG, NULL); + wlr_log_init(WLR_DEBUG, NULL); if (argc != 4) { - wlr_log(L_ERROR, "Do not run this program manually. " + wlr_log(WLR_ERROR, "Do not run this program manually. " "See man 5 sway and look for output options."); return 1; } diff --git a/swayidle/main.c b/swayidle/main.c index 7666578f..678d622f 100644 --- a/swayidle/main.c +++ b/swayidle/main.c @@ -1,22 +1,21 @@ #define _XOPEN_SOURCE 500 +#include #include -#include #include +#include #include #include -#include #include #include #include #include #include +#include #include #include #include -#include -#include -#include "idle-client-protocol.h" #include "config.h" +#include "idle-client-protocol.h" #include "list.h" #ifdef SWAY_IDLE_HAS_SYSTEMD #include @@ -36,7 +35,6 @@ struct swayidle_state { struct wl_display *display; struct org_kde_kwin_idle_timeout *idle_timer; struct org_kde_kwin_idle_timeout *lock_timer; - struct wlr_output_layout *layout; struct wl_event_loop *event_loop; list_t *timeout_cmds; } state; @@ -59,24 +57,24 @@ static void cmd_exec(void *data) { return; } char *param = (char *)data; - wlr_log(L_DEBUG, "Cmd exec %s", param); + wlr_log(WLR_DEBUG, "Cmd exec %s", param); pid_t pid = fork(); if (pid == 0) { pid = fork(); if (pid == 0) { char *const cmd[] = { "sh", "-c", param, NULL, }; execvp(cmd[0], cmd); - wlr_log_errno(L_ERROR, "execve failed!"); + wlr_log_errno(WLR_ERROR, "execve failed!"); exit(1); } else if (pid < 0) { - wlr_log_errno(L_ERROR, "fork failed"); + wlr_log_errno(WLR_ERROR, "fork failed"); exit(1); } exit(0); } else if (pid < 0) { - wlr_log_errno(L_ERROR, "fork failed"); + wlr_log_errno(WLR_ERROR, "fork failed"); } else { - wlr_log(L_DEBUG, "Spawned process %s", param); + wlr_log(WLR_DEBUG, "Spawned process %s", param); waitpid(pid, NULL, 0); } } @@ -86,7 +84,7 @@ static int lock_fd = -1; static int ongoing_fd = -1; static int release_lock(void *data) { - wlr_log(L_INFO, "Releasing sleep lock %d", ongoing_fd); + wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd); if (ongoing_fd >= 0) { close(ongoing_fd); } @@ -101,7 +99,7 @@ void acquire_sleep_lock() { int ret = sd_bus_default_system(&bus); if (ret < 0) { - wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", + wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", strerror(-ret)); return; } @@ -112,17 +110,17 @@ void acquire_sleep_lock() { &error, &msg, "ssss", "sleep", "swayidle", "Setup Up Lock Screen", "delay"); if (ret < 0) { - wlr_log(L_ERROR, "Failed to send Inhibit signal: %s", + wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s", strerror(-ret)); } else { ret = sd_bus_message_read(msg, "h", &lock_fd); if (ret < 0) { - wlr_log(L_ERROR, + wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s", strerror(-ret)); } } - wlr_log(L_INFO, "Got sleep lock: %d", lock_fd); + wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd); } static int prepare_for_sleep(sd_bus_message *msg, void *userdata, @@ -131,10 +129,10 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata, int going_down = 1; int ret = sd_bus_message_read(msg, "b", &going_down); if (ret < 0) { - wlr_log(L_ERROR, "Failed to parse D-Bus response for Inhibit: %s", + wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s", strerror(-ret)); } - wlr_log(L_DEBUG, "PrepareForSleep signal received %d", going_down); + wlr_log(WLR_DEBUG, "PrepareForSleep signal received %d", going_down); if (!going_down) { acquire_sleep_lock(); return 0; @@ -151,7 +149,7 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata, wl_event_loop_add_timer(state.event_loop, release_lock, NULL); wl_event_source_timer_update(source, 1000); } - wlr_log(L_DEBUG, "Prepare for sleep done"); + wlr_log(WLR_DEBUG, "Prepare for sleep done"); return 0; } @@ -165,10 +163,10 @@ static int dbus_event(int fd, uint32_t mask, void *data) { void setup_sleep_listener() { struct sd_bus *bus; - + int ret = sd_bus_default_system(&bus); if (ret < 0) { - wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", + wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", strerror(-ret)); return; } @@ -183,7 +181,7 @@ void setup_sleep_listener() { "/org/freedesktop/login1"); ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL); if (ret < 0) { - wlr_log(L_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); + wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); return; } acquire_sleep_lock(); @@ -214,7 +212,7 @@ static const struct wl_registry_listener registry_listener = { static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) { struct swayidle_timeout_cmd *cmd = data; - wlr_log(L_DEBUG, "idle state"); + wlr_log(WLR_DEBUG, "idle state"); if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) { cmd->idle_cmd->callback(cmd->idle_cmd->param); } @@ -222,7 +220,7 @@ static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) { static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) { struct swayidle_timeout_cmd *cmd = data; - wlr_log(L_DEBUG, "active state"); + wlr_log(WLR_DEBUG, "active state"); if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) { cmd->resume_cmd->callback(cmd->resume_cmd->param); } @@ -235,12 +233,12 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = { struct swayidle_cmd *parse_command(int argc, char **argv) { if (argc < 1) { - wlr_log(L_ERROR, "Too few parameters for command in parse_command"); + wlr_log(WLR_ERROR, "Too few parameters for command in parse_command"); return NULL; } struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd)); - wlr_log(L_DEBUG, "Command: %s", argv[0]); + wlr_log(WLR_DEBUG, "Command: %s", argv[0]); cmd->callback = cmd_exec; cmd->param = argv[0]; return cmd; @@ -248,7 +246,7 @@ struct swayidle_cmd *parse_command(int argc, char **argv) { int parse_timeout(int argc, char **argv) { if (argc < 3) { - wlr_log(L_ERROR, "Too few parameters to timeout command. " + wlr_log(WLR_ERROR, "Too few parameters to timeout command. " "Usage: timeout "); exit(-1); } @@ -256,7 +254,7 @@ int parse_timeout(int argc, char **argv) { char *endptr; int seconds = strtoul(argv[1], &endptr, 10); if (errno != 0 || *endptr != '\0') { - wlr_log(L_ERROR, "Invalid timeout parameter '%s', it should be a " + wlr_log(WLR_ERROR, "Invalid timeout parameter '%s', it should be a " "numeric value representing seconds", optarg); exit(-1); } @@ -264,13 +262,13 @@ int parse_timeout(int argc, char **argv) { calloc(1, sizeof(struct swayidle_timeout_cmd)); cmd->timeout = seconds * 1000; - wlr_log(L_DEBUG, "Register idle timeout at %d ms", cmd->timeout); - wlr_log(L_DEBUG, "Setup idle"); + wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout); + wlr_log(WLR_DEBUG, "Setup idle"); cmd->idle_cmd = parse_command(argc - 2, &argv[2]); int result = 3; if (argc >= 5 && !strcmp("resume", argv[3])) { - wlr_log(L_DEBUG, "Setup resume"); + wlr_log(WLR_DEBUG, "Setup resume"); cmd->resume_cmd = parse_command(argc - 4, &argv[4]); result = 5; } @@ -280,14 +278,14 @@ int parse_timeout(int argc, char **argv) { int parse_sleep(int argc, char **argv) { if (argc < 2) { - wlr_log(L_ERROR, "Too few parameters to before-sleep command. " + wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. " "Usage: before-sleep "); exit(-1); } lock_cmd = parse_command(argc - 1, &argv[1]); if (lock_cmd) { - wlr_log(L_DEBUG, "Setup sleep lock: %s", lock_cmd->param); + wlr_log(WLR_DEBUG, "Setup sleep lock: %s", lock_cmd->param); } return 2; @@ -314,10 +312,10 @@ int parse_args(int argc, char *argv[]) { } if (debug) { - wlr_log_init(L_DEBUG, NULL); - wlr_log(L_DEBUG, "Loglevel debug"); + wlr_log_init(WLR_DEBUG, NULL); + wlr_log(WLR_DEBUG, "Loglevel debug"); } else { - wlr_log_init(L_INFO, NULL); + wlr_log_init(WLR_INFO, NULL); } @@ -326,13 +324,13 @@ int parse_args(int argc, char *argv[]) { int i = optind; while (i < argc) { if (!strcmp("timeout", argv[i])) { - wlr_log(L_DEBUG, "Got timeout"); + wlr_log(WLR_DEBUG, "Got timeout"); i += parse_timeout(argc - i, &argv[i]); } else if (!strcmp("before-sleep", argv[i])) { - wlr_log(L_DEBUG, "Got before-sleep"); + wlr_log(WLR_DEBUG, "Got before-sleep"); i += parse_sleep(argc - i, &argv[i]); } else { - wlr_log(L_ERROR, "Unsupported command '%s'", argv[i]); + wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]); exit(-1); } } @@ -358,16 +356,16 @@ static int display_event(int fd, uint32_t mask, void *data) { sway_terminate(0); } if (wl_display_dispatch(state.display) < 0) { - wlr_log_errno(L_ERROR, "wl_display_dispatch failed, exiting"); + wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting"); sway_terminate(0); - }; + } return 0; } void register_idle_timeout(void *item) { struct swayidle_timeout_cmd *cmd = item; if (cmd == NULL || !cmd->timeout) { - wlr_log(L_ERROR, "Invalid idle cmd, will not register"); + wlr_log(WLR_ERROR, "Invalid idle cmd, will not register"); return; } state.idle_timer = @@ -376,7 +374,7 @@ void register_idle_timeout(void *item) { org_kde_kwin_idle_timeout_add_listener(state.idle_timer, &idle_timer_listener, cmd); } else { - wlr_log(L_ERROR, "Could not create idle timer"); + wlr_log(WLR_ERROR, "Could not create idle timer"); } } @@ -390,22 +388,21 @@ int main(int argc, char *argv[]) { state.display = wl_display_connect(NULL); if (state.display == NULL) { - wlr_log(L_ERROR, "Failed to create display"); + wlr_log(WLR_ERROR, "Failed to create display"); return -3; } struct wl_registry *registry = wl_display_get_registry(state.display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(state.display); - state.layout = wlr_output_layout_create(); state.event_loop = wl_event_loop_create(); if (idle_manager == NULL) { - wlr_log(L_ERROR, "Display doesn't support idle protocol"); + wlr_log(WLR_ERROR, "Display doesn't support idle protocol"); return -4; } if (seat == NULL) { - wlr_log(L_ERROR, "Seat error"); + wlr_log(WLR_ERROR, "Seat error"); return -5; } @@ -417,7 +414,7 @@ int main(int argc, char *argv[]) { } #endif if (!should_run) { - wlr_log(L_INFO, "No command specified! Nothing to do, will exit"); + wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit"); sway_terminate(0); } list_foreach(state.timeout_cmds, register_idle_timeout); diff --git a/swaylock/main.c b/swaylock/main.c index 591df7b4..668a8742 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -21,6 +21,7 @@ #include "pool-buffer.h" #include "cairo.h" #include "log.h" +#include "readline.h" #include "stringop.h" #include "util.h" #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" @@ -34,14 +35,16 @@ void sway_terminate(int exit_code) { static void daemonize() { int fds[2]; if (pipe(fds) != 0) { - wlr_log(L_ERROR, "Failed to pipe"); + wlr_log(WLR_ERROR, "Failed to pipe"); exit(1); } if (fork() == 0) { + setsid(); close(fds[0]); int devnull = open("/dev/null", O_RDWR); dup2(STDOUT_FILENO, devnull); dup2(STDERR_FILENO, devnull); + close(devnull); uint8_t success = 0; if (chdir("/") != 0) { write(fds[1], &success, 1); @@ -56,7 +59,7 @@ static void daemonize() { close(fds[1]); uint8_t success; if (read(fds[0], &success, 1) != 1 || !success) { - wlr_log(L_ERROR, "Failed to daemonize"); + wlr_log(WLR_ERROR, "Failed to daemonize"); exit(1); } close(fds[0]); @@ -83,6 +86,13 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener; static cairo_surface_t *select_image(struct swaylock_state *state, struct swaylock_surface *surface); +static bool surface_is_opaque(struct swaylock_surface *surface) { + if (surface->image) { + return cairo_surface_get_content(surface->image) == CAIRO_CONTENT_COLOR; + } + return (surface->state->args.colors.background & 0xff) == 0xff; +} + static void create_layer_surface(struct swaylock_surface *surface) { struct swaylock_state *state = surface->state; @@ -107,6 +117,17 @@ static void create_layer_surface(struct swaylock_surface *surface) { surface->layer_surface, true); zwlr_layer_surface_v1_add_listener(surface->layer_surface, &layer_surface_listener, surface); + + if (surface_is_opaque(surface) && + surface->state->args.mode != BACKGROUND_MODE_CENTER && + surface->state->args.mode != BACKGROUND_MODE_FIT) { + struct wl_region *region = + wl_compositor_create_region(surface->state->compositor); + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_opaque_region(surface->surface, region); + wl_region_destroy(region); + } + wl_surface_commit(surface->surface); } @@ -218,7 +239,7 @@ static void handle_xdg_output_logical_position(void *data, static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output, const char *name) { - wlr_log(L_DEBUG, "output name is %s", name); + wlr_log(WLR_DEBUG, "output name is %s", name); struct swaylock_surface *surface = data; surface->xdg_output = output; surface->output_name = strdup(name); @@ -324,22 +345,25 @@ static void load_image(char *arg, struct swaylock_state *state) { image->path = strdup(arg); } - bool exists = false; - struct swaylock_image *iter_image; - wl_list_for_each(iter_image, &state->images, link) { + struct swaylock_image *iter_image, *temp; + wl_list_for_each_safe(iter_image, temp, &state->images, link) { if (lenient_strcmp(iter_image->output_name, image->output_name) == 0) { - exists = true; + if (image->output_name) { + wlr_log(WLR_DEBUG, + "Replacing image defined for output %s with %s", + image->output_name, image->path); + } else { + wlr_log(WLR_DEBUG, "Replacing default image with %s", + image->path); + } + wl_list_remove(&iter_image->link); + free(iter_image->cairo_surface); + free(iter_image->output_name); + free(iter_image->path); + free(iter_image); break; } } - if (exists) { - if (image->output_name) { - wlr_log(L_ERROR, "Multiple images defined for output %s", - image->output_name); - } else { - wlr_log(L_ERROR, "Multiple default images defined"); - } - } // Bash doesn't replace the ~ with $HOME if the output name is supplied wordexp_t p; @@ -357,71 +381,252 @@ static void load_image(char *arg, struct swaylock_state *state) { } wl_list_insert(&state->images, &image->link); state->args.mode = BACKGROUND_MODE_FILL; - wlr_log(L_DEBUG, "Loaded image %s for output %s", + wlr_log(WLR_DEBUG, "Loaded image %s for output %s", image->path, image->output_name ? image->output_name : "*"); } -static struct swaylock_state state; +static void set_default_colors(struct swaylock_colors *colors) { + colors->background = 0xFFFFFFFF; + colors->bs_highlight = 0xDB3300FF; + colors->key_highlight = 0x33DB00FF; + colors->separator = 0x000000FF; + colors->inside = (struct swaylock_colorset){ + .input = 0x000000C0, + .cleared = 0xE5A445C0, + .verifying = 0x0072FFC0, + .wrong = 0xFA0000C0, + }; + colors->line = (struct swaylock_colorset){ + .input = 0x000000FF, + .cleared = 0x000000FF, + .verifying = 0x000000FF, + .wrong = 0x000000FF, + }; + colors->ring = (struct swaylock_colorset){ + .input = 0x337D00FF, + .cleared = 0xE5A445FF, + .verifying = 0x3300FFFF, + .wrong = 0x7D3300FF, + }; + colors->text = (struct swaylock_colorset){ + .input = 0xE5A445FF, + .cleared = 0x000000FF, + .verifying = 0x000000FF, + .wrong = 0x000000FF, + }; +} + +enum line_mode { + LM_LINE, + LM_INSIDE, + LM_RING, +}; + +static int parse_options(int argc, char **argv, struct swaylock_state *state, + enum line_mode *line_mode, char **config_path) { + enum long_option_codes { + LO_BS_HL_COLOR = 256, + LO_FONT, + LO_IND_RADIUS, + LO_IND_THICKNESS, + LO_INSIDE_COLOR, + LO_INSIDE_CLEAR_COLOR, + LO_INSIDE_VER_COLOR, + LO_INSIDE_WRONG_COLOR, + LO_KEY_HL_COLOR, + LO_LINE_COLOR, + LO_LINE_CLEAR_COLOR, + LO_LINE_VER_COLOR, + LO_LINE_WRONG_COLOR, + LO_RING_COLOR, + LO_RING_CLEAR_COLOR, + LO_RING_VER_COLOR, + LO_RING_WRONG_COLOR, + LO_SEP_COLOR, + LO_TEXT_COLOR, + LO_TEXT_CLEAR_COLOR, + LO_TEXT_VER_COLOR, + LO_TEXT_WRONG_COLOR, + }; -int main(int argc, char **argv) { static struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, + {"config", required_argument, NULL, 'C'}, {"color", required_argument, NULL, 'c'}, + {"ignore-empty-password", no_argument, NULL, 'e'}, + {"daemonize", no_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, {"image", required_argument, NULL, 'i'}, + {"line-uses-inside", no_argument, NULL, 'n'}, + {"socket", required_argument, NULL, 'p'}, + {"line-uses-ring", no_argument, NULL, 'r'}, {"scaling", required_argument, NULL, 's'}, {"tiling", no_argument, NULL, 't'}, - {"version", no_argument, NULL, 'v'}, - {"socket", required_argument, NULL, 'p'}, {"no-unlock-indicator", no_argument, NULL, 'u'}, - {"daemonize", no_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'v'}, + {"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR}, + {"font", required_argument, NULL, LO_FONT}, + {"indicator-radius", required_argument, NULL, LO_IND_RADIUS}, + {"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS}, + {"inside-color", required_argument, NULL, LO_INSIDE_COLOR}, + {"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR}, + {"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR}, + {"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR}, + {"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR}, + {"line-color", required_argument, NULL, LO_LINE_COLOR}, + {"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR}, + {"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR}, + {"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR}, + {"ring-color", required_argument, NULL, LO_RING_COLOR}, + {"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR}, + {"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR}, + {"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR}, + {"separator-color", required_argument, NULL, LO_SEP_COLOR}, + {"text-color", required_argument, NULL, LO_TEXT_COLOR}, + {"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR}, + {"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR}, + {"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR}, {0, 0, 0, 0} }; const char usage[] = "Usage: swaylock [options...]\n" "\n" - " -h, --help Show help message and quit.\n" - " -c, --color Turn the screen into the given color instead of white.\n" - " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n" - " -t, --tiling Same as --scaling=tile.\n" - " -v, --version Show the version number and quit.\n" - " -i, --image [:] Display the given image.\n" - " -u, --no-unlock-indicator Disable the unlock indicator.\n" - " -f, --daemonize Detach from the controlling terminal.\n"; - - state.args = (struct swaylock_args){ - .mode = BACKGROUND_MODE_SOLID_COLOR, - .color = 0xFFFFFFFF, - .show_indicator = true, - }; - wl_list_init(&state.images); - - wlr_log_init(L_DEBUG, NULL); + " -C, --config " + "Path to the config file.\n" + " -c, --color " + "Turn the screen into the given color instead of white.\n" + " -e, --ignore-empty-password " + "When an empty password is provided, do not validate it.\n" + " -f, --daemonize " + "Detach from the controlling terminal after locking.\n" + " -h, --help " + "Show help message and quit.\n" + " -i, --image [:] " + "Display the given image.\n" + " -s, --scaling " + "Scaling mode: stretch, fill, fit, center, tile.\n" + " -t, --tiling " + "Same as --scaling=tile.\n" + " -u, --no-unlock-indicator " + "Disable the unlock indicator.\n" + " -v, --version " + "Show the version number and quit.\n" + " --bs-hl-color " + "Sets the color of backspace highlight segments.\n" + " --font " + "Sets the font of the text.\n" + " --indicator-radius " + "Sets the indicator radius.\n" + " --indicator-thickness " + "Sets the indicator thickness.\n" + " --inside-color " + "Sets the color of the inside of the indicator.\n" + " --inside-clear-color " + "Sets the color of the inside of the indicator when cleared.\n" + " --inside-ver-color " + "Sets the color of the inside of the indicator when verifying.\n" + " --inside-wrong-color " + "Sets the color of the inside of the indicator when invalid.\n" + " --key-hl-color " + "Sets the color of the key press highlight segments.\n" + " --line-color " + "Sets the color of the line between the inside and ring.\n" + " --line-clear-color " + "Sets the color of the line between the inside and ring when " + "cleared.\n" + " --line-ver-color " + "Sets the color of the line between the inside and ring when " + "verifying.\n" + " --line-wrong-color " + "Sets the color of the line between the inside and ring when " + "invalid.\n" + " -n, --line-uses-inside " + "Use the inside color for the line between the inside and ring.\n" + " -r, --line-uses-ring " + "Use the ring color for the line between the inside and ring.\n" + " --ring-color " + "Sets the color of the ring of the indicator.\n" + " --ring-clear-color " + "Sets the color of the ring of the indicator when cleared.\n" + " --ring-ver-color " + "Sets the color of the ring of the indicator when verifying.\n" + " --ring-wrong-color " + "Sets the color of the ring of the indicator when invalid.\n" + " --separator-color " + "Sets the color of the lines that separate highlight segments.\n" + " --text-color " + "Sets the color of the text.\n" + " --text-clear-color " + "Sets the color of the text when cleared.\n" + " --text-ver-color " + "Sets the color of the text when verifying.\n" + " --text-wrong-color " + "Sets the color of the text when invalid.\n" + "\n" + "All options are of the form .\n"; int c; + optind = 1; while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index); + int opt_idx = 0; + c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx); if (c == -1) { break; } switch (c) { - case 'c': { - state.args.color = parse_color(optarg); - state.args.mode = BACKGROUND_MODE_SOLID_COLOR; + case 'C': + if (config_path) { + *config_path = strdup(optarg); + } + break; + case 'c': + if (state) { + state->args.colors.background = parse_color(optarg); + state->args.mode = BACKGROUND_MODE_SOLID_COLOR; + } + break; + case 'e': + if (state) { + state->args.ignore_empty = true; + } + break; + case 'f': + if (state) { + state->args.daemonize = true; + } break; - } case 'i': - load_image(optarg, &state); + if (state) { + load_image(optarg, state); + } + break; + case 'n': + if (line_mode) { + *line_mode = LM_INSIDE; + } + break; + case 'r': + if (line_mode) { + *line_mode = LM_RING; + } break; case 's': - state.args.mode = parse_background_mode(optarg); - if (state.args.mode == BACKGROUND_MODE_INVALID) { - return 1; + if (state) { + state->args.mode = parse_background_mode(optarg); + if (state->args.mode == BACKGROUND_MODE_INVALID) { + return 1; + } } break; case 't': - state.args.mode = BACKGROUND_MODE_TILE; + if (state) { + state->args.mode = BACKGROUND_MODE_TILE; + } + break; + case 'u': + if (state) { + state->args.show_indicator = false; + } break; case 'v': #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE @@ -430,12 +635,117 @@ int main(int argc, char **argv) { #else fprintf(stdout, "version unknown\n"); #endif - return 0; - case 'u': - state.args.show_indicator = false; + return 1; + case LO_BS_HL_COLOR: + if (state) { + state->args.colors.bs_highlight = parse_color(optarg); + } break; - case 'f': - daemonize(); + case LO_FONT: + if (state) { + free(state->args.font); + state->args.font = strdup(optarg); + } + break; + case LO_IND_RADIUS: + if (state) { + state->args.radius = strtol(optarg, NULL, 0); + } + break; + case LO_IND_THICKNESS: + if (state) { + state->args.thickness = strtol(optarg, NULL, 0); + } + break; + case LO_INSIDE_COLOR: + if (state) { + state->args.colors.inside.input = parse_color(optarg); + } + break; + case LO_INSIDE_CLEAR_COLOR: + if (state) { + state->args.colors.inside.cleared = parse_color(optarg); + } + break; + case LO_INSIDE_VER_COLOR: + if (state) { + state->args.colors.inside.verifying = parse_color(optarg); + } + break; + case LO_INSIDE_WRONG_COLOR: + if (state) { + state->args.colors.inside.wrong = parse_color(optarg); + } + break; + case LO_KEY_HL_COLOR: + if (state) { + state->args.colors.key_highlight = parse_color(optarg); + } + break; + case LO_LINE_COLOR: + if (state) { + state->args.colors.line.input = parse_color(optarg); + } + break; + case LO_LINE_CLEAR_COLOR: + if (state) { + state->args.colors.line.cleared = parse_color(optarg); + } + break; + case LO_LINE_VER_COLOR: + if (state) { + state->args.colors.line.verifying = parse_color(optarg); + } + break; + case LO_LINE_WRONG_COLOR: + if (state) { + state->args.colors.line.wrong = parse_color(optarg); + } + break; + case LO_RING_COLOR: + if (state) { + state->args.colors.ring.input = parse_color(optarg); + } + break; + case LO_RING_CLEAR_COLOR: + if (state) { + state->args.colors.ring.cleared = parse_color(optarg); + } + break; + case LO_RING_VER_COLOR: + if (state) { + state->args.colors.ring.verifying = parse_color(optarg); + } + break; + case LO_RING_WRONG_COLOR: + if (state) { + state->args.colors.ring.wrong = parse_color(optarg); + } + break; + case LO_SEP_COLOR: + if (state) { + state->args.colors.separator = parse_color(optarg); + } + break; + case LO_TEXT_COLOR: + if (state) { + state->args.colors.text.input = parse_color(optarg); + } + break; + case LO_TEXT_CLEAR_COLOR: + if (state) { + state->args.colors.text.cleared = parse_color(optarg); + } + break; + case LO_TEXT_VER_COLOR: + if (state) { + state->args.colors.text.verifying = parse_color(optarg); + } + break; + case LO_TEXT_WRONG_COLOR: + if (state) { + state->args.colors.text.wrong = parse_color(optarg); + } break; default: fprintf(stderr, "%s", usage); @@ -443,6 +753,141 @@ int main(int argc, char **argv) { } } + return 0; +} + +static bool file_exists(const char *path) { + return path && access(path, R_OK) != -1; +} + +static char *get_config_path(void) { + static const char *config_paths[] = { + "$HOME/.swaylock/config", + "$XDG_CONFIG_HOME/swaylock/config", + SYSCONFDIR "/swaylock/config", + }; + + if (!getenv("XDG_CONFIG_HOME")) { + char *home = getenv("HOME"); + char *config_home = malloc(strlen(home) + strlen("/.config") + 1); + if (!config_home) { + wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); + } else { + strcpy(config_home, home); + strcat(config_home, "/.config"); + setenv("XDG_CONFIG_HOME", config_home, 1); + wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); + free(config_home); + } + } + + wordexp_t p; + char *path; + for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { + if (wordexp(config_paths[i], &p, 0) == 0) { + path = strdup(p.we_wordv[0]); + wordfree(&p); + if (file_exists(path)) { + return path; + } + free(path); + } + } + + return NULL; +} + +static int load_config(char *path, struct swaylock_state *state, + enum line_mode *line_mode) { + FILE *config = fopen(path, "r"); + if (!config) { + wlr_log(WLR_ERROR, "Failed to read config. Running without it."); + return 0; + } + char *line; + int line_number = 0; + while (!feof(config)) { + line = read_line(config); + if (!line) { + continue; + } + + line_number++; + if (line[0] == '#') { + free(line); + continue; + } + if (strlen(line) == 0) { + free(line); + continue; + } + + wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line); + char flag[strlen(line) + 3]; + sprintf(flag, "--%s", line); + char *argv[] = {"swaylock", flag}; + int result = parse_options(2, argv, state, line_mode, NULL); + if (result != 0) { + free(line); + fclose(config); + return result; + } + free(line); + } + fclose(config); + return 0; +} + +static struct swaylock_state state; + +int main(int argc, char **argv) { + enum line_mode line_mode = LM_LINE; + state.args = (struct swaylock_args){ + .mode = BACKGROUND_MODE_SOLID_COLOR, + .font = strdup("sans-serif"), + .radius = 50, + .thickness = 10, + .ignore_empty = false, + .show_indicator = true, + }; + wl_list_init(&state.images); + set_default_colors(&state.args.colors); + + wlr_log_init(WLR_DEBUG, NULL); + + char *config_path = NULL; + int result = parse_options(argc, argv, NULL, NULL, &config_path); + if (result != 0) { + free(config_path); + return result; + } + if (!config_path) { + config_path = get_config_path(); + } + + if (config_path) { + wlr_log(WLR_DEBUG, "Found config at %s", config_path); + int config_status = load_config(config_path, &state, &line_mode); + free(config_path); + if (config_status != 0) { + return config_status; + } + } + + if (argc > 1) { + wlr_log(WLR_DEBUG, "Parsing CLI Args"); + int result = parse_options(argc, argv, &state, &line_mode, NULL); + if (result != 0) { + return result; + } + } + + if (line_mode == LM_INSIDE) { + state.args.colors.line = state.args.colors.inside; + } else if (line_mode == LM_RING) { + state.args.colors.line = state.args.colors.ring; + } + #ifdef __linux__ // Most non-linux platforms require root to mlock() if (mlock(state.password.buffer, sizeof(state.password.buffer)) != 0) { @@ -460,13 +905,13 @@ int main(int argc, char **argv) { wl_display_roundtrip(state.display); assert(state.compositor && state.layer_shell && state.shm); if (!state.input_inhibit_manager) { - wlr_log(L_ERROR, "Compositor does not support the input inhibitor " + wlr_log(WLR_ERROR, "Compositor does not support the input inhibitor " "protocol, refusing to run insecurely"); return 1; } if (wl_list_empty(&state.surfaces)) { - wlr_log(L_DEBUG, "Exiting - no outputs to show on."); + wlr_log(WLR_DEBUG, "Exiting - no outputs to show on."); return 0; } @@ -482,7 +927,7 @@ int main(int argc, char **argv) { } wl_display_roundtrip(state.display); } else { - wlr_log(L_INFO, "Compositor does not support zxdg output manager, " + wlr_log(WLR_INFO, "Compositor does not support zxdg output manager, " "images assigned to named outputs will not work"); } @@ -491,9 +936,16 @@ int main(int argc, char **argv) { create_layer_surface(surface); } + if (state.args.daemonize) { + wl_display_roundtrip(state.display); + daemonize(); + } + state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank } + + free(state.args.font); return 0; } diff --git a/swaylock/password.c b/swaylock/password.c index bb32286e..7c686b34 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -53,15 +53,15 @@ static bool attempt_password(struct swaylock_password *pw) { // TODO: only call pam_start once. keep the same handle the whole time if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { - wlr_log(L_ERROR, "PAM returned error %d", pam_err); + wlr_log(WLR_ERROR, "PAM returned error %d", pam_err); } if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { - wlr_log(L_ERROR, "pam_authenticate failed"); + wlr_log(WLR_ERROR, "pam_authenticate failed"); goto fail; } // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { - wlr_log(L_ERROR, "pam_end failed"); + wlr_log(WLR_ERROR, "pam_end failed"); goto fail; } clear_password_buffer(pw); @@ -95,9 +95,26 @@ void swaylock_handle_key(struct swaylock_state *state, switch (keysym) { case XKB_KEY_KP_Enter: /* fallthrough */ case XKB_KEY_Return: + if (state->args.ignore_empty && state->password.len == 0) { + break; + } + state->auth_state = AUTH_STATE_VALIDATING; damage_state(state); - wl_display_roundtrip(state->display); + while (wl_display_dispatch(state->display) != -1 && state->run_display) { + bool ok = 1; + struct swaylock_surface *surface; + wl_list_for_each(surface, &state->surfaces, link) { + if (surface->dirty) { + ok = 0; + } + } + if (ok) { + break; + } + } + wl_display_flush(state->display); + if (attempt_password(&state->password)) { state->run_display = false; break; diff --git a/swaylock/render.c b/swaylock/render.c index 2032ddcf..66c55965 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -7,11 +7,22 @@ #include "swaylock/swaylock.h" #define M_PI 3.14159265358979323846 -const int ARC_RADIUS = 50; -const int ARC_THICKNESS = 10; const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; +static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state, + struct swaylock_colorset *colorset) { + if (state->auth_state == AUTH_STATE_VALIDATING) { + cairo_set_source_u32(cairo, colorset->verifying); + } else if (state->auth_state == AUTH_STATE_INVALID) { + cairo_set_source_u32(cairo, colorset->wrong); + } else if (state->auth_state == AUTH_STATE_CLEAR) { + cairo_set_source_u32(cairo, colorset->cleared); + } else { + cairo_set_source_u32(cairo, colorset->input); + } +} + void render_frame(struct swaylock_surface *surface) { struct swaylock_state *state = surface->state; @@ -30,58 +41,37 @@ void render_frame(struct swaylock_surface *surface) { cairo_t *cairo = surface->current_buffer->cairo; cairo_identity_matrix(cairo); + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { - cairo_set_source_u32(cairo, state->args.color); + cairo_set_source_u32(cairo, state->args.colors.background); cairo_paint(cairo); } else { render_background_image(cairo, surface->image, state->args.mode, buffer_width, buffer_height); } + cairo_restore(cairo); cairo_identity_matrix(cairo); - int arc_radius = ARC_RADIUS * surface->scale; - int arc_thickness = ARC_THICKNESS * surface->scale; + int arc_radius = state->args.radius * surface->scale; + int arc_thickness = state->args.thickness * surface->scale; float type_indicator_border_thickness = TYPE_INDICATOR_BORDER_THICKNESS * surface->scale; if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { // Draw circle cairo_set_line_width(cairo, arc_thickness); - cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI); - switch (state->auth_state) { - case AUTH_STATE_INPUT: - case AUTH_STATE_INPUT_NOP: - case AUTH_STATE_BACKSPACE: { - cairo_set_source_rgba(cairo, 0, 0, 0, 0.75); - cairo_fill_preserve(cairo); - cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0); - cairo_stroke(cairo); - } break; - case AUTH_STATE_VALIDATING: { - cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75); - cairo_fill_preserve(cairo); - cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255); - cairo_stroke(cairo); - } break; - case AUTH_STATE_INVALID: { - cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75); - cairo_fill_preserve(cairo); - cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0); - cairo_stroke(cairo); - } break; - case AUTH_STATE_CLEAR: { - cairo_set_source_rgba(cairo, 229.0/255, 164.0/255, 69.0/255, 0.75); - cairo_fill_preserve(cairo); - cairo_set_source_rgb(cairo, 229.0/255, 164.0/255, 69.0/255); - cairo_stroke(cairo); - } break; - default: break; - } + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, + 0, 2 * M_PI); + set_color_for_state(cairo, state, &state->args.colors.inside); + cairo_fill_preserve(cairo); + set_color_for_state(cairo, state, &state->args.colors.ring); + cairo_stroke(cairo); // Draw a message char *text = NULL; - cairo_set_source_rgb(cairo, 0, 0, 0); - cairo_select_font_face(cairo, "sans-serif", + set_color_for_state(cairo, state, &state->args.colors.text); + cairo_select_font_face(cairo, state->args.font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cairo, arc_radius / 3.0f); switch (state->auth_state) { @@ -98,9 +88,10 @@ void render_frame(struct swaylock_surface *surface) { case AUTH_STATE_INPUT_NOP: if (state->xkb.caps_lock) { text = "Caps Lock"; - cairo_set_source_rgb(cairo, 229.0/255, 164.0/255, 69.0/255); } - default: break; + break; + default: + break; } if (text) { @@ -128,14 +119,14 @@ void render_frame(struct swaylock_surface *surface) { arc_radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE); if (state->auth_state == AUTH_STATE_INPUT) { - cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0); + cairo_set_source_u32(cairo, state->args.colors.key_highlight); } else { - cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0); + cairo_set_source_u32(cairo, state->args.colors.bs_highlight); } cairo_stroke(cairo); // Draw borders - cairo_set_source_rgb(cairo, 0, 0, 0); + cairo_set_source_u32(cairo, state->args.colors.separator); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, highlight_start, highlight_start + type_indicator_border_thickness); @@ -149,7 +140,7 @@ void render_frame(struct swaylock_surface *surface) { } // Draw inner + outer border of the circle - cairo_set_source_rgb(cairo, 0, 0, 0); + set_color_for_state(cairo, state, &state->args.colors.line); cairo_set_line_width(cairo, 2.0 * surface->scale); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius - arc_thickness / 2, 0, 2 * M_PI); diff --git a/swaylock/seat.c b/swaylock/seat.c index 6c66d220..c2630d87 100644 --- a/swaylock/seat.c +++ b/swaylock/seat.c @@ -12,13 +12,13 @@ static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, struct swaylock_state *state = data; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { close(fd); - wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format); + wlr_log(WLR_ERROR, "Unknown keymap format %d, aborting", format); exit(1); } char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (map_shm == MAP_FAILED) { close(fd); - wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting"); + wlr_log(WLR_ERROR, "Unable to initialize keymap shm, aborting"); exit(1); } struct xkb_keymap *keymap = xkb_keymap_new_from_string( diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd index 35d6444c..3107124f 100644 --- a/swaylock/swaylock.1.scd +++ b/swaylock/swaylock.1.scd @@ -12,23 +12,34 @@ Locks your Wayland session. # OPTIONS -*-h, --help* - Show help message and quit. +*-C, --config* + The config file to use. By default, the following paths are checked: + _$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and + _SYSCONFDIR/swaylock/config_. All flags aside from this one are valid + options in the configuration file using the format _long-option=value_. + For options such as _ignore-empty-password_, just supply the _long-option_. + All leading dashes should be omitted and the equals sign is required for + flags that take an argument. *-c, --color* Turn the screen into the given color. If -i is used, this sets the background of the image to the given color. Defaults to white (FFFFFF), or transparent (00000000) if an image is in use. +*-e, --ignore-empty-password* + When an empty password is provided by the user, do not validate it. + *-f, --daemonize* - Fork into the background after spawning. Note: this is the default behavior - of i3lock. + Detach from the controlling terminal after locking. + +*-h, --help* + Show help message and quit. *-i, --image* [:] Display the given image, optionally only on the given output. Use -c to set a background color. -*--scaling* +*-s, --scaling* Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. *-t, --tiling* @@ -42,51 +53,12 @@ Locks your Wayland session. # APPEARANCE -*--bshlcolor* +*--bs-hl-color* Sets the color of backspace highlight segments. *--font* Sets the font of the text inside the indicator. -*--insidecolor* - Sets the color of the inside of the indicator when typing or idle. - -*--insidevercolor* - Sets the color of the inside of the indicator when verifying. - -*--insidewrongcolor* - Sets the color of the inside of the indicator when invalid. - -*--keyhlcolor* - Sets the color of keypress highlight segments. - -*--linecolor* - Sets the color of the lines that separate the inside and outside of the - indicator. - -*-s, --line-uses-inside* - Use the color of the inside of the indicator for the line separating the - inside and outside of the indicator. - -*-r, --line-uses-ring* - Use the outer ring's color for the line separating the inside and outside of - the indicator. - -*--ringcolor* - Sets the color of the outside of the indicator when typing or idle. - -*--ringvercolor* - Sets the color of the outside of the indicator when verifying. - -*--ringwrongcolor* - Sets the color of the outside of the indicator when invalid. - -*--separatorcolor* - Sets the color of the lines that seperate highlight segments. - -*--textcolor* - Sets the color of the text inside the indicator. - *--indicator-radius* Sets the radius of the indicator to _radius_ pixels. The default value is 50. @@ -95,6 +67,72 @@ Locks your Wayland session. Sets the thickness of the indicator to _thickness_ pixels. The default value is 10. +*--inside-color* + Sets the color of the inside of the indicator when typing or idle. + +*--inside-clear-color* + Sets the color of the inside of the indicator when cleared. + +*--inside-ver-color* + Sets the color of the inside of the indicator when verifying. + +*--inside-wrong-color* + Sets the color of the inside of the indicator when invalid. + +*--key-hl-color* + Sets the color of key press highlight segments. + +*--line-color* + Sets the color of the lines that separate the inside and outside of the + indicator when typing or idle. + +*--line-clear-color* + Sets the color of the lines that separate the inside and outside of the + indicator when cleared. + +*--line-ver-color* + Sets the color of the lines that separate the inside and outside of the + indicator when verifying. + +*--line-wrong-color* + Sets the color of the lines that separate the inside and outside of the + indicator when invalid. + +*-n, --line-uses-inside* + Use the color of the inside of the indicator for the line separating the + inside and outside of the indicator. + +*-r, --line-uses-ring* + Use the outer ring's color for the line separating the inside and outside of + the indicator. + +*--ring-color* + Sets the color of the outside of the indicator when typing or idle. + +*--ring-clear-color* + Sets the color of the outside of the indicator when cleared. + +*--ring-ver-color* + Sets the color of the outside of the indicator when verifying. + +*--ring-wrong-color* + Sets the color of the outside of the indicator when invalid. + +*--separator-color* + Sets the color of the lines that separate highlight segments. + +*--text-color* + Sets the color of the text inside the indicator when typing or idle. + +*--text-clear-color* + Sets the color of the text inside the indicator when cleared. + +*--text-ver-color* + Sets the color of the text inside the indicator when verifying. + +*--text-wrong-color* + Sets the color of the text inside the indicator when invalid. + # AUTHORS Maintained by Drew DeVault , who is assisted by other open diff --git a/swaymsg/main.c b/swaymsg/main.c index 4283bf00..c4141ca5 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -240,44 +240,17 @@ static void pretty_print_version(json_object *v) { printf("sway version %s\n", json_object_get_string(ver)); } -static void pretty_print_clipboard(json_object *v) { - if (success(v, true)) { - if (json_object_is_type(v, json_type_array)) { - for (size_t i = 0; i < json_object_array_length(v); ++i) { - json_object *o = json_object_array_get_idx(v, i); - printf("%s\n", json_object_get_string(o)); - } - } else { - // NOTE: could be extended to print all received types - // instead just the first one when sways ipc server - // supports it - struct json_object_iterator iter = json_object_iter_begin(v); - struct json_object_iterator end = json_object_iter_end(v); - if (!json_object_iter_equal(&iter, &end)) { - json_object *obj = json_object_iter_peek_value(&iter); - if (success(obj, false)) { - json_object *content; - json_object_object_get_ex(obj, "content", &content); - printf("%s\n", json_object_get_string(content)); - } else { - json_object *error; - json_object_object_get_ex(obj, "error", &error); - printf("Error: %s\n", json_object_get_string(error)); - } - } - } - } else { - json_object *error; - json_object_object_get_ex(v, "error", &error); - printf("Error: %s\n", json_object_get_string(error)); - } +static void pretty_print_config(json_object *c) { + json_object *config; + json_object_object_get_ex(c, "config", &config); + printf("%s\n", json_object_get_string(config)); } static void pretty_print(int type, json_object *resp) { if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && - type != IPC_GET_VERSION && type != IPC_GET_CLIPBOARD && - type != IPC_GET_SEATS) { + type != IPC_GET_VERSION && type != IPC_GET_SEATS && + type != IPC_GET_CONFIG) { printf("%s\n", json_object_to_json_string_ext(resp, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); return; @@ -288,8 +261,8 @@ static void pretty_print(int type, json_object *resp) { return; } - if (type == IPC_GET_CLIPBOARD) { - pretty_print_clipboard(resp); + if (type == IPC_GET_CONFIG) { + pretty_print_config(resp); return; } @@ -323,7 +296,7 @@ int main(int argc, char **argv) { char *socket_path = NULL; char *cmdtype = NULL; - wlr_log_init(L_INFO, NULL); + wlr_log_init(WLR_INFO, NULL); static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, @@ -407,8 +380,10 @@ int main(int argc, char **argv) { type = IPC_GET_BAR_CONFIG; } else if (strcasecmp(cmdtype, "get_version") == 0) { type = IPC_GET_VERSION; - } else if (strcasecmp(cmdtype, "get_clipboard") == 0) { - type = IPC_GET_CLIPBOARD; + } else if (strcasecmp(cmdtype, "get_binding_modes") == 0) { + type = IPC_GET_BINDING_MODES; + } else if (strcasecmp(cmdtype, "get_config") == 0) { + type = IPC_GET_CONFIG; } else { sway_abort("Unknown message type %s", cmdtype); } diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 1aa6a1b0..a6e279da 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -59,8 +59,8 @@ _swaymsg_ [options...] [message] *get\_version* Get JSON-encoded version information for the running instance of sway. -*get\_clipboard* - Get JSON-encoded information about the clipboard. - Returns the current clipboard mime-types if called without - arguments, otherwise returns the clipboard data in the requested - formats. Encodes the data using base64 for non-text mime types. +*get\_binding\_modes* + Gets a JSON-encoded list of currently configured binding modes. + +*get\_config* + Gets a JSON-encoded copy of the current configuration.