Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gestures: Add more details and introduce on_gesture_handled #2168

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Gestures/Gesture.vala
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,25 @@ namespace Gala {
}

public class Gesture {
public const float INVALID_COORD = float.MAX;

public Clutter.EventType type;
public GestureDirection direction;
public int fingers;
public Clutter.InputDeviceType performed_on_device_type;

/**
* The x coordinate of the initial contact point for the gesture.
* Doesn't have to be set. In that case it is set to {@link INVALID_COORD}.
* Currently the only backend not setting this is {@link GestureTracker.enable_touchpad}.
*/
public float origin_x = INVALID_COORD;

/**
* The y coordinate of the initial contact point for the gesture.
* Doesn't have to be set. In that case it is set to {@link INVALID_COORD}.
* Currently the only backend not setting this is {@link GestureTracker.enable_touchpad}.
*/
public float origin_y = INVALID_COORD;
}
}
45 changes: 45 additions & 0 deletions src/Gestures/GestureSettings.vala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
* Utility class to access the gesture settings. Easily accessible through GestureTracker.settings.
*/
public class Gala.GestureSettings : Object {
public enum GestureAction {
NONE,
SWITCH_WORKSPACE,
MOVE_TO_WORKSPACE,
SWITCH_WINDOWS,
MULTITASKING_VIEW
}

private static GLib.Settings gala_settings;
private static GLib.Settings touchpad_settings;

Expand Down Expand Up @@ -69,4 +77,41 @@ public class Gala.GestureSettings : Object {
public static string get_string (string setting_id) {
return gala_settings.get_string (setting_id);
}

public static GestureAction get_action (Gesture gesture) {
if (gesture.type == TOUCHPAD_SWIPE) {
var fingers = gesture.fingers;

if (gesture.direction == LEFT || gesture.direction == RIGHT) {
var three_finger_swipe_horizontal = get_string ("three-finger-swipe-horizontal");
var four_finger_swipe_horizontal = get_string ("four-finger-swipe-horizontal");

if (fingers == 3 && three_finger_swipe_horizontal == "switch-to-workspace" ||
fingers == 4 && four_finger_swipe_horizontal == "switch-to-workspace") {
return SWITCH_WORKSPACE;
}

if (fingers == 3 && three_finger_swipe_horizontal == "move-to-workspace" ||
fingers == 4 && four_finger_swipe_horizontal == "move-to-workspace") {
return MOVE_TO_WORKSPACE;
}


if (fingers == 3 && three_finger_swipe_horizontal == "switch-windows" ||
fingers == 4 && four_finger_swipe_horizontal == "switch-windows") {
return SWITCH_WINDOWS;
}
} else if (gesture.direction == UP || gesture.direction == DOWN) {
var three_finger_swipe_up = get_string ("three-finger-swipe-up");
var four_finger_swipe_up = get_string ("four-finger-swipe-up");

if (fingers == 3 && three_finger_swipe_up == "multitasking-view" ||
fingers == 4 && four_finger_swipe_up == "multitasking-view") {
return MULTITASKING_VIEW;
}
}
}

return NONE;
}
}
38 changes: 32 additions & 6 deletions src/Gestures/GestureTracker.vala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

public interface Gala.GestureBackend : Object {
public signal bool on_gesture_detected (Gesture gesture, uint32 timestamp);
public signal void on_begin (double delta, uint64 time);
public signal void on_update (double delta, uint64 time);
public signal void on_end (double delta, uint64 time);

public virtual void prepare_gesture_handling () { }
}

/**
* Allow to use multi-touch gestures from different sources (backends).
* Usage:
Expand Down Expand Up @@ -68,11 +77,24 @@ public class Gala.GestureTracker : Object {

/**
* Emitted when a new gesture is detected.
* If the receiving code needs to handle this gesture, it should call to connect_handlers to
* start receiving updates.
* This should only be used to determine whether the gesture should be handled. This shouldn't
* do any preparations instead those should be done in {@link on_gesture_handled}. This is because
* the backend might have to do some preparations itself before you are allowed to do some to avoid
* conflicts.
* @param gesture Information about the gesture.
* @return true if the gesture will be handled false otherwise. If false is returned the other
* signals may still be emitted but aren't guaranteed to be.
*/
public signal void on_gesture_detected (Gesture gesture);
public signal bool on_gesture_detected (Gesture gesture);

/**
* Emitted if true was returned form {@link on_gesture_detected}. This should
* be used to do any preparations for gesture handling and to call {@link connect_handlers} to
* start receiving updates.
* @param gesture the same gesture as in {@link on_gesture_detected}
* @param timestamp the timestamp of the event that initiated the gesture or {@link Meta.CURRENT_TIME}.
*/
public signal void on_gesture_handled (Gesture gesture, uint32 timestamp);

/**
* Emitted right after on_gesture_detected with the initial gesture information.
Expand Down Expand Up @@ -205,10 +227,14 @@ public class Gala.GestureTracker : Object {
return value;
}

private void gesture_detected (Gesture gesture) {
if (enabled) {
on_gesture_detected (gesture);
private bool gesture_detected (GestureBackend backend, Gesture gesture, uint32 timestamp) {
if (enabled && on_gesture_detected (gesture)) {
backend.prepare_gesture_handling ();
on_gesture_handled (gesture, timestamp);
return true;
}

return false;
}

private void gesture_begin (double percentage, uint64 elapsed_time) {
Expand Down
21 changes: 10 additions & 11 deletions src/Gestures/ScrollBackend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,13 @@
/**
* This gesture backend transforms the touchpad scroll events received by an actor into gestures.
*/
public class Gala.ScrollBackend : Object {
public class Gala.ScrollBackend : Object, GestureBackend {
// Mutter does not expose the size of the touchpad, so we use the same values as GTK apps.
// From GNOME Shell, TOUCHPAD_BASE_[WIDTH|HEIGHT] / SCROLL_MULTIPLIER
// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/swipeTracker.js
private const double FINISH_DELTA_HORIZONTAL = 40;
private const double FINISH_DELTA_VERTICAL = 30;

public signal void on_gesture_detected (Gesture gesture);
public signal void on_begin (double delta, uint64 time);
public signal void on_update (double delta, uint64 time);
public signal void on_end (double delta, uint64 time);

public Clutter.Actor actor { get; construct; }
public Clutter.Orientation orientation { get; construct; }
public GestureSettings settings { get; construct; }
Expand Down Expand Up @@ -62,7 +57,7 @@ public class Gala.ScrollBackend : Object {
return false;
}

uint64 time = event.get_time ();
var time = event.get_time ();
double x, y;
event.get_scroll_delta (out x, out y);

Expand All @@ -80,10 +75,12 @@ public class Gala.ScrollBackend : Object {

if (!started) {
if (delta_x != 0 || delta_y != 0) {
Gesture gesture = build_gesture (delta_x, delta_y, orientation);
float origin_x, origin_y;
event.get_coords (out origin_x, out origin_y);
Gesture gesture = build_gesture (origin_x, origin_y, delta_x, delta_y, orientation, time);
started = true;
direction = gesture.direction;
on_gesture_detected (gesture);
on_gesture_detected (gesture, time);

double delta = calculate_delta (delta_x, delta_y, direction);
on_begin (delta, time);
Expand Down Expand Up @@ -114,7 +111,7 @@ public class Gala.ScrollBackend : Object {
&& event.get_scroll_direction () == Clutter.ScrollDirection.SMOOTH;
}

private static Gesture build_gesture (double delta_x, double delta_y, Clutter.Orientation orientation) {
private static Gesture build_gesture (float origin_x, float origin_y, double delta_x, double delta_y, Clutter.Orientation orientation, uint32 timestamp) {
GestureDirection direction;
if (orientation == Clutter.Orientation.HORIZONTAL) {
direction = delta_x > 0 ? GestureDirection.RIGHT : GestureDirection.LEFT;
Expand All @@ -126,7 +123,9 @@ public class Gala.ScrollBackend : Object {
type = Clutter.EventType.SCROLL,
direction = direction,
fingers = 2,
performed_on_device_type = Clutter.InputDeviceType.TOUCHPAD_DEVICE
performed_on_device_type = Clutter.InputDeviceType.TOUCHPAD_DEVICE,
origin_x = origin_x,
origin_y = origin_y
};
}

Expand Down
9 changes: 2 additions & 7 deletions src/Gestures/ToucheggBackend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@
* Singleton class to manage the connection with Touchégg daemon and receive touch events.
* See: [[https://github.com/JoseExposito/touchegg]]
*/
public class Gala.ToucheggBackend : Object {
public signal void on_gesture_detected (Gesture gesture);
public signal void on_begin (double delta, uint64 time);
public signal void on_update (double delta, uint64 time);
public signal void on_end (double delta, uint64 time);

public class Gala.ToucheggBackend : Object, GestureBackend {
/**
* Gesture type as returned by the daemon.
*/
Expand Down Expand Up @@ -202,7 +197,7 @@ public class Gala.ToucheggBackend : Object {
switch (signal_name) {
case DBUS_ON_GESTURE_BEGIN:
Idle.add (() => {
on_gesture_detected (make_gesture (type, direction, fingers, performed_on_device_type));
on_gesture_detected (make_gesture (type, direction, fingers, performed_on_device_type), Meta.CURRENT_TIME);
on_begin (delta, elapsed_time);
return false;
});
Expand Down
40 changes: 18 additions & 22 deletions src/Widgets/MultitaskingView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ namespace Gala {
multitasking_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION);
multitasking_gesture_tracker.enable_touchpad ();
multitasking_gesture_tracker.on_gesture_detected.connect (on_multitasking_gesture_detected);
multitasking_gesture_tracker.on_gesture_handled.connect (() => toggle (true, false));

workspace_gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH);
workspace_gesture_tracker.enable_touchpad ();
workspace_gesture_tracker.enable_scroll (this, Clutter.Orientation.HORIZONTAL);
workspace_gesture_tracker.on_gesture_detected.connect (on_workspace_gesture_detected);
workspace_gesture_tracker.on_gesture_handled.connect (switch_workspace_with_gesture);

workspaces = new Clutter.Actor ();

Expand Down Expand Up @@ -290,43 +292,37 @@ namespace Gala {
workspaces.add_transition ("nudge", nudge);
}

private void on_multitasking_gesture_detected (Gesture gesture) {
if (gesture.type != Clutter.EventType.TOUCHPAD_SWIPE ||
(gesture.fingers == 3 && GestureSettings.get_string ("three-finger-swipe-up") != "multitasking-view") ||
(gesture.fingers == 4 && GestureSettings.get_string ("four-finger-swipe-up") != "multitasking-view")
) {
return;
private bool on_multitasking_gesture_detected (Gesture gesture) {
if (GestureSettings.get_action (gesture) != MULTITASKING_VIEW) {
return false;
}

if (gesture.direction == GestureDirection.UP && !opened) {
toggle (true, false);
} else if (gesture.direction == GestureDirection.DOWN && opened) {
toggle (true, false);
if (gesture.direction == UP && !opened || gesture.direction == DOWN && opened) {
return true;
}

return false;
}

private void on_workspace_gesture_detected (Gesture gesture) {
private bool on_workspace_gesture_detected (Gesture gesture) {
if (!opened) {
return;
return false;
}

var can_handle_swipe = gesture.type == Clutter.EventType.TOUCHPAD_SWIPE &&
(gesture.direction == GestureDirection.LEFT || gesture.direction == GestureDirection.RIGHT);

var fingers = (gesture.fingers == 3 && Gala.GestureSettings.get_string ("three-finger-swipe-horizontal") == "switch-to-workspace") ||
(gesture.fingers == 4 && Gala.GestureSettings.get_string ("four-finger-swipe-horizontal") == "switch-to-workspace");

if (gesture.type == Clutter.EventType.SCROLL || (can_handle_swipe && fingers)) {
var direction = workspace_gesture_tracker.settings.get_natural_scroll_direction (gesture);
switch_workspace_with_gesture (direction);
if (gesture.type == SCROLL || GestureSettings.get_action (gesture) == SWITCH_WORKSPACE) {
return true;
}

return false;
}

private void switch_workspace_with_gesture (Meta.MotionDirection direction) {
private void switch_workspace_with_gesture (Gesture gesture, uint32 timestamp) {
if (switching_workspace_in_progress) {
return;
}

var direction = workspace_gesture_tracker.settings.get_natural_scroll_direction (gesture);

unowned var manager = display.get_workspace_manager ();
var num_workspaces = manager.get_n_workspaces ();
var relative_dir = (direction == Meta.MotionDirection.LEFT) ? -1 : 1;
Expand Down
69 changes: 27 additions & 42 deletions src/WindowManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ namespace Gala {
gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH);
gesture_tracker.enable_touchpad ();
gesture_tracker.on_gesture_detected.connect (on_gesture_detected);
gesture_tracker.on_gesture_handled.connect (on_gesture_handled);

info = Meta.PluginInfo () {name = "Gala", version = Config.VERSION, author = "Gala Developers",
license = "GPLv3", description = "A nice elementary window manager"};
Expand Down Expand Up @@ -553,57 +554,41 @@ namespace Gala {
}
}

private void on_gesture_detected (Gesture gesture) {
private bool on_gesture_detected (Gesture gesture) {
if (workspace_view.is_opened ()) {
return;
}

if (gesture.type != Clutter.EventType.TOUCHPAD_SWIPE ||
(gesture.direction != GestureDirection.LEFT && gesture.direction != GestureDirection.RIGHT)) {
return;
return false;
}

unowned var display = get_display ();

var fingers = gesture.fingers;

var three_finger_swipe_horizontal = GestureSettings.get_string ("three-finger-swipe-horizontal");
var four_finger_swipe_horizontal = GestureSettings.get_string ("four-finger-swipe-horizontal");

var three_fingers_switch_to_workspace = fingers == 3 && three_finger_swipe_horizontal == "switch-to-workspace";
var four_fingers_switch_to_workspace = fingers == 4 && four_finger_swipe_horizontal == "switch-to-workspace";

var three_fingers_move_to_workspace = fingers == 3 && three_finger_swipe_horizontal == "move-to-workspace";
var four_fingers_move_to_workspace = fingers == 4 && four_finger_swipe_horizontal == "move-to-workspace";

var three_fingers_switch_windows = fingers == 3 && three_finger_swipe_horizontal == "switch-windows";
var four_fingers_switch_windows = fingers == 4 && four_finger_swipe_horizontal == "switch-windows";
var action = GestureSettings.get_action (gesture);
switch_workspace_with_gesture = action == SWITCH_WORKSPACE || action == MOVE_TO_WORKSPACE;
return switch_workspace_with_gesture || (action == SWITCH_WINDOWS && !window_switcher.opened);
}

switch_workspace_with_gesture = three_fingers_switch_to_workspace || four_fingers_switch_to_workspace;
if (switch_workspace_with_gesture) {
var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
switch_to_next_workspace (direction, display.get_current_time ());
return;
}
private void on_gesture_handled (Gesture gesture, uint32 timestamp) {
var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);

switch_workspace_with_gesture = three_fingers_move_to_workspace || four_fingers_move_to_workspace;
if (switch_workspace_with_gesture) {
unowned var manager = display.get_workspace_manager ();
switch (GestureSettings.get_action (gesture)) {
case MOVE_TO_WORKSPACE:
unowned var display = get_display ();
unowned var manager = display.get_workspace_manager ();

var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
moving = display.focus_window;
if (moving != null) {
moving.change_workspace (manager.get_active_workspace ().get_neighbor (direction));
}
switch_to_next_workspace (direction, timestamp);
break;

moving = display.focus_window;
if (moving != null) {
moving.change_workspace (manager.get_active_workspace ().get_neighbor (direction));
}
case SWITCH_WORKSPACE:
switch_to_next_workspace (direction, timestamp);
break;

switch_to_next_workspace (direction, display.get_current_time ());
return;
}
case SWITCH_WINDOWS:
window_switcher.handle_gesture (gesture.direction);
break;

var switch_windows = three_fingers_switch_windows || four_fingers_switch_windows;
if (switch_windows && !window_switcher.opened) {
window_switcher.handle_gesture (gesture.direction);
default:
break;
}
}

Expand Down
Loading
Loading