Compare commits
43 commits
b7d2d56263
...
a81e80fe68
Author | SHA1 | Date | |
---|---|---|---|
Alex Janka | a81e80fe68 | ||
Alex Janka | 5293fb9206 | ||
Alex Janka | d01f16d09c | ||
59a10cf4c7 | |||
59c2b7a884 | |||
4d7ca7d975 | |||
358d649412 | |||
5ace6bb7bd | |||
60ecc42e42 | |||
dec948dc80 | |||
8193bb6e4b | |||
84c1b9c7f3 | |||
5bb8877abe | |||
d7ca31c596 | |||
4f9ac2dfbb | |||
b491ac42b0 | |||
1fedb1a1ee | |||
0da822151d | |||
a12a8ea7b0 | |||
7214db9c2d | |||
bd3745c6ed | |||
83e0872e79 | |||
0a619a03ab | |||
54daaa9ed2 | |||
42fcd6c86d | |||
288b04d740 | |||
1113e5653a | |||
0b521abe88 | |||
519bfc4ecc | |||
3480479f17 | |||
067613854b | |||
fdf764d3f6 | |||
29c481da95 | |||
3ad50f9627 | |||
8da7c5080c | |||
9e551680db | |||
8afad34fd5 | |||
8440285e8d | |||
44f54b9933 | |||
0c6d69b7fe | |||
6552778dd6 | |||
0b86078eea | |||
4e9e7c8d1a |
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": false,
|
||||||
|
"C_Cpp.default.compileCommands": "/home/alex/forks/swayfx/builddir/compile_commands.json",
|
||||||
|
"C_Cpp.default.configurationProvider": "mesonbuild.mesonbuild",
|
||||||
|
"files.associations": {
|
||||||
|
"*.h": "c"
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,6 +90,60 @@ char *gesture_parse(const char *input, struct gesture *output) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Similar to gesture_parse but with fewer checks to match the different
|
||||||
|
// workspace_gesture syntax
|
||||||
|
char *workspace_gesture_parse(const char *input, struct gesture *output) {
|
||||||
|
// Clear output in case of failure
|
||||||
|
output->type = GESTURE_TYPE_NONE;
|
||||||
|
output->fingers = GESTURE_FINGERS_ANY;
|
||||||
|
output->directions = GESTURE_DIRECTION_NONE;
|
||||||
|
|
||||||
|
// Split input type, fingers and directions
|
||||||
|
list_t *split = split_string(input, ":");
|
||||||
|
if (split->length < 1 || split->length > 2) {
|
||||||
|
return format_str(
|
||||||
|
"expected [:<fingers>][:direction], got %s",
|
||||||
|
input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse optional arguments
|
||||||
|
if (split->length > 1) {
|
||||||
|
char *next = split->items[0];
|
||||||
|
|
||||||
|
// Try to parse as finger count (1-9)
|
||||||
|
if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {
|
||||||
|
output->fingers = atoi(next);
|
||||||
|
|
||||||
|
// Move to next if available
|
||||||
|
next = split->length == 2 ? split->items[1] : NULL;
|
||||||
|
} else if (split->length == 2) {
|
||||||
|
// Fail here if argument can only be finger count
|
||||||
|
return format_str("expected 1-9, got %s", next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is an argument left, try to parse as direction
|
||||||
|
if (next) {
|
||||||
|
list_t *directions = split_string(next, "+");
|
||||||
|
|
||||||
|
for (int i = 0; i < directions->length; ++i) {
|
||||||
|
const char *item = directions->items[i];
|
||||||
|
if (strcmp(item, "horizontal") == 0) {
|
||||||
|
output->type = GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL;
|
||||||
|
} else if (strcmp(item, "vertical") == 0) {
|
||||||
|
output->type = GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL;
|
||||||
|
} else {
|
||||||
|
return format_str("expected direction, got %s", item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list_free_items_and_destroy(directions);
|
||||||
|
}
|
||||||
|
} // if optional args
|
||||||
|
|
||||||
|
list_free_items_and_destroy(split);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
const char *gesture_type_string(enum gesture_type type) {
|
const char *gesture_type_string(enum gesture_type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case GESTURE_TYPE_NONE:
|
case GESTURE_TYPE_NONE:
|
||||||
|
@ -100,6 +154,10 @@ const char *gesture_type_string(enum gesture_type type) {
|
||||||
return "pinch";
|
return "pinch";
|
||||||
case GESTURE_TYPE_SWIPE:
|
case GESTURE_TYPE_SWIPE:
|
||||||
return "swipe";
|
return "swipe";
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
|
||||||
|
return "workspace_swipe_horizontal";
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
|
||||||
|
return "workspace_swipe_vertical";
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -313,6 +371,8 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
|
||||||
}
|
}
|
||||||
// Gesture without any direction
|
// Gesture without any direction
|
||||||
case GESTURE_TYPE_HOLD:
|
case GESTURE_TYPE_HOLD:
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
|
||||||
break;
|
break;
|
||||||
// Not tracking any gesture
|
// Not tracking any gesture
|
||||||
case GESTURE_TYPE_NONE:
|
case GESTURE_TYPE_NONE:
|
||||||
|
|
|
@ -12,6 +12,8 @@ enum gesture_type {
|
||||||
GESTURE_TYPE_HOLD,
|
GESTURE_TYPE_HOLD,
|
||||||
GESTURE_TYPE_PINCH,
|
GESTURE_TYPE_PINCH,
|
||||||
GESTURE_TYPE_SWIPE,
|
GESTURE_TYPE_SWIPE,
|
||||||
|
GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL,
|
||||||
|
GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Turns single type enum value to constant string representation.
|
// Turns single type enum value to constant string representation.
|
||||||
|
@ -57,6 +59,13 @@ struct gesture {
|
||||||
*/
|
*/
|
||||||
char *gesture_parse(const char *input, struct gesture *output);
|
char *gesture_parse(const char *input, struct gesture *output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses workspace gesture from [:<fingers>][:<directions>] string.
|
||||||
|
*
|
||||||
|
* Return NULL on success, otherwise error message string
|
||||||
|
*/
|
||||||
|
char *workspace_gesture_parse(const char *input, struct gesture *output);
|
||||||
|
|
||||||
// Turns gesture into string representation
|
// Turns gesture into string representation
|
||||||
char *gesture_to_string(struct gesture *gesture);
|
char *gesture_to_string(struct gesture *gesture);
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,7 @@ sway_cmd cmd_bindcode;
|
||||||
sway_cmd cmd_bindgesture;
|
sway_cmd cmd_bindgesture;
|
||||||
sway_cmd cmd_bindswitch;
|
sway_cmd cmd_bindswitch;
|
||||||
sway_cmd cmd_bindsym;
|
sway_cmd cmd_bindsym;
|
||||||
|
sway_cmd cmd_bindworkspacegesture;
|
||||||
sway_cmd cmd_blur;
|
sway_cmd cmd_blur;
|
||||||
sway_cmd cmd_blur_brightness;
|
sway_cmd cmd_blur_brightness;
|
||||||
sway_cmd cmd_blur_contrast;
|
sway_cmd cmd_blur_contrast;
|
||||||
|
@ -223,11 +224,15 @@ sway_cmd cmd_unbindcode;
|
||||||
sway_cmd cmd_unbindswitch;
|
sway_cmd cmd_unbindswitch;
|
||||||
sway_cmd cmd_unbindgesture;
|
sway_cmd cmd_unbindgesture;
|
||||||
sway_cmd cmd_unbindsym;
|
sway_cmd cmd_unbindsym;
|
||||||
|
sway_cmd cmd_unbindworkspacegesture;
|
||||||
sway_cmd cmd_unmark;
|
sway_cmd cmd_unmark;
|
||||||
sway_cmd cmd_urgent;
|
sway_cmd cmd_urgent;
|
||||||
sway_cmd cmd_workspace;
|
sway_cmd cmd_workspace;
|
||||||
sway_cmd cmd_workspace_layout;
|
sway_cmd cmd_workspace_layout;
|
||||||
sway_cmd cmd_ws_auto_back_and_forth;
|
sway_cmd cmd_ws_auto_back_and_forth;
|
||||||
|
sway_cmd cmd_ws_gesture_spring_size;
|
||||||
|
sway_cmd cmd_ws_gesture_threshold;
|
||||||
|
sway_cmd cmd_ws_gesture_wrap_around;
|
||||||
sway_cmd cmd_xwayland;
|
sway_cmd cmd_xwayland;
|
||||||
|
|
||||||
sway_cmd bar_cmd_bindcode;
|
sway_cmd bar_cmd_bindcode;
|
||||||
|
|
|
@ -51,6 +51,7 @@ enum binding_flags {
|
||||||
BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
|
BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
|
||||||
BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key
|
BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key
|
||||||
BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match
|
BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match
|
||||||
|
BINDING_INVERTED = 1 << 10, // workspace gesture only; gesture is inverted
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -501,6 +502,10 @@ struct sway_config {
|
||||||
bool titlebar_separator;
|
bool titlebar_separator;
|
||||||
bool scratchpad_minimize;
|
bool scratchpad_minimize;
|
||||||
|
|
||||||
|
int workspace_gesture_spring_size;
|
||||||
|
bool workspace_gesture_wrap_around;
|
||||||
|
float workspace_gesture_threshold;
|
||||||
|
|
||||||
list_t *layer_criteria;
|
list_t *layer_criteria;
|
||||||
|
|
||||||
char *swaynag_command;
|
char *swaynag_command;
|
||||||
|
|
10
include/sway/fx_util/animation_utils.h
Normal file
10
include/sway/fx_util/animation_utils.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef ANIMATION_UTILS_H
|
||||||
|
#define ANIMATION_UTILS_H
|
||||||
|
|
||||||
|
double lerp (double a, double b, double t);
|
||||||
|
|
||||||
|
double ease_out_cubic (double t);
|
||||||
|
|
||||||
|
// TODO: Add more easing functions in the future like ease_in and ease_in_out, etc...
|
||||||
|
|
||||||
|
#endif
|
|
@ -15,6 +15,19 @@ struct decoration_data get_undecorated_decoration_data();
|
||||||
struct sway_server;
|
struct sway_server;
|
||||||
struct sway_container;
|
struct sway_container;
|
||||||
|
|
||||||
|
enum swipe_gesture_direction {
|
||||||
|
SWIPE_GESTURE_DIRECTION_NONE,
|
||||||
|
SWIPE_GESTURE_DIRECTION_HORIZONTAL,
|
||||||
|
SWIPE_GESTURE_DIRECTION_VERTICAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct workspace_scroll {
|
||||||
|
double percent;
|
||||||
|
double avg_velocity;
|
||||||
|
int num_updates;
|
||||||
|
enum swipe_gesture_direction direction;
|
||||||
|
};
|
||||||
|
|
||||||
struct decoration_data {
|
struct decoration_data {
|
||||||
float alpha;
|
float alpha;
|
||||||
float saturation;
|
float saturation;
|
||||||
|
@ -32,6 +45,7 @@ struct render_data {
|
||||||
pixman_region32_t *damage;
|
pixman_region32_t *damage;
|
||||||
struct wlr_box *clip_box;
|
struct wlr_box *clip_box;
|
||||||
struct decoration_data deco_data;
|
struct decoration_data deco_data;
|
||||||
|
bool on_focused_workspace;
|
||||||
struct sway_view *view;
|
struct sway_view *view;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,6 +85,8 @@ struct sway_output {
|
||||||
struct wl_listener needs_frame;
|
struct wl_listener needs_frame;
|
||||||
struct wl_listener request_state;
|
struct wl_listener request_state;
|
||||||
|
|
||||||
|
struct workspace_scroll workspace_scroll;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wl_signal disable;
|
struct wl_signal disable;
|
||||||
} events;
|
} events;
|
||||||
|
@ -227,6 +243,20 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
|
||||||
void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
|
struct workspace_scroll workspace_scroll_get_default();
|
||||||
|
|
||||||
|
bool workspace_scroll_equal(struct workspace_scroll *a, struct workspace_scroll *b);
|
||||||
|
|
||||||
|
void workspace_scroll_begin(struct sway_seat *seat,
|
||||||
|
enum swipe_gesture_direction direction);
|
||||||
|
|
||||||
|
void workspace_scroll_update(struct sway_seat *seat, struct gesture_tracker *tracker,
|
||||||
|
struct wlr_pointer_swipe_update_event *event, int invert);
|
||||||
|
|
||||||
|
void workspace_scroll_end(struct sway_seat *seat);
|
||||||
|
|
||||||
|
void workspace_scroll_reset(struct sway_seat *seat, struct sway_workspace *ws);
|
||||||
|
|
||||||
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
|
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -70,11 +70,13 @@ struct sway_workspace *workspace_by_number(const char* name);
|
||||||
|
|
||||||
struct sway_workspace *workspace_by_name(const char*);
|
struct sway_workspace *workspace_by_name(const char*);
|
||||||
|
|
||||||
struct sway_workspace *workspace_output_next(struct sway_workspace *current);
|
struct sway_workspace *workspace_output_next(struct sway_workspace *current,
|
||||||
|
bool should_wrap);
|
||||||
|
|
||||||
struct sway_workspace *workspace_next(struct sway_workspace *current);
|
struct sway_workspace *workspace_next(struct sway_workspace *current);
|
||||||
|
|
||||||
struct sway_workspace *workspace_output_prev(struct sway_workspace *current);
|
struct sway_workspace *workspace_output_prev(struct sway_workspace *current,
|
||||||
|
bool should_wrap);
|
||||||
|
|
||||||
struct sway_workspace *workspace_prev(struct sway_workspace *current);
|
struct sway_workspace *workspace_prev(struct sway_workspace *current);
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ static const struct cmd_handler handlers[] = {
|
||||||
{ "bindgesture", cmd_bindgesture },
|
{ "bindgesture", cmd_bindgesture },
|
||||||
{ "bindswitch", cmd_bindswitch },
|
{ "bindswitch", cmd_bindswitch },
|
||||||
{ "bindsym", cmd_bindsym },
|
{ "bindsym", cmd_bindsym },
|
||||||
|
{ "bindworkspacegesture", cmd_bindworkspacegesture },
|
||||||
{ "blur", cmd_blur },
|
{ "blur", cmd_blur },
|
||||||
{ "blur_brightness", cmd_blur_brightness },
|
{ "blur_brightness", cmd_blur_brightness },
|
||||||
{ "blur_contrast", cmd_blur_contrast },
|
{ "blur_contrast", cmd_blur_contrast },
|
||||||
|
@ -96,6 +97,7 @@ static const struct cmd_handler handlers[] = {
|
||||||
{ "no_focus", cmd_no_focus },
|
{ "no_focus", cmd_no_focus },
|
||||||
{ "output", cmd_output },
|
{ "output", cmd_output },
|
||||||
{ "popup_during_fullscreen", cmd_popup_during_fullscreen },
|
{ "popup_during_fullscreen", cmd_popup_during_fullscreen },
|
||||||
|
{ "primary_selection", cmd_primary_selection },
|
||||||
{ "seat", cmd_seat },
|
{ "seat", cmd_seat },
|
||||||
{ "set", cmd_set },
|
{ "set", cmd_set },
|
||||||
{ "shadow_blur_radius", cmd_shadow_blur_radius },
|
{ "shadow_blur_radius", cmd_shadow_blur_radius },
|
||||||
|
@ -118,8 +120,12 @@ static const struct cmd_handler handlers[] = {
|
||||||
{ "unbindgesture", cmd_unbindgesture },
|
{ "unbindgesture", cmd_unbindgesture },
|
||||||
{ "unbindswitch", cmd_unbindswitch },
|
{ "unbindswitch", cmd_unbindswitch },
|
||||||
{ "unbindsym", cmd_unbindsym },
|
{ "unbindsym", cmd_unbindsym },
|
||||||
|
{ "unbindworkspacegesture", cmd_unbindworkspacegesture },
|
||||||
{ "workspace", cmd_workspace },
|
{ "workspace", cmd_workspace },
|
||||||
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
|
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
|
||||||
|
{ "workspace_gesture_spring_size", cmd_ws_gesture_spring_size },
|
||||||
|
{ "workspace_gesture_threshold", cmd_ws_gesture_threshold },
|
||||||
|
{ "workspace_gesture_wrap_around", cmd_ws_gesture_wrap_around },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Config-time only commands. Keep alphabetized */
|
/* Config-time only commands. Keep alphabetized */
|
||||||
|
|
|
@ -164,3 +164,76 @@ struct cmd_results *cmd_bindgesture(int argc, char **argv) {
|
||||||
struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
|
struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
|
||||||
return cmd_bind_or_unbind_gesture(argc, argv, true);
|
return cmd_bind_or_unbind_gesture(argc, argv, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse and execute bindgesture or unbindgesture command.
|
||||||
|
*/
|
||||||
|
static struct cmd_results *cmd_bind_or_unbind_workspacegesture(int argc,
|
||||||
|
char **argv, bool unbind) {
|
||||||
|
int minargs = 1;
|
||||||
|
char *bindtype = "bindgesture";
|
||||||
|
if (unbind) {
|
||||||
|
bindtype = "unbindgesture";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cmd_results *error = NULL;
|
||||||
|
if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
|
||||||
|
if (!binding) {
|
||||||
|
return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
|
||||||
|
}
|
||||||
|
binding->input = strdup("*");
|
||||||
|
|
||||||
|
bool warn = true;
|
||||||
|
|
||||||
|
// Handle flags
|
||||||
|
binding->flags |= BINDING_EXACT;
|
||||||
|
while (argc > 0) {
|
||||||
|
if (strcmp("--inverted", argv[0]) == 0) {
|
||||||
|
binding->flags |= BINDING_INVERTED;
|
||||||
|
} else if (strcmp("--no-warn", argv[0]) == 0) {
|
||||||
|
warn = false;
|
||||||
|
} else if (strncmp("--input-device=", argv[0],
|
||||||
|
strlen("--input-device=")) == 0) {
|
||||||
|
free(binding->input);
|
||||||
|
binding->input = strdup(argv[0] + strlen("--input-device="));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
argv++;
|
||||||
|
argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc < minargs) {
|
||||||
|
free(binding);
|
||||||
|
return cmd_results_new(CMD_FAILURE,
|
||||||
|
"Invalid %s command (expected at least %d "
|
||||||
|
"non-option arguments, got %d)", bindtype, minargs, argc);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* errmsg = NULL;
|
||||||
|
if ((errmsg = workspace_gesture_parse(argv[0], &binding->gesture))) {
|
||||||
|
free(binding);
|
||||||
|
struct cmd_results *final = cmd_results_new(CMD_FAILURE,
|
||||||
|
"Invalid %s command (%s)",
|
||||||
|
bindtype, errmsg);
|
||||||
|
free(errmsg);
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unbind) {
|
||||||
|
return gesture_binding_remove(binding, argv[0]);
|
||||||
|
}
|
||||||
|
binding->command = NULL;
|
||||||
|
return gesture_binding_add(binding, argv[0], warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cmd_results *cmd_bindworkspacegesture(int argc, char **argv) {
|
||||||
|
return cmd_bind_or_unbind_workspacegesture(argc, argv, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cmd_results *cmd_unbindworkspacegesture(int argc, char **argv) {
|
||||||
|
return cmd_bind_or_unbind_workspacegesture(argc, argv, true);
|
||||||
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
#include "sway/commands.h"
|
#include "sway/commands.h"
|
||||||
#include "sway/config.h"
|
#include "sway/config.h"
|
||||||
#include "sway/input/seat.h"
|
#include "sway/input/seat.h"
|
||||||
|
#include "sway/output.h"
|
||||||
#include "sway/tree/workspace.h"
|
#include "sway/tree/workspace.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "stringop.h"
|
#include "stringop.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
|
static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
|
||||||
struct workspace_config *wsc = workspace_find_config(ws_name);
|
struct workspace_config *wsc = workspace_find_config(ws_name);
|
||||||
|
|
49
sway/commands/workspace_gesture.c
Normal file
49
sway/commands/workspace_gesture.c
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include "sway/commands.h"
|
||||||
|
#include "sway/config.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct cmd_results *cmd_ws_gesture_spring_size(int argc, char **argv) {
|
||||||
|
struct cmd_results *error = NULL;
|
||||||
|
if ((error = checkarg(argc, "workspace_gesture_spring_size", EXPECTED_EQUAL_TO, 1))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *inv;
|
||||||
|
int value = strtol(argv[0], &inv, 10);
|
||||||
|
if (*inv != '\0' || value < 0 || value > 250) {
|
||||||
|
return cmd_results_new(CMD_FAILURE, "Invalid size specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
config->workspace_gesture_spring_size = value;
|
||||||
|
|
||||||
|
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cmd_results *cmd_ws_gesture_wrap_around(int argc, char **argv) {
|
||||||
|
struct cmd_results *error = NULL;
|
||||||
|
if ((error = checkarg(argc, "workspace_gesture_wrap_around", EXPECTED_EQUAL_TO, 1))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
config->workspace_gesture_wrap_around = parse_boolean(argv[0], true);
|
||||||
|
|
||||||
|
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cmd_results *cmd_ws_gesture_threshold(int argc, char **argv) {
|
||||||
|
struct cmd_results *error = NULL;
|
||||||
|
if ((error = checkarg(argc, "workspace_gesture_threshold", EXPECTED_EQUAL_TO, 1))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *err;
|
||||||
|
float val = strtof(argv[0], &err);
|
||||||
|
if (*err || val < 0.1f || val > 0.9f) {
|
||||||
|
return cmd_results_new(CMD_INVALID, "workspace_gesture_threshold float invalid. "
|
||||||
|
"Should be between 0.1 and 0.9");
|
||||||
|
}
|
||||||
|
config->workspace_gesture_threshold = val;
|
||||||
|
|
||||||
|
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||||
|
}
|
|
@ -369,6 +369,10 @@ static void config_defaults(struct sway_config *config) {
|
||||||
config->titlebar_separator = true;
|
config->titlebar_separator = true;
|
||||||
config->scratchpad_minimize = false;
|
config->scratchpad_minimize = false;
|
||||||
|
|
||||||
|
config->workspace_gesture_spring_size = 50;
|
||||||
|
config->workspace_gesture_wrap_around = false;
|
||||||
|
config->workspace_gesture_threshold = 0.5;
|
||||||
|
|
||||||
if (!(config->layer_criteria = create_list())) goto cleanup;
|
if (!(config->layer_criteria = create_list())) goto cleanup;
|
||||||
|
|
||||||
// The keysym to keycode translation
|
// The keysym to keycode translation
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "scenefx/render/pass.h"
|
#include "scenefx/render/pass.h"
|
||||||
#include "sway/config.h"
|
#include "sway/config.h"
|
||||||
|
#include "sway/fx_util/animation_utils.h"
|
||||||
#include "sway/desktop/transaction.h"
|
#include "sway/desktop/transaction.h"
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
#include "sway/input/seat.h"
|
#include "sway/input/seat.h"
|
||||||
|
@ -36,6 +37,10 @@
|
||||||
#include "sway/tree/root.h"
|
#include "sway/tree/root.h"
|
||||||
#include "sway/tree/view.h"
|
#include "sway/tree/view.h"
|
||||||
#include "sway/tree/workspace.h"
|
#include "sway/tree/workspace.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define PREV_WS_LIMIT -1.0f
|
||||||
|
#define NEXT_WS_LIMIT 1.0f
|
||||||
|
|
||||||
#if WLR_HAS_DRM_BACKEND
|
#if WLR_HAS_DRM_BACKEND
|
||||||
#include <wlr/backend/drm.h>
|
#include <wlr/backend/drm.h>
|
||||||
|
@ -615,7 +620,8 @@ static int output_repaint_timer_handler(void *data) {
|
||||||
|
|
||||||
if (fullscreen_con && fullscreen_con->view && !debug.noscanout
|
if (fullscreen_con && fullscreen_con->view && !debug.noscanout
|
||||||
// Only output to monitor without compositing when saturation is changed
|
// Only output to monitor without compositing when saturation is changed
|
||||||
&& fullscreen_con->saturation == 1.0f) {
|
&& fullscreen_con->saturation == 1.0f &&
|
||||||
|
output->workspace_scroll.percent == 0.0f) {
|
||||||
// Try to scan-out the fullscreen view
|
// Try to scan-out the fullscreen view
|
||||||
static bool last_scanned_out = false;
|
static bool last_scanned_out = false;
|
||||||
bool scanned_out =
|
bool scanned_out =
|
||||||
|
@ -1114,6 +1120,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
|
||||||
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
||||||
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
|
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
|
||||||
update_output_manager_config(server);
|
update_output_manager_config(server);
|
||||||
|
|
||||||
|
output->workspace_scroll = workspace_scroll_get_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_output_layout_change(struct wl_listener *listener,
|
void handle_output_layout_change(struct wl_listener *listener,
|
||||||
|
@ -1241,3 +1249,166 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
|
||||||
oc = store_output_config(oc);
|
oc = store_output_config(oc);
|
||||||
apply_output_config(oc, output);
|
apply_output_config(oc, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct workspace_scroll workspace_scroll_get_default() {
|
||||||
|
return (struct workspace_scroll) {
|
||||||
|
.percent = 0,
|
||||||
|
.avg_velocity = 0,
|
||||||
|
.num_updates = 0,
|
||||||
|
.direction = SWIPE_GESTURE_DIRECTION_NONE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool workspace_scroll_equal(struct workspace_scroll *a, struct workspace_scroll *b) {
|
||||||
|
return a->avg_velocity == b->avg_velocity &&
|
||||||
|
a->direction == b->direction &&
|
||||||
|
a->num_updates == b->num_updates &&
|
||||||
|
a->percent == b->percent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_scroll_begin(struct sway_seat *seat,
|
||||||
|
enum swipe_gesture_direction direction) {
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
struct sway_output *output = focused_ws->output;
|
||||||
|
|
||||||
|
// Reset the state
|
||||||
|
output->workspace_scroll = workspace_scroll_get_default();
|
||||||
|
output->workspace_scroll.direction = direction;
|
||||||
|
|
||||||
|
output_damage_whole(output);
|
||||||
|
transaction_commit_dirty();
|
||||||
|
|
||||||
|
// Unset focus
|
||||||
|
seat_set_focus_workspace(seat, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_scroll_update(struct sway_seat *seat, struct gesture_tracker *tracker,
|
||||||
|
struct wlr_pointer_swipe_update_event *event, int invert) {
|
||||||
|
double delta_sum;
|
||||||
|
enum swipe_gesture_direction direction;
|
||||||
|
switch (tracker->type) {
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
|
||||||
|
direction = SWIPE_GESTURE_DIRECTION_HORIZONTAL;
|
||||||
|
delta_sum = tracker->dx + event->dx * invert;
|
||||||
|
break;
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
|
||||||
|
direction = SWIPE_GESTURE_DIRECTION_VERTICAL;
|
||||||
|
delta_sum = tracker->dy + event->dy * invert;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
struct sway_output *output = focused_ws->output;
|
||||||
|
struct workspace_scroll *ws_scroll = &output->workspace_scroll;
|
||||||
|
|
||||||
|
if (direction != ws_scroll->direction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int visible_index = list_find(output->workspaces, focused_ws);
|
||||||
|
if (visible_index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the updated average velocity
|
||||||
|
ws_scroll->avg_velocity = fabs(delta_sum) / (++ws_scroll->num_updates);
|
||||||
|
// TODO: Make configurable
|
||||||
|
const int SPEED_FACTOR = 750;
|
||||||
|
double percent = delta_sum / SPEED_FACTOR;
|
||||||
|
|
||||||
|
double min = PREV_WS_LIMIT, max = NEXT_WS_LIMIT;
|
||||||
|
if (!config->workspace_gesture_wrap_around) {
|
||||||
|
// Visualized to the user that this is the last / first workspace by
|
||||||
|
// allowing a small eased swipe, a "Spring effect"
|
||||||
|
double spring_limit = (double) config->workspace_gesture_spring_size /
|
||||||
|
output->width * output->wlr_output->scale;
|
||||||
|
// Make sure that the limit is always smaller than the threshold to
|
||||||
|
// avoid accidental workspace switches
|
||||||
|
double small_threshold = MAX(config->workspace_gesture_threshold - 0.1, 0);
|
||||||
|
spring_limit = MIN(small_threshold, spring_limit);
|
||||||
|
// Limit the percent depending on if the workspace is the first/last or in
|
||||||
|
// the middle somewhere. Uses ease_out to make the limit feel more natural.
|
||||||
|
if (visible_index + 1 >= output->workspaces->length) {
|
||||||
|
max = spring_limit;
|
||||||
|
if (percent > 0) {
|
||||||
|
percent = lerp(0, max, ease_out_cubic(fabs(percent)));
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (visible_index == 0) {
|
||||||
|
min = -spring_limit;
|
||||||
|
if (percent < 0) {
|
||||||
|
percent = lerp(0, min, ease_out_cubic(fabs(percent)));
|
||||||
|
max = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the tracker data if we aren't exceeding the max swipe limit
|
||||||
|
if (percent < max && percent > min) {
|
||||||
|
tracker->dx += event->dx * invert;
|
||||||
|
tracker->dy += event->dy * invert;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_scroll->percent = CLAMP(percent, min, max);
|
||||||
|
ws_scroll->direction = direction;
|
||||||
|
|
||||||
|
output_damage_whole(output);
|
||||||
|
transaction_commit_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_scroll_end(struct sway_seat *seat) {
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
struct sway_output *output = focused_ws->output;
|
||||||
|
struct workspace_scroll *ws_scroll = &output->workspace_scroll;
|
||||||
|
|
||||||
|
int visible_index = list_find(output->workspaces, focused_ws);
|
||||||
|
|
||||||
|
bool not_edge_ws = config->workspace_gesture_wrap_around;
|
||||||
|
int dir;
|
||||||
|
if (ws_scroll->percent < 0) {
|
||||||
|
dir = PREV_WS_LIMIT;
|
||||||
|
not_edge_ws |= visible_index > 0;
|
||||||
|
} else if (ws_scroll->percent > 0) {
|
||||||
|
dir = NEXT_WS_LIMIT;
|
||||||
|
not_edge_ws |= visible_index + 1 < output->workspaces->length;
|
||||||
|
} else {
|
||||||
|
// Skip setting workspace if the percentage is zero
|
||||||
|
goto reset_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make configurable
|
||||||
|
const int VELOCITY_NEEDED = 8;
|
||||||
|
// Only switch workspaces when the percent exceeds the threshold or if
|
||||||
|
// the avg_speed exceeds the limit (for fast but short swipes).
|
||||||
|
bool threshold_met = fabs(ws_scroll->percent) >= config->workspace_gesture_threshold;
|
||||||
|
bool enough_velocity = ws_scroll->avg_velocity >= VELOCITY_NEEDED && not_edge_ws;
|
||||||
|
if (!threshold_met && !enough_velocity) {
|
||||||
|
goto reset_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ws_index = wrap(visible_index + dir, output->workspaces->length);
|
||||||
|
focused_ws = output->workspaces->items[ws_index];
|
||||||
|
sway_log(SWAY_DEBUG, "Switched to workspace: %s\n", focused_ws->name);
|
||||||
|
|
||||||
|
reset_state:
|
||||||
|
workspace_scroll_reset(seat, focused_ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_scroll_reset(struct sway_seat *seat, struct sway_workspace *ws) {
|
||||||
|
if (!ws) {
|
||||||
|
ws = seat_get_focused_workspace(seat);
|
||||||
|
}
|
||||||
|
struct sway_output *output = ws->output;
|
||||||
|
|
||||||
|
workspace_switch(ws);
|
||||||
|
seat_consider_warp_to_focus(seat);
|
||||||
|
|
||||||
|
// Reset the state
|
||||||
|
output->workspace_scroll = workspace_scroll_get_default();
|
||||||
|
|
||||||
|
output_damage_whole(output);
|
||||||
|
transaction_commit_dirty();
|
||||||
|
}
|
||||||
|
|
|
@ -92,6 +92,83 @@ struct decoration_data get_undecorated_decoration_data() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust the box position when switching the workspace
|
||||||
|
static void adjust_box_to_workspace_offset(struct wlr_box *box,
|
||||||
|
bool on_focused_workspace, struct sway_output *output) {
|
||||||
|
float scroll_percent = output->workspace_scroll.percent;
|
||||||
|
|
||||||
|
int ws_dimen;
|
||||||
|
int *box_coord = NULL;
|
||||||
|
switch (output->workspace_scroll.direction) {
|
||||||
|
case SWIPE_GESTURE_DIRECTION_NONE:
|
||||||
|
return;
|
||||||
|
case SWIPE_GESTURE_DIRECTION_HORIZONTAL:
|
||||||
|
ws_dimen = output->width;
|
||||||
|
box_coord = &box->x;
|
||||||
|
break;
|
||||||
|
case SWIPE_GESTURE_DIRECTION_VERTICAL:
|
||||||
|
ws_dimen = output->height;
|
||||||
|
box_coord = &box->y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (box_coord == NULL || ws_dimen == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*box_coord -= ws_dimen * scroll_percent;
|
||||||
|
if (!on_focused_workspace) {
|
||||||
|
if (scroll_percent > 0) {
|
||||||
|
*box_coord += ws_dimen;
|
||||||
|
} else if (scroll_percent < 0) {
|
||||||
|
*box_coord -= ws_dimen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clips the rendered damage to the workspace region.
|
||||||
|
// Fixes containers being rendered across workspaces while switching.
|
||||||
|
static void adjust_damage_to_workspace_bounds(pixman_region32_t *damage,
|
||||||
|
bool on_focused_workspace, struct sway_output *output) {
|
||||||
|
float scale = output->wlr_output->scale;
|
||||||
|
float scroll_percent = output->workspace_scroll.percent;
|
||||||
|
int x = 0, y = 0;
|
||||||
|
|
||||||
|
int ws_dimen = 0;
|
||||||
|
int *coord = NULL;
|
||||||
|
switch (output->workspace_scroll.direction) {
|
||||||
|
case SWIPE_GESTURE_DIRECTION_NONE:
|
||||||
|
return;
|
||||||
|
case SWIPE_GESTURE_DIRECTION_HORIZONTAL:
|
||||||
|
ws_dimen = output->width;
|
||||||
|
coord = &x;
|
||||||
|
break;
|
||||||
|
case SWIPE_GESTURE_DIRECTION_VERTICAL:
|
||||||
|
ws_dimen = output->height;
|
||||||
|
coord = &y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coord == NULL || ws_dimen == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*coord = round(-ws_dimen * scroll_percent);
|
||||||
|
if (!on_focused_workspace) {
|
||||||
|
if (scroll_percent > 0) {
|
||||||
|
*coord += ws_dimen;
|
||||||
|
} else if (scroll_percent < 0) {
|
||||||
|
*coord -= ws_dimen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
||||||
|
pixman_region32_intersect_rect(damage, damage,
|
||||||
|
0, 0, width, height);
|
||||||
|
pixman_region32_translate(damage, x * scale, y * scale);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply scale to a width or height.
|
* Apply scale to a width or height.
|
||||||
*
|
*
|
||||||
|
@ -208,7 +285,6 @@ void render_blur(struct fx_render_context *ctx, struct wlr_texture *texture,
|
||||||
.src_box = *src_box,
|
.src_box = *src_box,
|
||||||
.dst_box = proj_box,
|
.dst_box = proj_box,
|
||||||
.transform = WL_OUTPUT_TRANSFORM_NORMAL,
|
.transform = WL_OUTPUT_TRANSFORM_NORMAL,
|
||||||
.alpha = &deco_data.alpha,
|
|
||||||
.clip = &damage,
|
.clip = &damage,
|
||||||
.filter_mode = WLR_SCALE_FILTER_BILINEAR,
|
.filter_mode = WLR_SCALE_FILTER_BILINEAR,
|
||||||
},
|
},
|
||||||
|
@ -351,6 +427,12 @@ static void render_surface_iterator(struct sway_output *output,
|
||||||
clip_box.x = fmax(dst_box.x, data->clip_box->x);
|
clip_box.x = fmax(dst_box.x, data->clip_box->x);
|
||||||
clip_box.y = fmax(dst_box.y, data->clip_box->y);
|
clip_box.y = fmax(dst_box.y, data->clip_box->y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->view) {
|
||||||
|
adjust_box_to_workspace_offset(&dst_box, data->on_focused_workspace, output);
|
||||||
|
adjust_box_to_workspace_offset(&clip_box, data->on_focused_workspace, output);
|
||||||
|
}
|
||||||
|
|
||||||
scale_box(&dst_box, wlr_output->scale);
|
scale_box(&dst_box, wlr_output->scale);
|
||||||
scale_box(&clip_box, wlr_output->scale);
|
scale_box(&clip_box, wlr_output->scale);
|
||||||
|
|
||||||
|
@ -421,6 +503,7 @@ static void render_layer_iterator(struct sway_output *output,
|
||||||
static void render_layer_toplevel(struct fx_render_context *ctx, struct wl_list *layer_surfaces) {
|
static void render_layer_toplevel(struct fx_render_context *ctx, struct wl_list *layer_surfaces) {
|
||||||
struct render_data data = {
|
struct render_data data = {
|
||||||
.deco_data = get_undecorated_decoration_data(),
|
.deco_data = get_undecorated_decoration_data(),
|
||||||
|
.on_focused_workspace = true,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
};
|
};
|
||||||
output_layer_for_each_toplevel_surface(ctx->output, layer_surfaces,
|
output_layer_for_each_toplevel_surface(ctx->output, layer_surfaces,
|
||||||
|
@ -430,6 +513,7 @@ static void render_layer_toplevel(struct fx_render_context *ctx, struct wl_list
|
||||||
static void render_layer_popups(struct fx_render_context *ctx, struct wl_list *layer_surfaces) {
|
static void render_layer_popups(struct fx_render_context *ctx, struct wl_list *layer_surfaces) {
|
||||||
struct render_data data = {
|
struct render_data data = {
|
||||||
.deco_data = get_undecorated_decoration_data(),
|
.deco_data = get_undecorated_decoration_data(),
|
||||||
|
.on_focused_workspace = true,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
};
|
};
|
||||||
output_layer_for_each_popup_surface(ctx->output, layer_surfaces,
|
output_layer_for_each_popup_surface(ctx->output, layer_surfaces,
|
||||||
|
@ -440,6 +524,7 @@ static void render_layer_toplevel(struct fx_render_context *ctx, struct wl_list
|
||||||
static void render_unmanaged(struct fx_render_context *ctx, struct wl_list *unmanaged) {
|
static void render_unmanaged(struct fx_render_context *ctx, struct wl_list *unmanaged) {
|
||||||
struct render_data data = {
|
struct render_data data = {
|
||||||
.deco_data = get_undecorated_decoration_data(),
|
.deco_data = get_undecorated_decoration_data(),
|
||||||
|
.on_focused_workspace = true,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
};
|
};
|
||||||
output_unmanaged_for_each_surface(ctx->output, unmanaged,
|
output_unmanaged_for_each_surface(ctx->output, unmanaged,
|
||||||
|
@ -450,6 +535,7 @@ static void render_unmanaged(struct fx_render_context *ctx, struct wl_list *unma
|
||||||
static void render_drag_icons(struct fx_render_context *ctx, struct wl_list *drag_icons) {
|
static void render_drag_icons(struct fx_render_context *ctx, struct wl_list *drag_icons) {
|
||||||
struct render_data data = {
|
struct render_data data = {
|
||||||
.deco_data = get_undecorated_decoration_data(),
|
.deco_data = get_undecorated_decoration_data(),
|
||||||
|
.on_focused_workspace = true,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
};
|
};
|
||||||
output_drag_icons_for_each_surface(ctx->output, drag_icons,
|
output_drag_icons_for_each_surface(ctx->output, drag_icons,
|
||||||
|
@ -548,9 +634,10 @@ void premultiply_alpha(float color[4], float opacity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_view_toplevels(struct fx_render_context *ctx,
|
static void render_view_toplevels(struct fx_render_context *ctx,
|
||||||
struct sway_view *view, struct decoration_data deco_data) {
|
struct sway_view *view, struct decoration_data deco_data, bool on_focused_workspace) {
|
||||||
struct render_data data = {
|
struct render_data data = {
|
||||||
.deco_data = deco_data,
|
.deco_data = deco_data,
|
||||||
|
.on_focused_workspace = on_focused_workspace,
|
||||||
.view = view,
|
.view = view,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
};
|
};
|
||||||
|
@ -598,6 +685,7 @@ static void render_view_popups(struct fx_render_context *ctx, struct sway_view *
|
||||||
struct decoration_data deco_data) {
|
struct decoration_data deco_data) {
|
||||||
struct render_data data = {
|
struct render_data data = {
|
||||||
.deco_data = deco_data,
|
.deco_data = deco_data,
|
||||||
|
.on_focused_workspace = true,
|
||||||
.view = view,
|
.view = view,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
};
|
};
|
||||||
|
@ -700,13 +788,14 @@ static void render_saved_view(struct fx_render_context *ctx, struct sway_view *v
|
||||||
* Render a view's surface, shadow, and left/bottom/right borders.
|
* Render a view's surface, shadow, and left/bottom/right borders.
|
||||||
*/
|
*/
|
||||||
static void render_view(struct fx_render_context *ctx, struct sway_container *con,
|
static void render_view(struct fx_render_context *ctx, struct sway_container *con,
|
||||||
struct border_colors *colors, struct decoration_data deco_data) {
|
struct border_colors *colors, struct decoration_data deco_data,
|
||||||
|
bool on_focused_workspace) {
|
||||||
struct sway_view *view = con->view;
|
struct sway_view *view = con->view;
|
||||||
|
|
||||||
if (!wl_list_empty(&view->saved_buffers)) {
|
if (!wl_list_empty(&view->saved_buffers)) {
|
||||||
render_saved_view(ctx, view, deco_data);
|
render_saved_view(ctx, view, deco_data);
|
||||||
} else if (view->surface) {
|
} else if (view->surface) {
|
||||||
render_view_toplevels(ctx, view, deco_data);
|
render_view_toplevels(ctx, view, deco_data, on_focused_workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sway_container_state *state = &con->current;
|
struct sway_container_state *state = &con->current;
|
||||||
|
@ -725,6 +814,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
||||||
box.y = floor(state->y) - ctx->output->ly;
|
box.y = floor(state->y) - ctx->output->ly;
|
||||||
box.width = state->width;
|
box.width = state->width;
|
||||||
box.height = state->height;
|
box.height = state->height;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
int shadow_corner_radius = corner_radius == 0 ? 0 : corner_radius + state->border_thickness;
|
int shadow_corner_radius = corner_radius == 0 ? 0 : corner_radius + state->border_thickness;
|
||||||
float* shadow_color = view_is_urgent(view) || state->focused ?
|
float* shadow_color = view_is_urgent(view) || state->focused ?
|
||||||
|
@ -748,6 +838,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
||||||
box.y = floor(state->content_y);
|
box.y = floor(state->content_y);
|
||||||
box.width = state->border_thickness;
|
box.width = state->border_thickness;
|
||||||
box.height = state->content_height - corner_radius;
|
box.height = state->content_height - corner_radius;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
if (corner_radius && !deco_data.has_titlebar) {
|
if (corner_radius && !deco_data.has_titlebar) {
|
||||||
box.y += corner_radius;
|
box.y += corner_radius;
|
||||||
box.height -= corner_radius;
|
box.height -= corner_radius;
|
||||||
|
@ -771,6 +862,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
||||||
box.y = floor(state->content_y);
|
box.y = floor(state->content_y);
|
||||||
box.width = state->border_thickness;
|
box.width = state->border_thickness;
|
||||||
box.height = state->content_height - corner_radius;
|
box.height = state->content_height - corner_radius;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
if (corner_radius && !deco_data.has_titlebar) {
|
if (corner_radius && !deco_data.has_titlebar) {
|
||||||
box.y += corner_radius;
|
box.y += corner_radius;
|
||||||
box.height -= corner_radius;
|
box.height -= corner_radius;
|
||||||
|
@ -790,6 +882,8 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
||||||
box.y = floor(state->content_y + state->content_height);
|
box.y = floor(state->content_y + state->content_height);
|
||||||
box.width = state->width;
|
box.width = state->width;
|
||||||
box.height = state->border_thickness;
|
box.height = state->border_thickness;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
|
|
||||||
// adjust sizing for rounded border corners
|
// adjust sizing for rounded border corners
|
||||||
if (deco_data.corner_radius) {
|
if (deco_data.corner_radius) {
|
||||||
box.x += corner_radius + state->border_thickness;
|
box.x += corner_radius + state->border_thickness;
|
||||||
|
@ -805,6 +899,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
||||||
if (state->border_left) {
|
if (state->border_left) {
|
||||||
box.x = floor(state->x);
|
box.x = floor(state->x);
|
||||||
box.y = floor(state->y + state->height - size);
|
box.y = floor(state->y + state->height - size);
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
box.width = size;
|
box.width = size;
|
||||||
box.height = size;
|
box.height = size;
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
|
@ -814,8 +909,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
||||||
if (state->border_right) {
|
if (state->border_right) {
|
||||||
box.x = floor(state->x + state->width - size);
|
box.x = floor(state->x + state->width - size);
|
||||||
box.y = floor(state->y + state->height - size);
|
box.y = floor(state->y + state->height - size);
|
||||||
box.width = size;
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
box.height = size;
|
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rounded_border_corner(ctx, &box, color, scaled_corner_radius,
|
render_rounded_border_corner(ctx, &box, color, scaled_corner_radius,
|
||||||
scaled_border_thickness, BOTTOM_RIGHT);
|
scaled_border_thickness, BOTTOM_RIGHT);
|
||||||
|
@ -836,7 +930,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
||||||
static void render_titlebar(struct fx_render_context *ctx, struct sway_container *con,
|
static void render_titlebar(struct fx_render_context *ctx, struct sway_container *con,
|
||||||
int x, int y, int width, struct border_colors *colors, int corner_radius,
|
int x, int y, int width, struct border_colors *colors, int corner_radius,
|
||||||
enum corner_location corner_location, struct wlr_texture *title_texture,
|
enum corner_location corner_location, struct wlr_texture *title_texture,
|
||||||
struct wlr_texture *marks_texture) {
|
struct wlr_texture *marks_texture, bool on_focused_workspace) {
|
||||||
struct wlr_box box;
|
struct wlr_box box;
|
||||||
float color[4];
|
float color[4];
|
||||||
struct sway_output *output = ctx->output;
|
struct sway_output *output = ctx->output;
|
||||||
|
@ -868,6 +962,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.width -= corner_radius;
|
box.width -= corner_radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
|
|
||||||
|
@ -877,6 +972,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.y = y + container_titlebar_height() - titlebar_border_thickness;
|
box.y = y + container_titlebar_height() - titlebar_border_thickness;
|
||||||
box.width = width;
|
box.width = width;
|
||||||
box.height = titlebar_border_thickness;
|
box.height = titlebar_border_thickness;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
}
|
}
|
||||||
|
@ -890,6 +986,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.height -= corner_radius;
|
box.height -= corner_radius;
|
||||||
box.y += corner_radius;
|
box.y += corner_radius;
|
||||||
}
|
}
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
|
|
||||||
|
@ -902,6 +999,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.height -= corner_radius;
|
box.height -= corner_radius;
|
||||||
box.y += corner_radius;
|
box.y += corner_radius;
|
||||||
}
|
}
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
|
|
||||||
|
@ -913,6 +1011,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.y = y;
|
box.y = y;
|
||||||
box.width = corner_radius * 2;
|
box.width = corner_radius * 2;
|
||||||
box.height = corner_radius * 2;
|
box.height = corner_radius * 2;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rounded_border_corner(ctx, &box, color, corner_radius,
|
render_rounded_border_corner(ctx, &box, color, corner_radius,
|
||||||
titlebar_border_thickness * output_scale, TOP_LEFT);
|
titlebar_border_thickness * output_scale, TOP_LEFT);
|
||||||
|
@ -924,6 +1023,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.y = y;
|
box.y = y;
|
||||||
box.width = corner_radius * 2;
|
box.width = corner_radius * 2;
|
||||||
box.height = corner_radius * 2;
|
box.height = corner_radius * 2;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rounded_border_corner(ctx, &box, color, corner_radius,
|
render_rounded_border_corner(ctx, &box, color, corner_radius,
|
||||||
titlebar_border_thickness * output_scale, TOP_RIGHT);
|
titlebar_border_thickness * output_scale, TOP_RIGHT);
|
||||||
|
@ -977,6 +1077,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
if (ob_inner_width < clip_box.width) {
|
if (ob_inner_width < clip_box.width) {
|
||||||
clip_box.width = ob_inner_width;
|
clip_box.width = ob_inner_width;
|
||||||
}
|
}
|
||||||
|
adjust_box_to_workspace_offset(&texture_box, on_focused_workspace, output);
|
||||||
render_texture(ctx, marks_texture,
|
render_texture(ctx, marks_texture,
|
||||||
NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data);
|
NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data);
|
||||||
|
|
||||||
|
@ -987,11 +1088,13 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.y = roundf((y + titlebar_border_thickness) * output_scale);
|
box.y = roundf((y + titlebar_border_thickness) * output_scale);
|
||||||
box.width = clip_box.width;
|
box.width = clip_box.width;
|
||||||
box.height = ob_padding_above;
|
box.height = ob_padding_above;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
|
|
||||||
// Padding below
|
// Padding below
|
||||||
box.y += ob_padding_above + clip_box.height;
|
box.y += ob_padding_above + clip_box.height;
|
||||||
box.height = ob_padding_below + bottom_border_compensation;
|
box.height = ob_padding_below + bottom_border_compensation;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1049,6 +1152,8 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
clip_box.width = ob_inner_width - ob_marks_width;
|
clip_box.width = ob_inner_width - ob_marks_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adjust_box_to_workspace_offset(&texture_box, on_focused_workspace, output);
|
||||||
|
adjust_box_to_workspace_offset(&clip_box, on_focused_workspace, output);
|
||||||
render_texture(ctx, title_texture,
|
render_texture(ctx, title_texture,
|
||||||
NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data);
|
NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data);
|
||||||
|
|
||||||
|
@ -1059,6 +1164,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.y = roundf((y + titlebar_border_thickness) * output_scale);
|
box.y = roundf((y + titlebar_border_thickness) * output_scale);
|
||||||
box.width = clip_box.width;
|
box.width = clip_box.width;
|
||||||
box.height = ob_padding_above;
|
box.height = ob_padding_above;
|
||||||
|
// adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
|
|
||||||
// Padding below
|
// Padding below
|
||||||
|
@ -1098,6 +1204,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
|
box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
|
||||||
box.y = roundf(bg_y * output_scale);
|
box.y = roundf(bg_y * output_scale);
|
||||||
box.height = ob_bg_height + bottom_border_compensation;
|
box.height = ob_bg_height + bottom_border_compensation;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1112,6 +1219,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
if (box.x + box.width < left_x) {
|
if (box.x + box.width < left_x) {
|
||||||
box.width += left_x - box.x - box.width;
|
box.width += left_x - box.x - box.width;
|
||||||
}
|
}
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
if (corner_radius && (corner_location == TOP_LEFT || corner_location == ALL)) {
|
if (corner_radius && (corner_location == TOP_LEFT || corner_location == ALL)) {
|
||||||
render_rounded_rect(ctx, &box, color, corner_radius, TOP_LEFT);
|
render_rounded_rect(ctx, &box, color, corner_radius, TOP_LEFT);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1130,6 +1238,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
box.width += box.x - right_rx;
|
box.width += box.x - right_rx;
|
||||||
box.x = right_rx;
|
box.x = right_rx;
|
||||||
}
|
}
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||||
if (corner_radius && (corner_location == TOP_RIGHT || corner_location == ALL)) {
|
if (corner_radius && (corner_location == TOP_RIGHT || corner_location == ALL)) {
|
||||||
render_rounded_rect(ctx, &box, color, corner_radius, TOP_RIGHT);
|
render_rounded_rect(ctx, &box, color, corner_radius, TOP_RIGHT);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1141,7 +1250,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
||||||
* Render the top border line for a view using "border pixel".
|
* Render the top border line for a view using "border pixel".
|
||||||
*/
|
*/
|
||||||
static void render_top_border(struct fx_render_context *ctx, struct sway_container *con,
|
static void render_top_border(struct fx_render_context *ctx, struct sway_container *con,
|
||||||
struct border_colors *colors, int corner_radius) {
|
struct border_colors *colors, int corner_radius, bool on_focused_workspace) {
|
||||||
struct sway_container_state *state = &con->current;
|
struct sway_container_state *state = &con->current;
|
||||||
if (!state->border_top) {
|
if (!state->border_top) {
|
||||||
return;
|
return;
|
||||||
|
@ -1157,6 +1266,7 @@ static void render_top_border(struct fx_render_context *ctx, struct sway_contain
|
||||||
box.y = floor(state->y);
|
box.y = floor(state->y);
|
||||||
box.width = state->width - (2 * corner_radius);
|
box.width = state->width - (2 * corner_radius);
|
||||||
box.height = state->border_thickness;
|
box.height = state->border_thickness;
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
render_rect(ctx, &box, color);
|
render_rect(ctx, &box, color);
|
||||||
|
|
||||||
|
@ -1167,6 +1277,7 @@ static void render_top_border(struct fx_render_context *ctx, struct sway_contain
|
||||||
if (state->border_left) {
|
if (state->border_left) {
|
||||||
box.x = floor(state->x);
|
box.x = floor(state->x);
|
||||||
box.y = floor(state->y);
|
box.y = floor(state->y);
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
box.width = size;
|
box.width = size;
|
||||||
box.height = size;
|
box.height = size;
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
|
@ -1176,6 +1287,7 @@ static void render_top_border(struct fx_render_context *ctx, struct sway_contain
|
||||||
if (state->border_right) {
|
if (state->border_right) {
|
||||||
box.x = floor(state->x + state->width - size);
|
box.x = floor(state->x + state->width - size);
|
||||||
box.y = floor(state->y);
|
box.y = floor(state->y);
|
||||||
|
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||||
box.width = size;
|
box.width = size;
|
||||||
box.height = size;
|
box.height = size;
|
||||||
scale_box(&box, output_scale);
|
scale_box(&box, output_scale);
|
||||||
|
@ -1191,10 +1303,13 @@ struct parent_data {
|
||||||
list_t *children;
|
list_t *children;
|
||||||
bool focused;
|
bool focused;
|
||||||
struct sway_container *active_child;
|
struct sway_container *active_child;
|
||||||
|
|
||||||
|
// Indicates whether the target is on the focused workspace or not.
|
||||||
|
bool on_focused_workspace;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void render_container(struct fx_render_context *ctx,
|
static void render_container(struct fx_render_context *ctx,
|
||||||
struct sway_container *con, bool parent_focused);
|
struct sway_container *con, bool parent_focused, bool is_current_ws);
|
||||||
|
|
||||||
// TODO: no rounding top corners when rendering with titlebar
|
// TODO: no rounding top corners when rendering with titlebar
|
||||||
/**
|
/**
|
||||||
|
@ -1254,17 +1369,18 @@ static void render_containers_linear(struct fx_render_context *ctx, struct paren
|
||||||
.discard_transparent = false,
|
.discard_transparent = false,
|
||||||
.shadow = child->shadow_enabled,
|
.shadow = child->shadow_enabled,
|
||||||
};
|
};
|
||||||
render_view(ctx, child, colors, deco_data);
|
render_view(ctx, child, colors, deco_data, parent->on_focused_workspace);
|
||||||
if (has_titlebar) {
|
if (has_titlebar) {
|
||||||
render_titlebar(ctx, child, floor(state->x), floor(state->y),
|
render_titlebar(ctx, child, floor(state->x), floor(state->y),
|
||||||
state->width, colors, deco_data.corner_radius,
|
state->width, colors, deco_data.corner_radius,
|
||||||
ALL, title_texture, marks_texture);
|
ALL, title_texture, marks_texture, parent->on_focused_workspace);
|
||||||
} else if (state->border == B_PIXEL) {
|
} else if (state->border == B_PIXEL) {
|
||||||
render_top_border(ctx, child, colors, deco_data.corner_radius);
|
render_top_border(ctx, child, colors, deco_data.corner_radius, parent->on_focused_workspace);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
render_container(ctx, child,
|
render_container(ctx, child,
|
||||||
parent->focused || child->current.focused);
|
parent->focused || child->current.focused,
|
||||||
|
parent->on_focused_workspace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1359,7 +1475,8 @@ static void render_containers_tabbed(struct fx_render_context *ctx, struct paren
|
||||||
}
|
}
|
||||||
|
|
||||||
render_titlebar(ctx, child, x, parent->box.y, tab_width, colors,
|
render_titlebar(ctx, child, x, parent->box.y, tab_width, colors,
|
||||||
corner_radius, corner_location, title_texture, marks_texture);
|
corner_radius, corner_location, title_texture, marks_texture,
|
||||||
|
parent->on_focused_workspace);
|
||||||
|
|
||||||
if (child == current) {
|
if (child == current) {
|
||||||
current_colors = colors;
|
current_colors = colors;
|
||||||
|
@ -1368,10 +1485,12 @@ static void render_containers_tabbed(struct fx_render_context *ctx, struct paren
|
||||||
|
|
||||||
// Render surface and left/right/bottom borders
|
// Render surface and left/right/bottom borders
|
||||||
if (current->view) {
|
if (current->view) {
|
||||||
render_view(ctx, current, current_colors, deco_data);
|
render_view(ctx, current, current_colors, deco_data,
|
||||||
|
parent->on_focused_workspace);
|
||||||
} else {
|
} else {
|
||||||
render_container(ctx, current,
|
render_container(ctx, current,
|
||||||
parent->focused || current->current.focused);
|
parent->focused || current->current.focused,
|
||||||
|
parent->on_focused_workspace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1438,7 +1557,7 @@ static void render_containers_stacked(struct fx_render_context *ctx, struct pare
|
||||||
int y = parent->box.y + titlebar_height * i;
|
int y = parent->box.y + titlebar_height * i;
|
||||||
int corner_radius = i != 0 ? 0 : deco_data.corner_radius;
|
int corner_radius = i != 0 ? 0 : deco_data.corner_radius;
|
||||||
render_titlebar(ctx, child, parent->box.x, y, parent->box.width, colors,
|
render_titlebar(ctx, child, parent->box.x, y, parent->box.width, colors,
|
||||||
corner_radius, ALL, title_texture, marks_texture);
|
corner_radius, ALL, title_texture, marks_texture, parent->on_focused_workspace);
|
||||||
|
|
||||||
if (child == current) {
|
if (child == current) {
|
||||||
current_colors = colors;
|
current_colors = colors;
|
||||||
|
@ -1447,10 +1566,12 @@ static void render_containers_stacked(struct fx_render_context *ctx, struct pare
|
||||||
|
|
||||||
// Render surface and left/right/bottom borders
|
// Render surface and left/right/bottom borders
|
||||||
if (current->view) {
|
if (current->view) {
|
||||||
render_view(ctx, current, current_colors, deco_data);
|
render_view(ctx, current, current_colors, deco_data,
|
||||||
|
parent->on_focused_workspace);
|
||||||
} else {
|
} else {
|
||||||
render_container(ctx, current,
|
render_container(ctx, current,
|
||||||
parent->focused || current->current.focused);
|
parent->focused || current->current.focused,
|
||||||
|
parent->on_focused_workspace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1479,7 +1600,7 @@ static void render_containers(struct fx_render_context *ctx, struct parent_data
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_container(struct fx_render_context *ctx,
|
static void render_container(struct fx_render_context *ctx,
|
||||||
struct sway_container *con, bool focused) {
|
struct sway_container *con, bool focused, bool is_focused_ws) {
|
||||||
struct parent_data data = {
|
struct parent_data data = {
|
||||||
.layout = con->current.layout,
|
.layout = con->current.layout,
|
||||||
.box = {
|
.box = {
|
||||||
|
@ -1491,12 +1612,13 @@ static void render_container(struct fx_render_context *ctx,
|
||||||
.children = con->current.children,
|
.children = con->current.children,
|
||||||
.focused = focused,
|
.focused = focused,
|
||||||
.active_child = con->current.focused_inactive_child,
|
.active_child = con->current.focused_inactive_child,
|
||||||
|
.on_focused_workspace = is_focused_ws,
|
||||||
};
|
};
|
||||||
render_containers(ctx, &data);
|
render_containers(ctx, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_workspace(struct fx_render_context *ctx,
|
static void render_workspace(struct fx_render_context *ctx,
|
||||||
struct sway_workspace *ws, bool focused) {
|
struct sway_workspace *ws, bool focused, bool on_focused_workspace) {
|
||||||
struct parent_data data = {
|
struct parent_data data = {
|
||||||
.layout = ws->current.layout,
|
.layout = ws->current.layout,
|
||||||
.box = {
|
.box = {
|
||||||
|
@ -1508,12 +1630,13 @@ static void render_workspace(struct fx_render_context *ctx,
|
||||||
.children = ws->current.tiling,
|
.children = ws->current.tiling,
|
||||||
.focused = focused,
|
.focused = focused,
|
||||||
.active_child = ws->current.focused_inactive_child,
|
.active_child = ws->current.focused_inactive_child,
|
||||||
|
.on_focused_workspace = on_focused_workspace,
|
||||||
};
|
};
|
||||||
render_containers(ctx, &data);
|
render_containers(ctx, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_floating_container(struct fx_render_context *ctx,
|
static void render_floating_container(struct fx_render_context *ctx,
|
||||||
struct sway_container *con) {
|
struct sway_container *con, bool is_focused_ws) {
|
||||||
struct sway_container_state *state = &con->current;
|
struct sway_container_state *state = &con->current;
|
||||||
if (con->view) {
|
if (con->view) {
|
||||||
struct sway_view *view = con->view;
|
struct sway_view *view = con->view;
|
||||||
|
@ -1549,32 +1672,44 @@ static void render_floating_container(struct fx_render_context *ctx,
|
||||||
.discard_transparent = false,
|
.discard_transparent = false,
|
||||||
.shadow = con->shadow_enabled,
|
.shadow = con->shadow_enabled,
|
||||||
};
|
};
|
||||||
render_view(ctx, con, colors, deco_data);
|
render_view(ctx, con, colors, deco_data, is_focused_ws);
|
||||||
if (has_titlebar) {
|
if (has_titlebar) {
|
||||||
render_titlebar(ctx, con, floor(con->current.x), floor(con->current.y), con->current.width,
|
render_titlebar(ctx, con, floor(con->current.x), floor(con->current.y), con->current.width,
|
||||||
colors, deco_data.corner_radius, ALL, title_texture, marks_texture);
|
colors, deco_data.corner_radius, ALL, title_texture, marks_texture, is_focused_ws);
|
||||||
} else if (state->border == B_PIXEL) {
|
} else if (state->border == B_PIXEL) {
|
||||||
render_top_border(ctx, con, colors, deco_data.corner_radius);
|
render_top_border(ctx, con, colors, deco_data.corner_radius, is_focused_ws);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
render_container(ctx, con, state->focused);
|
render_container(ctx, con, state->focused, is_focused_ws);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_floating(struct fx_render_context *ctx) {
|
static void render_floating(struct fx_render_context *ctx,
|
||||||
|
struct sway_workspace *other_ws, bool has_fullscreen) {
|
||||||
for (int i = 0; i < root->outputs->length; ++i) {
|
for (int i = 0; i < root->outputs->length; ++i) {
|
||||||
struct sway_output *output = root->outputs->items[i];
|
struct sway_output *output = root->outputs->items[i];
|
||||||
for (int j = 0; j < output->current.workspaces->length; ++j) {
|
|
||||||
struct sway_workspace *ws = output->current.workspaces->items[j];
|
// Don't render floating windows across outputs when switching workspaces
|
||||||
if (!workspace_is_visible(ws)) {
|
if (output->workspace_scroll.percent != 0 && output != ctx->output) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sway_workspace *visible_ws = output->current.active_workspace;
|
||||||
|
for (int j = 0; j < output->current.workspaces->length; ++j) {
|
||||||
|
struct sway_workspace *ws = output->current.workspaces->items[j];
|
||||||
|
|
||||||
|
// Only render affected workspaces
|
||||||
|
if ((ws != other_ws && ws != visible_ws) ||
|
||||||
|
(workspace_is_visible(ws) && has_fullscreen)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (int k = 0; k < ws->current.floating->length; ++k) {
|
for (int k = 0; k < ws->current.floating->length; ++k) {
|
||||||
struct sway_container *floater = ws->current.floating->items[k];
|
struct sway_container *floater = ws->current.floating->items[k];
|
||||||
if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
|
if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
render_floating_container(ctx, floater);
|
render_floating_container(ctx, floater, ws == visible_ws);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1587,6 +1722,57 @@ static void render_seatops(struct fx_render_context *ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void render_fullscreen_con(struct fx_render_context *ctx,
|
||||||
|
pixman_region32_t *transformed_damage, struct sway_container *fullscreen_con,
|
||||||
|
struct sway_workspace *workspace, bool clear_whole_screen) {
|
||||||
|
struct wlr_output *wlr_output = ctx->output->wlr_output;
|
||||||
|
|
||||||
|
bool on_focused_workspace = workspace == ctx->output->current.active_workspace;
|
||||||
|
|
||||||
|
// Only clear the transformed fullscreen bounds
|
||||||
|
pixman_region32_t dmg;
|
||||||
|
pixman_region32_init(&dmg);
|
||||||
|
if (!clear_whole_screen) {
|
||||||
|
pixman_region32_copy(&dmg, transformed_damage);
|
||||||
|
adjust_damage_to_workspace_bounds(&dmg, on_focused_workspace, ctx->output);
|
||||||
|
transformed_damage = &dmg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){
|
||||||
|
.base = {
|
||||||
|
.box = { .width = wlr_output->width, .height = wlr_output->height },
|
||||||
|
.color = { .r = 0, .g = 0, .b = 0, .a = 1 },
|
||||||
|
.clip = transformed_damage,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fullscreen_con->view) {
|
||||||
|
struct decoration_data deco_data = get_undecorated_decoration_data();
|
||||||
|
deco_data.saturation = fullscreen_con->saturation;
|
||||||
|
if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
|
||||||
|
render_saved_view(ctx, fullscreen_con->view, deco_data);
|
||||||
|
} else if (fullscreen_con->view->surface) {
|
||||||
|
render_view_toplevels(ctx, fullscreen_con->view, deco_data, on_focused_workspace);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
render_container(ctx, fullscreen_con, fullscreen_con->current.focused, on_focused_workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < workspace->current.floating->length; ++i) {
|
||||||
|
struct sway_container *floater =
|
||||||
|
workspace->current.floating->items[i];
|
||||||
|
if (container_is_transient_for(floater, fullscreen_con)) {
|
||||||
|
render_floating_container(ctx, floater, on_focused_workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if HAVE_XWAYLAND
|
||||||
|
render_unmanaged(ctx, &root->xwayland_unmanaged);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pixman_region32_fini(&dmg);
|
||||||
|
}
|
||||||
|
|
||||||
void output_render(struct fx_render_context *ctx) {
|
void output_render(struct fx_render_context *ctx) {
|
||||||
struct wlr_output *wlr_output = ctx->output->wlr_output;
|
struct wlr_output *wlr_output = ctx->output->wlr_output;
|
||||||
struct sway_output *output = ctx->output;
|
struct sway_output *output = ctx->output;
|
||||||
|
@ -1599,10 +1785,29 @@ void output_render(struct fx_render_context *ctx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the sibling workspaces
|
||||||
|
struct sway_workspace *other_ws = NULL;
|
||||||
|
if (output->workspace_scroll.percent < 0) {
|
||||||
|
other_ws = workspace_output_prev(workspace,
|
||||||
|
config->workspace_gesture_wrap_around);
|
||||||
|
} else if (output->workspace_scroll.percent > 0) {
|
||||||
|
other_ws = workspace_output_next(workspace,
|
||||||
|
config->workspace_gesture_wrap_around);
|
||||||
|
}
|
||||||
|
|
||||||
struct sway_container *fullscreen_con = root->fullscreen_global;
|
struct sway_container *fullscreen_con = root->fullscreen_global;
|
||||||
if (!fullscreen_con) {
|
if (!fullscreen_con) {
|
||||||
fullscreen_con = workspace->current.fullscreen;
|
fullscreen_con = workspace->current.fullscreen;
|
||||||
}
|
}
|
||||||
|
// Also check if the sibling workspace has a fullscreen container
|
||||||
|
bool has_fullscreen = fullscreen_con != NULL;
|
||||||
|
bool other_ws_has_fullscreen = false;
|
||||||
|
if (other_ws) {
|
||||||
|
other_ws_has_fullscreen = other_ws->current.fullscreen;
|
||||||
|
if (!has_fullscreen) {
|
||||||
|
has_fullscreen = other_ws_has_fullscreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!pixman_region32_not_empty(damage)) {
|
if (!pixman_region32_not_empty(damage)) {
|
||||||
// Output isn't damaged but needs buffer swap
|
// Output isn't damaged but needs buffer swap
|
||||||
|
@ -1643,6 +1848,7 @@ void output_render(struct fx_render_context *ctx) {
|
||||||
if (server.session_lock.lock != NULL) {
|
if (server.session_lock.lock != NULL) {
|
||||||
struct render_data data = {
|
struct render_data data = {
|
||||||
.deco_data = get_undecorated_decoration_data(),
|
.deco_data = get_undecorated_decoration_data(),
|
||||||
|
.on_focused_workspace = true,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1666,37 +1872,9 @@ void output_render(struct fx_render_context *ctx) {
|
||||||
goto render_overlay;
|
goto render_overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullscreen_con) {
|
if (fullscreen_con && output->workspace_scroll.percent == 0) {
|
||||||
fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){
|
// Only draw fullscreen con if not transitioning between workspaces
|
||||||
.base = {
|
render_fullscreen_con(ctx, &transformed_damage, fullscreen_con, workspace, true);
|
||||||
.box = { .width = wlr_output->width, .height = wlr_output->height },
|
|
||||||
.color = { .r = 0, .g = 0, .b = 0, .a = 1 },
|
|
||||||
.clip = &transformed_damage,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fullscreen_con->view) {
|
|
||||||
struct decoration_data deco_data = get_undecorated_decoration_data();
|
|
||||||
deco_data.saturation = fullscreen_con->saturation;
|
|
||||||
if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
|
|
||||||
render_saved_view(ctx, fullscreen_con->view, deco_data);
|
|
||||||
} else if (fullscreen_con->view->surface) {
|
|
||||||
render_view_toplevels(ctx, fullscreen_con->view, deco_data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
render_container(ctx, fullscreen_con, fullscreen_con->current.focused);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < workspace->current.floating->length; ++i) {
|
|
||||||
struct sway_container *floater =
|
|
||||||
workspace->current.floating->items[i];
|
|
||||||
if (container_is_transient_for(floater, fullscreen_con)) {
|
|
||||||
render_floating_container(ctx, floater);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if HAVE_XWAYLAND
|
|
||||||
render_unmanaged(ctx, &root->xwayland_unmanaged);
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
int output_width, output_height;
|
int output_width, output_height;
|
||||||
wlr_output_transformed_resolution(wlr_output, &output_width, &output_height);
|
wlr_output_transformed_resolution(wlr_output, &output_width, &output_height);
|
||||||
|
@ -1779,8 +1957,16 @@ void output_render(struct fx_render_context *ctx) {
|
||||||
fx_render_pass_add_optimized_blur(ctx->pass, &blur_options);
|
fx_render_pass_add_optimized_blur(ctx->pass, &blur_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
render_workspace(ctx, workspace, workspace->current.focused);
|
// Render both workspaces
|
||||||
render_floating(ctx);
|
if (!other_ws_has_fullscreen && other_ws) {
|
||||||
|
render_workspace(ctx, other_ws, false, false);
|
||||||
|
}
|
||||||
|
if (!fullscreen_con) {
|
||||||
|
render_workspace(ctx, workspace, workspace->current.focused, true);
|
||||||
|
}
|
||||||
|
render_floating(ctx,
|
||||||
|
!other_ws_has_fullscreen ? other_ws : NULL,
|
||||||
|
fullscreen_con != NULL);
|
||||||
#if HAVE_XWAYLAND
|
#if HAVE_XWAYLAND
|
||||||
render_unmanaged(ctx, &root->xwayland_unmanaged);
|
render_unmanaged(ctx, &root->xwayland_unmanaged);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1793,6 +1979,43 @@ void output_render(struct fx_render_context *ctx) {
|
||||||
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
|
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
|
||||||
render_layer_popups(ctx,
|
render_layer_popups(ctx,
|
||||||
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
|
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
|
||||||
|
|
||||||
|
// Render the fullscreen containers on top
|
||||||
|
if (has_fullscreen) {
|
||||||
|
float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
|
||||||
|
// Render a blank rect next to the fullscreen container if
|
||||||
|
// there's no sibling workspace in the swipes direction
|
||||||
|
if (!other_ws) {
|
||||||
|
struct wlr_box mon_box = { 0, 0, output->width, output->height };
|
||||||
|
adjust_box_to_workspace_offset(&mon_box, false, output);
|
||||||
|
scale_box(&mon_box, output->wlr_output->scale);
|
||||||
|
render_rect(ctx, &mon_box, clear_color);
|
||||||
|
|
||||||
|
// Render a shadow to separate the edge and the fullscreen
|
||||||
|
// container
|
||||||
|
if (config_should_parameters_shadow()) {
|
||||||
|
struct wlr_box shadow_box = { 0, 0, output->width, output->height };
|
||||||
|
adjust_box_to_workspace_offset(&shadow_box, true, output);
|
||||||
|
scale_box(&shadow_box, output->wlr_output->scale);
|
||||||
|
// Render rect to fix minor pixel gaps between fullscreen
|
||||||
|
// container and shadow
|
||||||
|
render_rect(ctx, &shadow_box, clear_color);
|
||||||
|
render_box_shadow(ctx, &shadow_box,
|
||||||
|
config->shadow_color, config->shadow_blur_sigma, 0,
|
||||||
|
0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Render sibling fullscreen container
|
||||||
|
struct sway_container *f_con = other_ws->current.fullscreen;
|
||||||
|
if (other_ws->current.fullscreen) {
|
||||||
|
render_fullscreen_con(ctx, &transformed_damage, f_con, other_ws, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Render focused fullscreen container
|
||||||
|
if (fullscreen_con) {
|
||||||
|
render_fullscreen_con(ctx, &transformed_damage, fullscreen_con, workspace, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_seatops(ctx);
|
render_seatops(ctx);
|
||||||
|
|
12
sway/fx_util/animation_utils.c
Normal file
12
sway/fx_util/animation_utils.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "sway/fx_util/animation_utils.h"
|
||||||
|
|
||||||
|
double lerp (double a, double b, double t) {
|
||||||
|
return a * (1.0 - t) + b * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ease_out_cubic (double t) {
|
||||||
|
double p = t - 1;
|
||||||
|
return pow(p, 3) + 1;
|
||||||
|
}
|
|
@ -1053,6 +1053,14 @@ static void handle_swipe_begin(struct sway_seat *seat,
|
||||||
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
|
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
|
||||||
struct seatop_default_event *seatop = seat->seatop_data;
|
struct seatop_default_event *seatop = seat->seatop_data;
|
||||||
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
|
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
|
||||||
|
} else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL, event->fingers, device)) {
|
||||||
|
struct seatop_default_event *seatop = seat->seatop_data;
|
||||||
|
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL, event->fingers);
|
||||||
|
workspace_scroll_begin(seat, SWIPE_GESTURE_DIRECTION_HORIZONTAL);
|
||||||
|
} else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL, event->fingers, device)) {
|
||||||
|
struct seatop_default_event *seatop = seat->seatop_data;
|
||||||
|
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL, event->fingers);
|
||||||
|
workspace_scroll_begin(seat, SWIPE_GESTURE_DIRECTION_VERTICAL);
|
||||||
} else {
|
} else {
|
||||||
// ... otherwise forward to client
|
// ... otherwise forward to client
|
||||||
struct sway_cursor *cursor = seat->cursor;
|
struct sway_cursor *cursor = seat->cursor;
|
||||||
|
@ -1070,6 +1078,29 @@ static void handle_swipe_update(struct sway_seat *seat,
|
||||||
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
||||||
gesture_tracker_update(&seatop->gestures,
|
gesture_tracker_update(&seatop->gestures,
|
||||||
event->dx, event->dy, NAN, NAN);
|
event->dx, event->dy, NAN, NAN);
|
||||||
|
} else if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL) ||
|
||||||
|
gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL)) {
|
||||||
|
// Find the gesture and update the swipe percentage
|
||||||
|
struct gesture_tracker *tracker = &seatop->gestures;
|
||||||
|
struct sway_input_device *device =
|
||||||
|
event->pointer ? event->pointer->base.data : NULL;
|
||||||
|
// Determine name of input that received gesture
|
||||||
|
char *input = device
|
||||||
|
? input_device_get_identifier(device->wlr_device)
|
||||||
|
: strdup("*");
|
||||||
|
|
||||||
|
struct gesture gesture = {
|
||||||
|
.fingers = tracker->fingers,
|
||||||
|
.type = tracker->type,
|
||||||
|
.directions = GESTURE_DIRECTION_NONE,
|
||||||
|
};
|
||||||
|
struct sway_gesture_binding *binding = gesture_binding_match(
|
||||||
|
config->current_mode->gesture_bindings, &gesture, input);
|
||||||
|
|
||||||
|
if (binding) {
|
||||||
|
int invert = binding->flags & BINDING_INVERTED ? 1 : -1;
|
||||||
|
workspace_scroll_update(seat, tracker, event, invert);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// ... otherwise forward to client
|
// ... otherwise forward to client
|
||||||
struct sway_cursor *cursor = seat->cursor;
|
struct sway_cursor *cursor = seat->cursor;
|
||||||
|
@ -1083,13 +1114,17 @@ static void handle_swipe_end(struct sway_seat *seat,
|
||||||
struct wlr_pointer_swipe_end_event *event) {
|
struct wlr_pointer_swipe_end_event *event) {
|
||||||
// Ensure gesture is being tracked and was not cancelled
|
// Ensure gesture is being tracked and was not cancelled
|
||||||
struct seatop_default_event *seatop = seat->seatop_data;
|
struct seatop_default_event *seatop = seat->seatop_data;
|
||||||
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE) &&
|
||||||
|
!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL) &&
|
||||||
|
!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL)) {
|
||||||
struct sway_cursor *cursor = seat->cursor;
|
struct sway_cursor *cursor = seat->cursor;
|
||||||
wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,
|
wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,
|
||||||
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
|
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->cancelled) {
|
if (event->cancelled &&
|
||||||
|
seatop->gestures.type != GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL
|
||||||
|
&& seatop->gestures.type != GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL) {
|
||||||
gesture_tracker_cancel(&seatop->gestures);
|
gesture_tracker_cancel(&seatop->gestures);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1101,7 +1136,15 @@ static void handle_swipe_end(struct sway_seat *seat,
|
||||||
&seatop->gestures, device);
|
&seatop->gestures, device);
|
||||||
|
|
||||||
if (binding) {
|
if (binding) {
|
||||||
|
switch (binding->gesture.type) {
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
|
||||||
|
workspace_scroll_end(seat);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
gesture_binding_execute(seat, binding);
|
gesture_binding_execute(seat, binding);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,6 +1160,23 @@ static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) {
|
||||||
e->previous_node = node_at_coords(seat,
|
e->previous_node = node_at_coords(seat,
|
||||||
cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
|
cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
|
||||||
|
|
||||||
|
// Reset the swipe if any other button is pressed during the swipe
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
if (focused_ws) {
|
||||||
|
struct sway_output *output = focused_ws->output;
|
||||||
|
struct workspace_scroll workspace_scroll_default = workspace_scroll_get_default();
|
||||||
|
switch (e->gestures.type) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
|
||||||
|
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
|
||||||
|
if (!workspace_scroll_equal(&output->workspace_scroll, &workspace_scroll_default)) {
|
||||||
|
workspace_scroll_reset(seat, NULL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (surface) {
|
if (surface) {
|
||||||
if (seat_is_input_allowed(seat, surface)) {
|
if (seat_is_input_allowed(seat, surface)) {
|
||||||
wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy);
|
wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy);
|
||||||
|
|
|
@ -24,6 +24,8 @@ sway_sources = files(
|
||||||
'desktop/xdg_shell.c',
|
'desktop/xdg_shell.c',
|
||||||
'desktop/launcher.c',
|
'desktop/launcher.c',
|
||||||
|
|
||||||
|
'fx_util/animation_utils.c',
|
||||||
|
|
||||||
'input/input-manager.c',
|
'input/input-manager.c',
|
||||||
'input/cursor.c',
|
'input/cursor.c',
|
||||||
'input/keyboard.c',
|
'input/keyboard.c',
|
||||||
|
@ -144,6 +146,7 @@ sway_sources = files(
|
||||||
'commands/unmark.c',
|
'commands/unmark.c',
|
||||||
'commands/urgent.c',
|
'commands/urgent.c',
|
||||||
'commands/workspace.c',
|
'commands/workspace.c',
|
||||||
|
'commands/workspace_gesture.c',
|
||||||
'commands/workspace_layout.c',
|
'commands/workspace_layout.c',
|
||||||
'commands/ws_auto_back_and_forth.c',
|
'commands/ws_auto_back_and_forth.c',
|
||||||
'commands/xwayland.c',
|
'commands/xwayland.c',
|
||||||
|
|
|
@ -63,33 +63,38 @@
|
||||||
#define SWAY_LAYER_SHELL_VERSION 4
|
#define SWAY_LAYER_SHELL_VERSION 4
|
||||||
|
|
||||||
#if WLR_HAS_DRM_BACKEND
|
#if WLR_HAS_DRM_BACKEND
|
||||||
static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
|
static void handle_drm_lease_request(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
/* We only offer non-desktop outputs, but in the future we might want to do
|
/* We only offer non-desktop outputs, but in the future we might want to do
|
||||||
* more logic here. */
|
* more logic here. */
|
||||||
|
|
||||||
struct wlr_drm_lease_request_v1 *req = data;
|
struct wlr_drm_lease_request_v1 *req = data;
|
||||||
struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
|
struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
|
||||||
if (!lease) {
|
if (!lease)
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Failed to grant lease request");
|
sway_log(SWAY_ERROR, "Failed to grant lease request");
|
||||||
wlr_drm_lease_request_v1_reject(req);
|
wlr_drm_lease_request_v1_reject(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool is_privileged(const struct wl_global *global) {
|
static bool is_privileged(const struct wl_global *global)
|
||||||
|
{
|
||||||
#if WLR_HAS_DRM_BACKEND
|
#if WLR_HAS_DRM_BACKEND
|
||||||
if (server.drm_lease_manager != NULL) {
|
if (server.drm_lease_manager != NULL)
|
||||||
|
{
|
||||||
struct wlr_drm_lease_device_v1 *drm_lease_dev;
|
struct wlr_drm_lease_device_v1 *drm_lease_dev;
|
||||||
wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) {
|
wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link)
|
||||||
if (drm_lease_dev->global == global) {
|
{
|
||||||
|
if (drm_lease_dev->global == global)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return
|
return global == server.output_manager_v1->global ||
|
||||||
global == server.output_manager_v1->global ||
|
|
||||||
global == server.output_power_manager_v1->global ||
|
global == server.output_power_manager_v1->global ||
|
||||||
global == server.input_method->global ||
|
global == server.input_method->global ||
|
||||||
global == server.foreign_toplevel_manager->global ||
|
global == server.foreign_toplevel_manager->global ||
|
||||||
|
@ -107,10 +112,12 @@ static bool is_privileged(const struct wl_global *global) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool filter_global(const struct wl_client *client,
|
static bool filter_global(const struct wl_client *client,
|
||||||
const struct wl_global *global, void *data) {
|
const struct wl_global *global, void *data)
|
||||||
|
{
|
||||||
#if HAVE_XWAYLAND
|
#if HAVE_XWAYLAND
|
||||||
struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
|
struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
|
||||||
if (xwayland && global == xwayland->shell_v1->global) {
|
if (xwayland && global == xwayland->shell_v1->global)
|
||||||
|
{
|
||||||
return xwayland->server != NULL && client == xwayland->server->client;
|
return xwayland->server != NULL && client == xwayland->server->client;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -120,14 +127,16 @@ static bool filter_global(const struct wl_client *client,
|
||||||
const struct wlr_security_context_v1_state *security_context =
|
const struct wlr_security_context_v1_state *security_context =
|
||||||
wlr_security_context_manager_v1_lookup_client(
|
wlr_security_context_manager_v1_lookup_client(
|
||||||
server.security_context_manager_v1, (struct wl_client *)client);
|
server.security_context_manager_v1, (struct wl_client *)client);
|
||||||
if (is_privileged(global)) {
|
if (is_privileged(global))
|
||||||
|
{
|
||||||
return security_context == NULL;
|
return security_context == NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_init(struct sway_server *server) {
|
bool server_init(struct sway_server *server)
|
||||||
|
{
|
||||||
sway_log(SWAY_DEBUG, "Initializing Wayland server");
|
sway_log(SWAY_DEBUG, "Initializing Wayland server");
|
||||||
server->wl_display = wl_display_create();
|
server->wl_display = wl_display_create();
|
||||||
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
|
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
|
||||||
|
@ -135,20 +144,23 @@ bool server_init(struct sway_server *server) {
|
||||||
wl_display_set_global_filter(server->wl_display, filter_global, NULL);
|
wl_display_set_global_filter(server->wl_display, filter_global, NULL);
|
||||||
|
|
||||||
server->backend = wlr_backend_autocreate(server->wl_display, &server->session);
|
server->backend = wlr_backend_autocreate(server->wl_display, &server->session);
|
||||||
if (!server->backend) {
|
if (!server->backend)
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Unable to create backend");
|
sway_log(SWAY_ERROR, "Unable to create backend");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
server->renderer = fx_renderer_create(server->backend);
|
server->renderer = fx_renderer_create(server->backend);
|
||||||
if (!server->renderer) {
|
if (!server->renderer)
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Failed to create fx_renderer");
|
sway_log(SWAY_ERROR, "Failed to create fx_renderer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
|
wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
|
||||||
|
|
||||||
if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) {
|
if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL)
|
||||||
|
{
|
||||||
wlr_drm_create(server->wl_display, server->renderer);
|
wlr_drm_create(server->wl_display, server->renderer);
|
||||||
server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
|
server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
|
||||||
server->wl_display, 4, server->renderer);
|
server->wl_display, 4, server->renderer);
|
||||||
|
@ -156,7 +168,8 @@ bool server_init(struct sway_server *server) {
|
||||||
|
|
||||||
server->allocator = wlr_allocator_autocreate(server->backend,
|
server->allocator = wlr_allocator_autocreate(server->backend,
|
||||||
server->renderer);
|
server->renderer);
|
||||||
if (!server->allocator) {
|
if (!server->allocator)
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Failed to create allocator");
|
sway_log(SWAY_ERROR, "Failed to create allocator");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -258,11 +271,14 @@ bool server_init(struct sway_server *server) {
|
||||||
#if WLR_HAS_DRM_BACKEND
|
#if WLR_HAS_DRM_BACKEND
|
||||||
server->drm_lease_manager =
|
server->drm_lease_manager =
|
||||||
wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
|
wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
|
||||||
if (server->drm_lease_manager) {
|
if (server->drm_lease_manager)
|
||||||
|
{
|
||||||
server->drm_lease_request.notify = handle_drm_lease_request;
|
server->drm_lease_request.notify = handle_drm_lease_request;
|
||||||
wl_signal_add(&server->drm_lease_manager->events.request,
|
wl_signal_add(&server->drm_lease_manager->events.request,
|
||||||
&server->drm_lease_request);
|
&server->drm_lease_request);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
|
sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
|
||||||
sway_log(SWAY_INFO, "VR will not be available");
|
sway_log(SWAY_INFO, "VR will not be available");
|
||||||
}
|
}
|
||||||
|
@ -302,26 +318,32 @@ bool server_init(struct sway_server *server) {
|
||||||
|
|
||||||
// Avoid using "wayland-0" as display socket
|
// Avoid using "wayland-0" as display socket
|
||||||
char name_candidate[16];
|
char name_candidate[16];
|
||||||
for (unsigned int i = 1; i <= 32; ++i) {
|
for (unsigned int i = 1; i <= 32; ++i)
|
||||||
|
{
|
||||||
snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
|
snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
|
||||||
if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {
|
if (wl_display_add_socket(server->wl_display, name_candidate) >= 0)
|
||||||
|
{
|
||||||
server->socket = strdup(name_candidate);
|
server->socket = strdup(name_candidate);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!server->socket) {
|
if (!server->socket)
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Unable to open wayland socket");
|
sway_log(SWAY_ERROR, "Unable to open wayland socket");
|
||||||
wlr_backend_destroy(server->backend);
|
wlr_backend_destroy(server->backend);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
server->headless_backend = wlr_headless_backend_create(server->wl_display);
|
server->headless_backend = wlr_headless_backend_create(server->wl_display);
|
||||||
if (!server->headless_backend) {
|
if (!server->headless_backend)
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
|
sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
|
||||||
wlr_backend_destroy(server->backend);
|
wlr_backend_destroy(server->backend);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
wlr_multi_backend_add(server->backend, server->headless_backend);
|
wlr_multi_backend_add(server->backend, server->headless_backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +353,8 @@ bool server_init(struct sway_server *server) {
|
||||||
root->fallback_output = output_create(wlr_output);
|
root->fallback_output = output_create(wlr_output);
|
||||||
|
|
||||||
// This may have been set already via -Dtxn-timeout
|
// This may have been set already via -Dtxn-timeout
|
||||||
if (!server->txn_timeout_ms) {
|
if (!server->txn_timeout_ms)
|
||||||
|
{
|
||||||
server->txn_timeout_ms = 200;
|
server->txn_timeout_ms = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +366,8 @@ bool server_init(struct sway_server *server) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_fini(struct sway_server *server) {
|
void server_fini(struct sway_server *server)
|
||||||
|
{
|
||||||
// TODO: free sway-specific resources
|
// TODO: free sway-specific resources
|
||||||
#if HAVE_XWAYLAND
|
#if HAVE_XWAYLAND
|
||||||
wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
|
wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
|
||||||
|
@ -353,18 +377,23 @@ void server_fini(struct sway_server *server) {
|
||||||
list_free(server->dirty_nodes);
|
list_free(server->dirty_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_start(struct sway_server *server) {
|
bool server_start(struct sway_server *server)
|
||||||
|
{
|
||||||
#if HAVE_XWAYLAND
|
#if HAVE_XWAYLAND
|
||||||
if (config->xwayland != XWAYLAND_MODE_DISABLED) {
|
if (config->xwayland != XWAYLAND_MODE_DISABLED)
|
||||||
|
{
|
||||||
sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)",
|
sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)",
|
||||||
config->xwayland == XWAYLAND_MODE_LAZY);
|
config->xwayland == XWAYLAND_MODE_LAZY);
|
||||||
server->xwayland.wlr_xwayland =
|
server->xwayland.wlr_xwayland =
|
||||||
wlr_xwayland_create(server->wl_display, server->compositor,
|
wlr_xwayland_create(server->wl_display, server->compositor,
|
||||||
config->xwayland == XWAYLAND_MODE_LAZY);
|
config->xwayland == XWAYLAND_MODE_LAZY);
|
||||||
if (!server->xwayland.wlr_xwayland) {
|
if (!server->xwayland.wlr_xwayland)
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Failed to start Xwayland");
|
sway_log(SWAY_ERROR, "Failed to start Xwayland");
|
||||||
unsetenv("DISPLAY");
|
unsetenv("DISPLAY");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface,
|
wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface,
|
||||||
&server->xwayland_surface);
|
&server->xwayland_surface);
|
||||||
server->xwayland_surface.notify = handle_xwayland_surface;
|
server->xwayland_surface.notify = handle_xwayland_surface;
|
||||||
|
@ -379,13 +408,15 @@ bool server_start(struct sway_server *server) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (config->primary_selection) {
|
if (config->primary_selection)
|
||||||
|
{
|
||||||
wlr_primary_selection_v1_device_manager_create(server->wl_display);
|
wlr_primary_selection_v1_device_manager_create(server->wl_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
sway_log(SWAY_INFO, "Starting backend on wayland display '%s'",
|
sway_log(SWAY_INFO, "Starting backend on wayland display '%s'",
|
||||||
server->socket);
|
server->socket);
|
||||||
if (!wlr_backend_start(server->backend)) {
|
if (!wlr_backend_start(server->backend))
|
||||||
|
{
|
||||||
sway_log(SWAY_ERROR, "Failed to start backend");
|
sway_log(SWAY_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(server->backend);
|
wlr_backend_destroy(server->backend);
|
||||||
return false;
|
return false;
|
||||||
|
@ -394,7 +425,8 @@ bool server_start(struct sway_server *server) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_run(struct sway_server *server) {
|
void server_run(struct sway_server *server)
|
||||||
|
{
|
||||||
sway_log(SWAY_INFO, "Running compositor on wayland display '%s'",
|
sway_log(SWAY_INFO, "Running compositor on wayland display '%s'",
|
||||||
server->socket);
|
server->socket);
|
||||||
wl_display_run(server->wl_display);
|
wl_display_run(server->wl_display);
|
||||||
|
|
|
@ -548,6 +548,61 @@ runtime.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*bindworkspacegesture* [--input-device=<device>] [--no-warn] \
|
||||||
|
[--inverted] [<fingers>][:direction]
|
||||||
|
Binds the swipe to switch workspaces in a 1:1 fashion, but only when
|
||||||
|
detected. Optionally can be limited to bind to a certain number of
|
||||||
|
_fingers_ or to certain _directions_.
|
||||||
|
|
||||||
|
[[ *type*
|
||||||
|
:[ *fingers*
|
||||||
|
:< *direction*
|
||||||
|
| swipe
|
||||||
|
: 3 - 5
|
||||||
|
: horizontal, vertical
|
||||||
|
|
||||||
|
The _fingers_ can be limited to any sensible number or left empty to accept
|
||||||
|
any finger counts.
|
||||||
|
Valid directions are _horizontal_ and _vertical_.
|
||||||
|
|
||||||
|
If a _input-device_ is given, the binding will only be executed for
|
||||||
|
that input device and will be executed instead of any binding that is
|
||||||
|
generic to all devices. By default, if you overwrite a binding,
|
||||||
|
swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
|
||||||
|
|
||||||
|
If _inverted_ is given, the swipe direction will be reversed.
|
||||||
|
|
||||||
|
The priority for matching bindings is as follows: input device, then
|
||||||
|
exact matches.
|
||||||
|
|
||||||
|
Gestures executed while the pointer is above a bar are not handled by sway.
|
||||||
|
See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
# Allow switching between workspaces with left and right swipes
|
||||||
|
bindworkspacegesture 4:horizontal
|
||||||
|
# Allow switching between workspaces with up and down swipes
|
||||||
|
bindworkspacegesture 3:vertical
|
||||||
|
# Allow switching between workspaces with up and down swipes but inverted
|
||||||
|
bindworkspacegesture --inverted 3:vertical
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
*workspace_gesture_spring_size* <value>
|
||||||
|
Adjusts the workspace gestures spring size. Can use values between
|
||||||
|
0 (disabled) and 100 while 50 is the default value.
|
||||||
|
|
||||||
|
*workspace_gesture_wrap_around* <enable|disable>
|
||||||
|
Sets whether or not the workspace gesture should wrap around when trying to
|
||||||
|
swipe past the first/last workspace. Disables the
|
||||||
|
_workspace_gesture_spring_size_ config option. Disabled by default.
|
||||||
|
|
||||||
|
*workspace_gesture_threshold* <value>
|
||||||
|
Adjusts the swipe threshold of switching workspaces. A lower value makes it
|
||||||
|
easier to switch workspaces. Accepts values between 0.1 (small swipes) and
|
||||||
|
0.9 (large swipes). The default value is set to 0.5.
|
||||||
|
|
||||||
*client.background* <color>
|
*client.background* <color>
|
||||||
This command is ignored and is only present for i3 compatibility.
|
This command is ignored and is only present for i3 compatibility.
|
||||||
|
|
||||||
|
|
|
@ -350,11 +350,17 @@ static struct sway_container *view_container_at(struct sway_node *parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sway_container *container = parent->sway_container;
|
struct sway_container *container = parent->sway_container;
|
||||||
|
|
||||||
|
int gaps = 0;
|
||||||
|
if (container->current.workspace) {
|
||||||
|
gaps = container->current.workspace->gaps_inner;
|
||||||
|
}
|
||||||
|
|
||||||
struct wlr_box box = {
|
struct wlr_box box = {
|
||||||
.x = container->pending.x,
|
.x = container->pending.x - (gaps / 2),
|
||||||
.y = container->pending.y,
|
.y = container->pending.y - (gaps / 2),
|
||||||
.width = container->pending.width,
|
.width = container->pending.width + gaps,
|
||||||
.height = container->pending.height,
|
.height = container->pending.height + gaps,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (wlr_box_contains_point(&box, lx, ly)) {
|
if (wlr_box_contains_point(&box, lx, ly)) {
|
||||||
|
|
|
@ -354,11 +354,11 @@ struct sway_workspace *workspace_by_name(const char *name) {
|
||||||
if (current && strcmp(name, "prev") == 0) {
|
if (current && strcmp(name, "prev") == 0) {
|
||||||
return workspace_prev(current);
|
return workspace_prev(current);
|
||||||
} else if (current && strcmp(name, "prev_on_output") == 0) {
|
} else if (current && strcmp(name, "prev_on_output") == 0) {
|
||||||
return workspace_output_prev(current);
|
return workspace_output_prev(current, true);
|
||||||
} else if (current && strcmp(name, "next") == 0) {
|
} else if (current && strcmp(name, "next") == 0) {
|
||||||
return workspace_next(current);
|
return workspace_next(current);
|
||||||
} else if (current && strcmp(name, "next_on_output") == 0) {
|
} else if (current && strcmp(name, "next_on_output") == 0) {
|
||||||
return workspace_output_next(current);
|
return workspace_output_next(current, true);
|
||||||
} else if (strcmp(name, "current") == 0) {
|
} else if (strcmp(name, "current") == 0) {
|
||||||
return current;
|
return current;
|
||||||
} else if (strcasecmp(name, "back_and_forth") == 0) {
|
} else if (strcasecmp(name, "back_and_forth") == 0) {
|
||||||
|
@ -521,7 +521,7 @@ struct sway_workspace *workspace_next(struct sway_workspace *workspace) {
|
||||||
* otherwise the next one is returned.
|
* otherwise the next one is returned.
|
||||||
*/
|
*/
|
||||||
static struct sway_workspace *workspace_output_prev_next_impl(
|
static struct sway_workspace *workspace_output_prev_next_impl(
|
||||||
struct sway_output *output, int dir) {
|
struct sway_output *output, int dir, bool should_wrap) {
|
||||||
struct sway_seat *seat = input_manager_current_seat();
|
struct sway_seat *seat = input_manager_current_seat();
|
||||||
struct sway_workspace *workspace = seat_get_focused_workspace(seat);
|
struct sway_workspace *workspace = seat_get_focused_workspace(seat);
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
|
@ -531,17 +531,27 @@ static struct sway_workspace *workspace_output_prev_next_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = list_find(output->workspaces, workspace);
|
int index = list_find(output->workspaces, workspace);
|
||||||
size_t new_index = wrap(index + dir, output->workspaces->length);
|
size_t new_index;
|
||||||
|
if (!should_wrap) {
|
||||||
|
new_index = index += dir;
|
||||||
|
if (index < 0 || index >= output->workspaces->length) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_index = wrap(index + dir, output->workspaces->length);
|
||||||
|
}
|
||||||
return output->workspaces->items[new_index];
|
return output->workspaces->items[new_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
|
struct sway_workspace *workspace_output_next(struct sway_workspace *current,
|
||||||
return workspace_output_prev_next_impl(current->output, 1);
|
bool should_wrap) {
|
||||||
|
return workspace_output_prev_next_impl(current->output, 1, should_wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
|
struct sway_workspace *workspace_output_prev(struct sway_workspace *current,
|
||||||
return workspace_output_prev_next_impl(current->output, -1);
|
bool should_wrap) {
|
||||||
|
return workspace_output_prev_next_impl(current->output, -1, should_wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sway_workspace *workspace_auto_back_and_forth(
|
struct sway_workspace *workspace_auto_back_and_forth(
|
||||||
|
@ -571,6 +581,7 @@ bool workspace_switch(struct sway_workspace *workspace) {
|
||||||
|
|
||||||
sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",
|
sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",
|
||||||
workspace, workspace->name);
|
workspace, workspace->name);
|
||||||
|
workspace->output->workspace_scroll = workspace_scroll_get_default();
|
||||||
struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node);
|
struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node);
|
||||||
if (next == NULL) {
|
if (next == NULL) {
|
||||||
next = &workspace->node;
|
next = &workspace->node;
|
||||||
|
|
Loading…
Reference in a new issue