Compare commits
42 commits
a81e80fe68
...
b7d2d56263
Author | SHA1 | Date | |
---|---|---|---|
Alex Janka | b7d2d56263 | ||
Alex Janka | e7a847d933 | ||
Alex Janka | ff677c9255 | ||
Alex Janka | 23910437c4 | ||
4166fd3728 | |||
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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
switch (type) {
|
||||
case GESTURE_TYPE_NONE:
|
||||
|
@ -100,6 +154,10 @@ const char *gesture_type_string(enum gesture_type type) {
|
|||
return "pinch";
|
||||
case GESTURE_TYPE_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;
|
||||
|
@ -313,6 +371,8 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
|
|||
}
|
||||
// Gesture without any direction
|
||||
case GESTURE_TYPE_HOLD:
|
||||
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
|
||||
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
|
||||
break;
|
||||
// Not tracking any gesture
|
||||
case GESTURE_TYPE_NONE:
|
||||
|
|
|
@ -12,6 +12,8 @@ enum gesture_type {
|
|||
GESTURE_TYPE_HOLD,
|
||||
GESTURE_TYPE_PINCH,
|
||||
GESTURE_TYPE_SWIPE,
|
||||
GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL,
|
||||
GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL,
|
||||
};
|
||||
|
||||
// Turns single type enum value to constant string representation.
|
||||
|
@ -57,6 +59,13 @@ struct gesture {
|
|||
*/
|
||||
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
|
||||
char *gesture_to_string(struct gesture *gesture);
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ sway_cmd cmd_bindcode;
|
|||
sway_cmd cmd_bindgesture;
|
||||
sway_cmd cmd_bindswitch;
|
||||
sway_cmd cmd_bindsym;
|
||||
sway_cmd cmd_bindworkspacegesture;
|
||||
sway_cmd cmd_blur;
|
||||
sway_cmd cmd_blur_brightness;
|
||||
sway_cmd cmd_blur_contrast;
|
||||
|
@ -223,11 +224,15 @@ sway_cmd cmd_unbindcode;
|
|||
sway_cmd cmd_unbindswitch;
|
||||
sway_cmd cmd_unbindgesture;
|
||||
sway_cmd cmd_unbindsym;
|
||||
sway_cmd cmd_unbindworkspacegesture;
|
||||
sway_cmd cmd_unmark;
|
||||
sway_cmd cmd_urgent;
|
||||
sway_cmd cmd_workspace;
|
||||
sway_cmd cmd_workspace_layout;
|
||||
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 bar_cmd_bindcode;
|
||||
|
|
|
@ -51,6 +51,7 @@ enum binding_flags {
|
|||
BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
|
||||
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_INVERTED = 1 << 10, // workspace gesture only; gesture is inverted
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -501,6 +502,10 @@ struct sway_config {
|
|||
bool titlebar_separator;
|
||||
bool scratchpad_minimize;
|
||||
|
||||
int workspace_gesture_spring_size;
|
||||
bool workspace_gesture_wrap_around;
|
||||
float workspace_gesture_threshold;
|
||||
|
||||
list_t *layer_criteria;
|
||||
|
||||
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_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 {
|
||||
float alpha;
|
||||
float saturation;
|
||||
|
@ -32,6 +45,7 @@ struct render_data {
|
|||
pixman_region32_t *damage;
|
||||
struct wlr_box *clip_box;
|
||||
struct decoration_data deco_data;
|
||||
bool on_focused_workspace;
|
||||
struct sway_view *view;
|
||||
};
|
||||
|
||||
|
@ -71,6 +85,8 @@ struct sway_output {
|
|||
struct wl_listener needs_frame;
|
||||
struct wl_listener request_state;
|
||||
|
||||
struct workspace_scroll workspace_scroll;
|
||||
|
||||
struct {
|
||||
struct wl_signal disable;
|
||||
} 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 *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);
|
||||
|
||||
#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_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_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);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ static const struct cmd_handler handlers[] = {
|
|||
{ "bindgesture", cmd_bindgesture },
|
||||
{ "bindswitch", cmd_bindswitch },
|
||||
{ "bindsym", cmd_bindsym },
|
||||
{ "bindworkspacegesture", cmd_bindworkspacegesture },
|
||||
{ "blur", cmd_blur },
|
||||
{ "blur_brightness", cmd_blur_brightness },
|
||||
{ "blur_contrast", cmd_blur_contrast },
|
||||
|
@ -96,6 +97,7 @@ static const struct cmd_handler handlers[] = {
|
|||
{ "no_focus", cmd_no_focus },
|
||||
{ "output", cmd_output },
|
||||
{ "popup_during_fullscreen", cmd_popup_during_fullscreen },
|
||||
{ "primary_selection", cmd_primary_selection },
|
||||
{ "seat", cmd_seat },
|
||||
{ "set", cmd_set },
|
||||
{ "shadow_blur_radius", cmd_shadow_blur_radius },
|
||||
|
@ -118,8 +120,12 @@ static const struct cmd_handler handlers[] = {
|
|||
{ "unbindgesture", cmd_unbindgesture },
|
||||
{ "unbindswitch", cmd_unbindswitch },
|
||||
{ "unbindsym", cmd_unbindsym },
|
||||
{ "unbindworkspacegesture", cmd_unbindworkspacegesture },
|
||||
{ "workspace", cmd_workspace },
|
||||
{ "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 */
|
||||
|
|
|
@ -164,3 +164,76 @@ struct cmd_results *cmd_bindgesture(int argc, char **argv) {
|
|||
struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
|
||||
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/config.h"
|
||||
#include "sway/input/seat.h"
|
||||
#include "sway/output.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "stringop.h"
|
||||
#include "util.h"
|
||||
|
||||
static struct workspace_config *workspace_config_find_or_create(char *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->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;
|
||||
|
||||
// The keysym to keycode translation
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "log.h"
|
||||
#include "scenefx/render/pass.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/fx_util/animation_utils.h"
|
||||
#include "sway/desktop/transaction.h"
|
||||
#include "sway/input/input-manager.h"
|
||||
#include "sway/input/seat.h"
|
||||
|
@ -36,6 +37,10 @@
|
|||
#include "sway/tree/root.h"
|
||||
#include "sway/tree/view.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
|
||||
#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
|
||||
// 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
|
||||
static bool last_scanned_out = false;
|
||||
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_damage_ring_set_bounds(&output->damage_ring, width, height);
|
||||
update_output_manager_config(server);
|
||||
|
||||
output->workspace_scroll = workspace_scroll_get_default();
|
||||
}
|
||||
|
||||
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);
|
||||
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.
|
||||
*
|
||||
|
@ -351,6 +428,12 @@ static void render_surface_iterator(struct sway_output *output,
|
|||
clip_box.x = fmax(dst_box.x, data->clip_box->x);
|
||||
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(&clip_box, wlr_output->scale);
|
||||
|
||||
|
@ -421,6 +504,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) {
|
||||
struct render_data data = {
|
||||
.deco_data = get_undecorated_decoration_data(),
|
||||
.on_focused_workspace = true,
|
||||
.ctx = ctx,
|
||||
};
|
||||
output_layer_for_each_toplevel_surface(ctx->output, layer_surfaces,
|
||||
|
@ -430,6 +514,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) {
|
||||
struct render_data data = {
|
||||
.deco_data = get_undecorated_decoration_data(),
|
||||
.on_focused_workspace = true,
|
||||
.ctx = ctx,
|
||||
};
|
||||
output_layer_for_each_popup_surface(ctx->output, layer_surfaces,
|
||||
|
@ -440,6 +525,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) {
|
||||
struct render_data data = {
|
||||
.deco_data = get_undecorated_decoration_data(),
|
||||
.on_focused_workspace = true,
|
||||
.ctx = ctx,
|
||||
};
|
||||
output_unmanaged_for_each_surface(ctx->output, unmanaged,
|
||||
|
@ -450,6 +536,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) {
|
||||
struct render_data data = {
|
||||
.deco_data = get_undecorated_decoration_data(),
|
||||
.on_focused_workspace = true,
|
||||
.ctx = ctx,
|
||||
};
|
||||
output_drag_icons_for_each_surface(ctx->output, drag_icons,
|
||||
|
@ -548,9 +635,10 @@ void premultiply_alpha(float color[4], float opacity) {
|
|||
}
|
||||
|
||||
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 = {
|
||||
.deco_data = deco_data,
|
||||
.on_focused_workspace = on_focused_workspace,
|
||||
.view = view,
|
||||
.ctx = ctx,
|
||||
};
|
||||
|
@ -598,6 +686,7 @@ static void render_view_popups(struct fx_render_context *ctx, struct sway_view *
|
|||
struct decoration_data deco_data) {
|
||||
struct render_data data = {
|
||||
.deco_data = deco_data,
|
||||
.on_focused_workspace = true,
|
||||
.view = view,
|
||||
.ctx = ctx,
|
||||
};
|
||||
|
@ -700,13 +789,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.
|
||||
*/
|
||||
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;
|
||||
|
||||
if (!wl_list_empty(&view->saved_buffers)) {
|
||||
render_saved_view(ctx, view, deco_data);
|
||||
} 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;
|
||||
|
@ -725,6 +815,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
|||
box.y = floor(state->y) - ctx->output->ly;
|
||||
box.width = state->width;
|
||||
box.height = state->height;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||
scale_box(&box, output_scale);
|
||||
int shadow_corner_radius = corner_radius == 0 ? 0 : corner_radius + state->border_thickness;
|
||||
float* shadow_color = view_is_urgent(view) || state->focused ?
|
||||
|
@ -748,6 +839,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
|||
box.y = floor(state->content_y);
|
||||
box.width = state->border_thickness;
|
||||
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) {
|
||||
box.y += corner_radius;
|
||||
box.height -= corner_radius;
|
||||
|
@ -771,6 +863,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
|||
box.y = floor(state->content_y);
|
||||
box.width = state->border_thickness;
|
||||
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) {
|
||||
box.y += corner_radius;
|
||||
box.height -= corner_radius;
|
||||
|
@ -790,6 +883,8 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
|||
box.y = floor(state->content_y + state->content_height);
|
||||
box.width = state->width;
|
||||
box.height = state->border_thickness;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||
|
||||
// adjust sizing for rounded border corners
|
||||
if (deco_data.corner_radius) {
|
||||
box.x += corner_radius + state->border_thickness;
|
||||
|
@ -805,6 +900,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
|||
if (state->border_left) {
|
||||
box.x = floor(state->x);
|
||||
box.y = floor(state->y + state->height - size);
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||
box.width = size;
|
||||
box.height = size;
|
||||
scale_box(&box, output_scale);
|
||||
|
@ -814,8 +910,7 @@ static void render_view(struct fx_render_context *ctx, struct sway_container *co
|
|||
if (state->border_right) {
|
||||
box.x = floor(state->x + state->width - size);
|
||||
box.y = floor(state->y + state->height - size);
|
||||
box.width = size;
|
||||
box.height = size;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rounded_border_corner(ctx, &box, color, scaled_corner_radius,
|
||||
scaled_border_thickness, BOTTOM_RIGHT);
|
||||
|
@ -836,7 +931,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,
|
||||
int x, int y, int width, struct border_colors *colors, int corner_radius,
|
||||
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;
|
||||
float color[4];
|
||||
struct sway_output *output = ctx->output;
|
||||
|
@ -868,6 +963,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.width -= corner_radius;
|
||||
}
|
||||
}
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rect(ctx, &box, color);
|
||||
|
||||
|
@ -877,6 +973,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.y = y + container_titlebar_height() - titlebar_border_thickness;
|
||||
box.width = width;
|
||||
box.height = titlebar_border_thickness;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rect(ctx, &box, color);
|
||||
}
|
||||
|
@ -890,6 +987,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.height -= corner_radius;
|
||||
box.y += corner_radius;
|
||||
}
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rect(ctx, &box, color);
|
||||
|
||||
|
@ -902,6 +1000,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.height -= corner_radius;
|
||||
box.y += corner_radius;
|
||||
}
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rect(ctx, &box, color);
|
||||
|
||||
|
@ -913,6 +1012,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.y = y;
|
||||
box.width = corner_radius * 2;
|
||||
box.height = corner_radius * 2;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rounded_border_corner(ctx, &box, color, corner_radius,
|
||||
titlebar_border_thickness * output_scale, TOP_LEFT);
|
||||
|
@ -924,6 +1024,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.y = y;
|
||||
box.width = corner_radius * 2;
|
||||
box.height = corner_radius * 2;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rounded_border_corner(ctx, &box, color, corner_radius,
|
||||
titlebar_border_thickness * output_scale, TOP_RIGHT);
|
||||
|
@ -977,6 +1078,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
if (ob_inner_width < clip_box.width) {
|
||||
clip_box.width = ob_inner_width;
|
||||
}
|
||||
adjust_box_to_workspace_offset(&texture_box, on_focused_workspace, output);
|
||||
render_texture(ctx, marks_texture,
|
||||
NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data);
|
||||
|
||||
|
@ -987,11 +1089,13 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.y = roundf((y + titlebar_border_thickness) * output_scale);
|
||||
box.width = clip_box.width;
|
||||
box.height = ob_padding_above;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
render_rect(ctx, &box, color);
|
||||
|
||||
// Padding below
|
||||
box.y += ob_padding_above + clip_box.height;
|
||||
box.height = ob_padding_below + bottom_border_compensation;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
render_rect(ctx, &box, color);
|
||||
}
|
||||
|
||||
|
@ -1049,6 +1153,8 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
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,
|
||||
NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data);
|
||||
|
||||
|
@ -1059,6 +1165,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.y = roundf((y + titlebar_border_thickness) * output_scale);
|
||||
box.width = clip_box.width;
|
||||
box.height = ob_padding_above;
|
||||
// adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
render_rect(ctx, &box, color);
|
||||
|
||||
// Padding below
|
||||
|
@ -1098,6 +1205,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.y = roundf(bg_y * output_scale);
|
||||
box.height = ob_bg_height + bottom_border_compensation;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, output);
|
||||
render_rect(ctx, &box, color);
|
||||
}
|
||||
|
||||
|
@ -1112,6 +1220,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
if (box.x + box.width < left_x) {
|
||||
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)) {
|
||||
render_rounded_rect(ctx, &box, color, corner_radius, TOP_LEFT);
|
||||
} else {
|
||||
|
@ -1130,6 +1239,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
box.width += 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)) {
|
||||
render_rounded_rect(ctx, &box, color, corner_radius, TOP_RIGHT);
|
||||
} else {
|
||||
|
@ -1141,7 +1251,7 @@ static void render_titlebar(struct fx_render_context *ctx, struct sway_container
|
|||
* 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,
|
||||
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;
|
||||
if (!state->border_top) {
|
||||
return;
|
||||
|
@ -1157,6 +1267,7 @@ static void render_top_border(struct fx_render_context *ctx, struct sway_contain
|
|||
box.y = floor(state->y);
|
||||
box.width = state->width - (2 * corner_radius);
|
||||
box.height = state->border_thickness;
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||
scale_box(&box, output_scale);
|
||||
render_rect(ctx, &box, color);
|
||||
|
||||
|
@ -1167,6 +1278,7 @@ static void render_top_border(struct fx_render_context *ctx, struct sway_contain
|
|||
if (state->border_left) {
|
||||
box.x = floor(state->x);
|
||||
box.y = floor(state->y);
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||
box.width = size;
|
||||
box.height = size;
|
||||
scale_box(&box, output_scale);
|
||||
|
@ -1176,6 +1288,7 @@ static void render_top_border(struct fx_render_context *ctx, struct sway_contain
|
|||
if (state->border_right) {
|
||||
box.x = floor(state->x + state->width - size);
|
||||
box.y = floor(state->y);
|
||||
adjust_box_to_workspace_offset(&box, on_focused_workspace, ctx->output);
|
||||
box.width = size;
|
||||
box.height = size;
|
||||
scale_box(&box, output_scale);
|
||||
|
@ -1191,10 +1304,13 @@ struct parent_data {
|
|||
list_t *children;
|
||||
bool focused;
|
||||
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,
|
||||
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
|
||||
/**
|
||||
|
@ -1254,17 +1370,18 @@ static void render_containers_linear(struct fx_render_context *ctx, struct paren
|
|||
.discard_transparent = false,
|
||||
.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) {
|
||||
render_titlebar(ctx, child, floor(state->x), floor(state->y),
|
||||
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) {
|
||||
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 {
|
||||
render_container(ctx, child,
|
||||
parent->focused || child->current.focused);
|
||||
parent->focused || child->current.focused,
|
||||
parent->on_focused_workspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1359,7 +1476,8 @@ static void render_containers_tabbed(struct fx_render_context *ctx, struct paren
|
|||
}
|
||||
|
||||
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) {
|
||||
current_colors = colors;
|
||||
|
@ -1368,10 +1486,12 @@ static void render_containers_tabbed(struct fx_render_context *ctx, struct paren
|
|||
|
||||
// Render surface and left/right/bottom borders
|
||||
if (current->view) {
|
||||
render_view(ctx, current, current_colors, deco_data);
|
||||
render_view(ctx, current, current_colors, deco_data,
|
||||
parent->on_focused_workspace);
|
||||
} else {
|
||||
render_container(ctx, current,
|
||||
parent->focused || current->current.focused);
|
||||
parent->focused || current->current.focused,
|
||||
parent->on_focused_workspace);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1438,7 +1558,7 @@ static void render_containers_stacked(struct fx_render_context *ctx, struct pare
|
|||
int y = parent->box.y + titlebar_height * i;
|
||||
int corner_radius = i != 0 ? 0 : deco_data.corner_radius;
|
||||
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) {
|
||||
current_colors = colors;
|
||||
|
@ -1447,10 +1567,12 @@ static void render_containers_stacked(struct fx_render_context *ctx, struct pare
|
|||
|
||||
// Render surface and left/right/bottom borders
|
||||
if (current->view) {
|
||||
render_view(ctx, current, current_colors, deco_data);
|
||||
render_view(ctx, current, current_colors, deco_data,
|
||||
parent->on_focused_workspace);
|
||||
} else {
|
||||
render_container(ctx, current,
|
||||
parent->focused || current->current.focused);
|
||||
parent->focused || current->current.focused,
|
||||
parent->on_focused_workspace);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1479,7 +1601,7 @@ static void render_containers(struct fx_render_context *ctx, struct parent_data
|
|||
}
|
||||
|
||||
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 = {
|
||||
.layout = con->current.layout,
|
||||
.box = {
|
||||
|
@ -1491,12 +1613,13 @@ static void render_container(struct fx_render_context *ctx,
|
|||
.children = con->current.children,
|
||||
.focused = focused,
|
||||
.active_child = con->current.focused_inactive_child,
|
||||
.on_focused_workspace = is_focused_ws,
|
||||
};
|
||||
render_containers(ctx, &data);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.layout = ws->current.layout,
|
||||
.box = {
|
||||
|
@ -1508,12 +1631,13 @@ static void render_workspace(struct fx_render_context *ctx,
|
|||
.children = ws->current.tiling,
|
||||
.focused = focused,
|
||||
.active_child = ws->current.focused_inactive_child,
|
||||
.on_focused_workspace = on_focused_workspace,
|
||||
};
|
||||
render_containers(ctx, &data);
|
||||
}
|
||||
|
||||
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;
|
||||
if (con->view) {
|
||||
struct sway_view *view = con->view;
|
||||
|
@ -1549,32 +1673,44 @@ static void render_floating_container(struct fx_render_context *ctx,
|
|||
.discard_transparent = false,
|
||||
.shadow = con->shadow_enabled,
|
||||
};
|
||||
render_view(ctx, con, colors, deco_data);
|
||||
render_view(ctx, con, colors, deco_data, is_focused_ws);
|
||||
if (has_titlebar) {
|
||||
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) {
|
||||
render_top_border(ctx, con, colors, deco_data.corner_radius);
|
||||
render_top_border(ctx, con, colors, deco_data.corner_radius, is_focused_ws);
|
||||
}
|
||||
} 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) {
|
||||
struct sway_output *output = root->outputs->items[i];
|
||||
|
||||
// Don't render floating windows across outputs when switching workspaces
|
||||
if (output->workspace_scroll.percent != 0 && output != ctx->output) {
|
||||
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];
|
||||
if (!workspace_is_visible(ws)) {
|
||||
|
||||
// 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) {
|
||||
struct sway_container *floater = ws->current.floating->items[k];
|
||||
if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
|
||||
continue;
|
||||
}
|
||||
render_floating_container(ctx, floater);
|
||||
render_floating_container(ctx, floater, ws == visible_ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1587,6 +1723,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) {
|
||||
struct wlr_output *wlr_output = ctx->output->wlr_output;
|
||||
struct sway_output *output = ctx->output;
|
||||
|
@ -1599,10 +1786,29 @@ void output_render(struct fx_render_context *ctx) {
|
|||
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;
|
||||
if (!fullscreen_con) {
|
||||
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)) {
|
||||
// Output isn't damaged but needs buffer swap
|
||||
|
@ -1643,6 +1849,7 @@ void output_render(struct fx_render_context *ctx) {
|
|||
if (server.session_lock.lock != NULL) {
|
||||
struct render_data data = {
|
||||
.deco_data = get_undecorated_decoration_data(),
|
||||
.on_focused_workspace = true,
|
||||
.ctx = ctx,
|
||||
};
|
||||
|
||||
|
@ -1666,37 +1873,9 @@ void output_render(struct fx_render_context *ctx) {
|
|||
goto render_overlay;
|
||||
}
|
||||
|
||||
if (fullscreen_con) {
|
||||
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);
|
||||
}
|
||||
} 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
|
||||
if (fullscreen_con && output->workspace_scroll.percent == 0) {
|
||||
// Only draw fullscreen con if not transitioning between workspaces
|
||||
render_fullscreen_con(ctx, &transformed_damage, fullscreen_con, workspace, true);
|
||||
} else {
|
||||
int output_width, output_height;
|
||||
wlr_output_transformed_resolution(wlr_output, &output_width, &output_height);
|
||||
|
@ -1779,8 +1958,16 @@ void output_render(struct fx_render_context *ctx) {
|
|||
fx_render_pass_add_optimized_blur(ctx->pass, &blur_options);
|
||||
}
|
||||
|
||||
render_workspace(ctx, workspace, workspace->current.focused);
|
||||
render_floating(ctx);
|
||||
// Render both workspaces
|
||||
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
|
||||
render_unmanaged(ctx, &root->xwayland_unmanaged);
|
||||
#endif
|
||||
|
@ -1793,6 +1980,43 @@ void output_render(struct fx_render_context *ctx) {
|
|||
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
|
||||
render_layer_popups(ctx,
|
||||
&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);
|
||||
|
|
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)) {
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
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 {
|
||||
// ... otherwise forward to client
|
||||
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)) {
|
||||
gesture_tracker_update(&seatop->gestures,
|
||||
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 {
|
||||
// ... otherwise forward to client
|
||||
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) {
|
||||
// Ensure gesture is being tracked and was not cancelled
|
||||
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;
|
||||
wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,
|
||||
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -1101,7 +1136,15 @@ static void handle_swipe_end(struct sway_seat *seat,
|
|||
&seatop->gestures, device);
|
||||
|
||||
if (binding) {
|
||||
gesture_binding_execute(seat, 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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1117,6 +1160,23 @@ static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) {
|
|||
e->previous_node = node_at_coords(seat,
|
||||
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 (seat_is_input_allowed(seat, surface)) {
|
||||
wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy);
|
||||
|
|
|
@ -24,6 +24,8 @@ sway_sources = files(
|
|||
'desktop/xdg_shell.c',
|
||||
'desktop/launcher.c',
|
||||
|
||||
'fx_util/animation_utils.c',
|
||||
|
||||
'input/input-manager.c',
|
||||
'input/cursor.c',
|
||||
'input/keyboard.c',
|
||||
|
@ -122,7 +124,7 @@ sway_sources = files(
|
|||
'commands/set.c',
|
||||
'commands/shadow_blur_radius.c',
|
||||
'commands/shadow_color.c',
|
||||
'commands/shadow_offset.c',
|
||||
'commands/shadow_offset.c',
|
||||
'commands/shadow_inactive_color.c',
|
||||
'commands/shadows.c',
|
||||
'commands/shadows_on_csd.c',
|
||||
|
@ -144,6 +146,7 @@ sway_sources = files(
|
|||
'commands/unmark.c',
|
||||
'commands/urgent.c',
|
||||
'commands/workspace.c',
|
||||
'commands/workspace_gesture.c',
|
||||
'commands/workspace_layout.c',
|
||||
'commands/ws_auto_back_and_forth.c',
|
||||
'commands/xwayland.c',
|
||||
|
|
180
sway/server.c
180
sway/server.c
|
@ -63,54 +63,61 @@
|
|||
#define SWAY_LAYER_SHELL_VERSION 4
|
||||
|
||||
#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
|
||||
* more logic here. */
|
||||
|
||||
struct wlr_drm_lease_request_v1 *req = data;
|
||||
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");
|
||||
wlr_drm_lease_request_v1_reject(req);
|
||||
}
|
||||
}
|
||||
#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 (server.drm_lease_manager != NULL) {
|
||||
if (server.drm_lease_manager != NULL)
|
||||
{
|
||||
struct wlr_drm_lease_device_v1 *drm_lease_dev;
|
||||
wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) {
|
||||
if (drm_lease_dev->global == global) {
|
||||
wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link)
|
||||
{
|
||||
if (drm_lease_dev->global == global)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return
|
||||
global == server.output_manager_v1->global ||
|
||||
global == server.output_power_manager_v1->global ||
|
||||
global == server.input_method->global ||
|
||||
global == server.foreign_toplevel_manager->global ||
|
||||
global == server.data_control_manager_v1->global ||
|
||||
global == server.screencopy_manager_v1->global ||
|
||||
global == server.export_dmabuf_manager_v1->global ||
|
||||
global == server.security_context_manager_v1->global ||
|
||||
global == server.gamma_control_manager_v1->global ||
|
||||
global == server.layer_shell->global ||
|
||||
global == server.session_lock.manager->global ||
|
||||
global == server.input->inhibit->global ||
|
||||
global == server.input->keyboard_shortcuts_inhibit->global ||
|
||||
global == server.input->virtual_keyboard->global ||
|
||||
global == server.input->virtual_pointer->global;
|
||||
return global == server.output_manager_v1->global ||
|
||||
global == server.output_power_manager_v1->global ||
|
||||
global == server.input_method->global ||
|
||||
global == server.foreign_toplevel_manager->global ||
|
||||
global == server.data_control_manager_v1->global ||
|
||||
global == server.screencopy_manager_v1->global ||
|
||||
global == server.export_dmabuf_manager_v1->global ||
|
||||
global == server.security_context_manager_v1->global ||
|
||||
global == server.gamma_control_manager_v1->global ||
|
||||
global == server.layer_shell->global ||
|
||||
global == server.session_lock.manager->global ||
|
||||
global == server.input->inhibit->global ||
|
||||
global == server.input->keyboard_shortcuts_inhibit->global ||
|
||||
global == server.input->virtual_keyboard->global ||
|
||||
global == server.input->virtual_pointer->global;
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
@ -119,15 +126,17 @@ static bool filter_global(const struct wl_client *client,
|
|||
// TODO: add a way for users to configure an allow-list
|
||||
const struct wlr_security_context_v1_state *security_context =
|
||||
wlr_security_context_manager_v1_lookup_client(
|
||||
server.security_context_manager_v1, (struct wl_client *)client);
|
||||
if (is_privileged(global)) {
|
||||
server.security_context_manager_v1, (struct wl_client *)client);
|
||||
if (is_privileged(global))
|
||||
{
|
||||
return security_context == NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool server_init(struct sway_server *server) {
|
||||
bool server_init(struct sway_server *server)
|
||||
{
|
||||
sway_log(SWAY_DEBUG, "Initializing Wayland server");
|
||||
server->wl_display = wl_display_create();
|
||||
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
|
||||
|
@ -135,37 +144,41 @@ bool server_init(struct sway_server *server) {
|
|||
wl_display_set_global_filter(server->wl_display, filter_global, NULL);
|
||||
|
||||
server->backend = wlr_backend_autocreate(server->wl_display, &server->session);
|
||||
if (!server->backend) {
|
||||
if (!server->backend)
|
||||
{
|
||||
sway_log(SWAY_ERROR, "Unable to create backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
server->renderer = fx_renderer_create(server->backend);
|
||||
if (!server->renderer) {
|
||||
if (!server->renderer)
|
||||
{
|
||||
sway_log(SWAY_ERROR, "Failed to create fx_renderer");
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
|
||||
server->wl_display, 4, server->renderer);
|
||||
}
|
||||
|
||||
server->allocator = wlr_allocator_autocreate(server->backend,
|
||||
server->renderer);
|
||||
if (!server->allocator) {
|
||||
server->renderer);
|
||||
if (!server->allocator)
|
||||
{
|
||||
sway_log(SWAY_ERROR, "Failed to create allocator");
|
||||
return false;
|
||||
}
|
||||
|
||||
server->compositor = wlr_compositor_create(server->wl_display, 6,
|
||||
server->renderer);
|
||||
server->renderer);
|
||||
server->compositor_new_surface.notify = handle_compositor_new_surface;
|
||||
wl_signal_add(&server->compositor->events.new_surface,
|
||||
&server->compositor_new_surface);
|
||||
&server->compositor_new_surface);
|
||||
|
||||
wlr_subcompositor_create(server->wl_display);
|
||||
|
||||
|
@ -176,13 +189,13 @@ bool server_init(struct sway_server *server) {
|
|||
wlr_gamma_control_manager_v1_create(server->wl_display);
|
||||
server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma;
|
||||
wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma,
|
||||
&server->gamma_control_set_gamma);
|
||||
&server->gamma_control_set_gamma);
|
||||
|
||||
server->new_output.notify = handle_new_output;
|
||||
wl_signal_add(&server->backend->events.new_output, &server->new_output);
|
||||
server->output_layout_change.notify = handle_output_layout_change;
|
||||
wl_signal_add(&root->output_layout->events.change,
|
||||
&server->output_layout_change);
|
||||
&server->output_layout_change);
|
||||
|
||||
wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
|
||||
|
||||
|
@ -190,15 +203,15 @@ bool server_init(struct sway_server *server) {
|
|||
sway_idle_inhibit_manager_v1_init();
|
||||
|
||||
server->layer_shell = wlr_layer_shell_v1_create(server->wl_display,
|
||||
SWAY_LAYER_SHELL_VERSION);
|
||||
SWAY_LAYER_SHELL_VERSION);
|
||||
wl_signal_add(&server->layer_shell->events.new_surface,
|
||||
&server->layer_shell_surface);
|
||||
&server->layer_shell_surface);
|
||||
server->layer_shell_surface.notify = handle_layer_shell_surface;
|
||||
|
||||
server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
|
||||
SWAY_XDG_SHELL_VERSION);
|
||||
SWAY_XDG_SHELL_VERSION);
|
||||
wl_signal_add(&server->xdg_shell->events.new_surface,
|
||||
&server->xdg_shell_surface);
|
||||
&server->xdg_shell_surface);
|
||||
server->xdg_shell_surface.notify = handle_xdg_shell_surface;
|
||||
|
||||
server->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
|
||||
|
@ -209,15 +222,15 @@ bool server_init(struct sway_server *server) {
|
|||
server->server_decoration_manager,
|
||||
WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
||||
wl_signal_add(&server->server_decoration_manager->events.new_decoration,
|
||||
&server->server_decoration);
|
||||
&server->server_decoration);
|
||||
server->server_decoration.notify = handle_server_decoration;
|
||||
wl_list_init(&server->decorations);
|
||||
|
||||
server->xdg_decoration_manager =
|
||||
wlr_xdg_decoration_manager_v1_create(server->wl_display);
|
||||
wl_signal_add(
|
||||
&server->xdg_decoration_manager->events.new_toplevel_decoration,
|
||||
&server->xdg_decoration);
|
||||
&server->xdg_decoration_manager->events.new_toplevel_decoration,
|
||||
&server->xdg_decoration);
|
||||
server->xdg_decoration.notify = handle_xdg_decoration;
|
||||
wl_list_init(&server->xdg_decorations);
|
||||
|
||||
|
@ -228,7 +241,7 @@ bool server_init(struct sway_server *server) {
|
|||
wlr_pointer_constraints_v1_create(server->wl_display);
|
||||
server->pointer_constraint.notify = handle_pointer_constraint;
|
||||
wl_signal_add(&server->pointer_constraints->events.new_constraint,
|
||||
&server->pointer_constraint);
|
||||
&server->pointer_constraint);
|
||||
|
||||
server->presentation =
|
||||
wlr_presentation_create(server->wl_display, server->backend);
|
||||
|
@ -237,17 +250,17 @@ bool server_init(struct sway_server *server) {
|
|||
wlr_output_manager_v1_create(server->wl_display);
|
||||
server->output_manager_apply.notify = handle_output_manager_apply;
|
||||
wl_signal_add(&server->output_manager_v1->events.apply,
|
||||
&server->output_manager_apply);
|
||||
&server->output_manager_apply);
|
||||
server->output_manager_test.notify = handle_output_manager_test;
|
||||
wl_signal_add(&server->output_manager_v1->events.test,
|
||||
&server->output_manager_test);
|
||||
&server->output_manager_test);
|
||||
|
||||
server->output_power_manager_v1 =
|
||||
wlr_output_power_manager_v1_create(server->wl_display);
|
||||
server->output_power_manager_set_mode.notify =
|
||||
handle_output_power_manager_set_mode;
|
||||
wl_signal_add(&server->output_power_manager_v1->events.set_mode,
|
||||
&server->output_power_manager_set_mode);
|
||||
&server->output_power_manager_set_mode);
|
||||
server->input_method = wlr_input_method_manager_v2_create(server->wl_display);
|
||||
server->text_input = wlr_text_input_manager_v3_create(server->wl_display);
|
||||
server->foreign_toplevel_manager =
|
||||
|
@ -256,13 +269,16 @@ bool server_init(struct sway_server *server) {
|
|||
sway_session_lock_init();
|
||||
|
||||
#if WLR_HAS_DRM_BACKEND
|
||||
server->drm_lease_manager=
|
||||
server->drm_lease_manager =
|
||||
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;
|
||||
wl_signal_add(&server->drm_lease_manager->events.request,
|
||||
&server->drm_lease_request);
|
||||
} else {
|
||||
&server->drm_lease_request);
|
||||
}
|
||||
else
|
||||
{
|
||||
sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
|
||||
sway_log(SWAY_INFO, "VR will not be available");
|
||||
}
|
||||
|
@ -287,11 +303,11 @@ bool server_init(struct sway_server *server) {
|
|||
server->xdg_activation_v1_request_activate.notify =
|
||||
xdg_activation_v1_handle_request_activate;
|
||||
wl_signal_add(&server->xdg_activation_v1->events.request_activate,
|
||||
&server->xdg_activation_v1_request_activate);
|
||||
&server->xdg_activation_v1_request_activate);
|
||||
server->xdg_activation_v1_new_token.notify =
|
||||
xdg_activation_v1_handle_new_token;
|
||||
wl_signal_add(&server->xdg_activation_v1->events.new_token,
|
||||
&server->xdg_activation_v1_new_token);
|
||||
&server->xdg_activation_v1_new_token);
|
||||
|
||||
struct wlr_cursor_shape_manager_v1 *cursor_shape_manager =
|
||||
wlr_cursor_shape_manager_v1_create(server->wl_display, 1);
|
||||
|
@ -302,36 +318,43 @@ bool server_init(struct sway_server *server) {
|
|||
|
||||
// Avoid using "wayland-0" as display socket
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!server->socket) {
|
||||
if (!server->socket)
|
||||
{
|
||||
sway_log(SWAY_ERROR, "Unable to open wayland socket");
|
||||
wlr_backend_destroy(server->backend);
|
||||
return false;
|
||||
}
|
||||
|
||||
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");
|
||||
wlr_backend_destroy(server->backend);
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
wlr_multi_backend_add(server->backend, server->headless_backend);
|
||||
}
|
||||
|
||||
struct wlr_output *wlr_output =
|
||||
wlr_headless_add_output(server->headless_backend, 800, 600);
|
||||
wlr_headless_add_output(server->headless_backend, 800, 600);
|
||||
wlr_output_set_name(wlr_output, "FALLBACK");
|
||||
root->fallback_output = output_create(wlr_output);
|
||||
|
||||
// This may have been set already via -Dtxn-timeout
|
||||
if (!server->txn_timeout_ms) {
|
||||
if (!server->txn_timeout_ms)
|
||||
{
|
||||
server->txn_timeout_ms = 200;
|
||||
}
|
||||
|
||||
|
@ -343,7 +366,8 @@ bool server_init(struct sway_server *server) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void server_fini(struct sway_server *server) {
|
||||
void server_fini(struct sway_server *server)
|
||||
{
|
||||
// TODO: free sway-specific resources
|
||||
#if HAVE_XWAYLAND
|
||||
wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
|
||||
|
@ -353,23 +377,28 @@ void server_fini(struct sway_server *server) {
|
|||
list_free(server->dirty_nodes);
|
||||
}
|
||||
|
||||
bool server_start(struct sway_server *server) {
|
||||
bool server_start(struct sway_server *server)
|
||||
{
|
||||
#if HAVE_XWAYLAND
|
||||
if (config->xwayland != XWAYLAND_MODE_DISABLED) {
|
||||
if (config->xwayland != XWAYLAND_MODE_DISABLED)
|
||||
{
|
||||
sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)",
|
||||
config->xwayland == XWAYLAND_MODE_LAZY);
|
||||
config->xwayland == XWAYLAND_MODE_LAZY);
|
||||
server->xwayland.wlr_xwayland =
|
||||
wlr_xwayland_create(server->wl_display, server->compositor,
|
||||
config->xwayland == XWAYLAND_MODE_LAZY);
|
||||
if (!server->xwayland.wlr_xwayland) {
|
||||
config->xwayland == XWAYLAND_MODE_LAZY);
|
||||
if (!server->xwayland.wlr_xwayland)
|
||||
{
|
||||
sway_log(SWAY_ERROR, "Failed to start Xwayland");
|
||||
unsetenv("DISPLAY");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface,
|
||||
&server->xwayland_surface);
|
||||
&server->xwayland_surface);
|
||||
server->xwayland_surface.notify = handle_xwayland_surface;
|
||||
wl_signal_add(&server->xwayland.wlr_xwayland->events.ready,
|
||||
&server->xwayland_ready);
|
||||
&server->xwayland_ready);
|
||||
server->xwayland_ready.notify = handle_xwayland_ready;
|
||||
|
||||
setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true);
|
||||
|
@ -379,13 +408,15 @@ bool server_start(struct sway_server *server) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (config->primary_selection) {
|
||||
if (config->primary_selection)
|
||||
{
|
||||
wlr_primary_selection_v1_device_manager_create(server->wl_display);
|
||||
}
|
||||
|
||||
sway_log(SWAY_INFO, "Starting backend on wayland display '%s'",
|
||||
server->socket);
|
||||
if (!wlr_backend_start(server->backend)) {
|
||||
server->socket);
|
||||
if (!wlr_backend_start(server->backend))
|
||||
{
|
||||
sway_log(SWAY_ERROR, "Failed to start backend");
|
||||
wlr_backend_destroy(server->backend);
|
||||
return false;
|
||||
|
@ -394,8 +425,9 @@ bool server_start(struct sway_server *server) {
|
|||
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'",
|
||||
server->socket);
|
||||
server->socket);
|
||||
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>
|
||||
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;
|
||||
|
||||
int gaps = 0;
|
||||
if (container->current.workspace) {
|
||||
gaps = container->current.workspace->gaps_inner;
|
||||
}
|
||||
|
||||
struct wlr_box box = {
|
||||
.x = container->pending.x,
|
||||
.y = container->pending.y,
|
||||
.width = container->pending.width,
|
||||
.height = container->pending.height,
|
||||
.x = container->pending.x - (gaps / 2),
|
||||
.y = container->pending.y - (gaps / 2),
|
||||
.width = container->pending.width + gaps,
|
||||
.height = container->pending.height + gaps,
|
||||
};
|
||||
|
||||
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) {
|
||||
return workspace_prev(current);
|
||||
} 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) {
|
||||
return workspace_next(current);
|
||||
} 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) {
|
||||
return current;
|
||||
} 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.
|
||||
*/
|
||||
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_workspace *workspace = seat_get_focused_workspace(seat);
|
||||
if (!workspace) {
|
||||
|
@ -531,17 +531,27 @@ static struct sway_workspace *workspace_output_prev_next_impl(
|
|||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
|
||||
return workspace_output_prev_next_impl(current->output, 1);
|
||||
struct sway_workspace *workspace_output_next(struct sway_workspace *current,
|
||||
bool should_wrap) {
|
||||
return workspace_output_prev_next_impl(current->output, 1, should_wrap);
|
||||
}
|
||||
|
||||
struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
|
||||
return workspace_output_prev_next_impl(current->output, -1);
|
||||
struct sway_workspace *workspace_output_prev(struct sway_workspace *current,
|
||||
bool should_wrap) {
|
||||
return workspace_output_prev_next_impl(current->output, -1, should_wrap);
|
||||
}
|
||||
|
||||
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",
|
||||
workspace, workspace->name);
|
||||
workspace->output->workspace_scroll = workspace_scroll_get_default();
|
||||
struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node);
|
||||
if (next == NULL) {
|
||||
next = &workspace->node;
|
||||
|
|
Loading…
Reference in a new issue