Skip to content

Commit

Permalink
Gestures: Add more details and introduce on_gesture_handled (#2168)
Browse files Browse the repository at this point in the history
Co-authored-by: Leo <[email protected]>
  • Loading branch information
leolost2605 and lenemter authored Dec 19, 2024
1 parent 6f0093a commit 0e82d63
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 91 deletions.
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

0 comments on commit 0e82d63

Please sign in to comment.