diff --git a/src/Gestures/Gesture.vala b/src/Gestures/Gesture.vala index 9bccedaf7..5ffd2831d 100644 --- a/src/Gestures/Gesture.vala +++ b/src/Gestures/Gesture.vala @@ -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; } } diff --git a/src/Gestures/GestureSettings.vala b/src/Gestures/GestureSettings.vala index 894bc0f10..4dd6da0bb 100644 --- a/src/Gestures/GestureSettings.vala +++ b/src/Gestures/GestureSettings.vala @@ -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; @@ -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; + } } diff --git a/src/Gestures/GestureTracker.vala b/src/Gestures/GestureTracker.vala index e56e9ee66..9ca525bb9 100644 --- a/src/Gestures/GestureTracker.vala +++ b/src/Gestures/GestureTracker.vala @@ -16,6 +16,15 @@ * along with this program. If not, see . */ +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: @@ -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. @@ -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) { diff --git a/src/Gestures/ScrollBackend.vala b/src/Gestures/ScrollBackend.vala index 1d4f42546..d833264cb 100644 --- a/src/Gestures/ScrollBackend.vala +++ b/src/Gestures/ScrollBackend.vala @@ -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; } @@ -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); @@ -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); @@ -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; @@ -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 }; } diff --git a/src/Gestures/ToucheggBackend.vala b/src/Gestures/ToucheggBackend.vala index d4eeb224b..df3f076d1 100644 --- a/src/Gestures/ToucheggBackend.vala +++ b/src/Gestures/ToucheggBackend.vala @@ -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. */ @@ -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; }); diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index d78db4478..6c00b783f 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -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 (); @@ -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; diff --git a/src/WindowManager.vala b/src/WindowManager.vala index b00c77c6c..5606d0488 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -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"}; @@ -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; } } diff --git a/src/Zoom.vala b/src/Zoom.vala index 29d471d47..9165bd1f7 100644 --- a/src/Zoom.vala +++ b/src/Zoom.vala @@ -32,6 +32,7 @@ public class Gala.Zoom : Object { gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION); gesture_tracker.enable_touchpad (); gesture_tracker.on_gesture_detected.connect (on_gesture_detected); + gesture_tracker.on_gesture_handled.connect ((gesture) => zoom_with_gesture (gesture.direction)); } ~Zoom () { @@ -61,18 +62,20 @@ public class Gala.Zoom : Object { zoom (-SHORTCUT_DELTA, true, AnimationsSettings.get_enable_animations ()); } - private void on_gesture_detected (Gesture gesture) { + private bool on_gesture_detected (Gesture gesture) { if (gesture.type != Clutter.EventType.TOUCHPAD_PINCH || (gesture.direction != GestureDirection.IN && gesture.direction != GestureDirection.OUT) ) { - return; + return false; } if ((gesture.fingers == 3 && GestureSettings.get_string ("three-finger-pinch") == "zoom") || (gesture.fingers == 4 && GestureSettings.get_string ("four-finger-pinch") == "zoom") ) { - zoom_with_gesture (gesture.direction); + return true; } + + return false; } private void zoom_with_gesture (GestureDirection direction) {