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 aba66f648..2f0c92a4e 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 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_detected (Gesture gesture); + public signal void on_gesture_handled (Gesture gesture, uint32 timestamp); /** * Emitted right after on_gesture_detected with the initial gesture information. @@ -98,7 +120,12 @@ public class Gala.GestureTracker : Object { /** * Backend used if enable_touchpad is called. */ - private ToucheggBackend touchpad_backend; + private ToucheggBackend? touchpad_backend; + + /** + * Pan backend used if enable_pan is called. + */ + private PanBackend pan_backend; /** * Scroll backend used if enable_scroll is called. @@ -137,6 +164,22 @@ public class Gala.GestureTracker : Object { touchpad_backend.on_end.connect (gesture_end); } + /** + * Allow to receive pan gestures. + * @param actor Clutter actor that will receive the events. + * @param travel_distance_func this will be called if a gesture is detected and true is returned from {@link on_gesture_detected}. + * The returned distance wil be used to calculate the percentage. It should be set to the amount something will travel (e.g. + * when moving an actor) based on the gesture to allow exact finger tracking. It can also be used + * to calculate the raw pixels the finger travelled at a given time with percentage * distance. + */ + public void enable_pan (WindowManager wm, Clutter.Actor actor, owned PanBackend.GetTravelDistance travel_distance_func) { + pan_backend = new PanBackend (wm, actor, (owned) travel_distance_func); + pan_backend.on_gesture_detected.connect (gesture_detected); + pan_backend.on_begin.connect (gesture_begin); + pan_backend.on_update.connect (gesture_update); + pan_backend.on_end.connect (gesture_end); + } + /** * Allow to receive scroll gestures. * @param actor Clutter actor that will receive the scroll events. @@ -201,10 +244,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/PanBackend.vala b/src/Gestures/PanBackend.vala new file mode 100644 index 000000000..60c3a3a3d --- /dev/null +++ b/src/Gestures/PanBackend.vala @@ -0,0 +1,161 @@ +/* + * Copyright 2024 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class Gala.PanBackend : Object, GestureBackend { + public delegate float GetTravelDistance (); + + public WindowManager wm { get; construct; } + public Clutter.Actor actor { get; construct; } + + private GetTravelDistance get_travel_distance_func; + + private ModalProxy? modal_proxy; + + private Clutter.PanAxis pan_axis; + private Clutter.PanAction pan_action; + + private GestureDirection direction; + + private float origin_x; + private float origin_y; + + private float current_x; + private float current_y; + + private float last_x_coord; + private float last_y_coord; + private uint last_n_points; + + private float travel_distance; + + public PanBackend (WindowManager wm, Clutter.Actor actor, owned GetTravelDistance get_travel_distance_func) { + Object (wm: wm, actor: actor); + + this.get_travel_distance_func = (owned) get_travel_distance_func; + } + + construct { + pan_action = new Clutter.PanAction () { + n_touch_points = 1 + }; + + actor.add_action_full ("pan-gesture", CAPTURE, pan_action); + + pan_action.gesture_begin.connect (on_gesture_begin); + pan_action.pan.connect (on_pan); + pan_action.gesture_end.connect (on_gesture_end); + pan_action.gesture_cancel.connect (on_gesture_end); + } + + ~PanBackend () { + actor.remove_action (pan_action); + } + + private bool on_gesture_begin () { + if (pan_action.get_last_event (0).get_source_device ().get_device_type () != TOUCHSCREEN_DEVICE) { + return false; + } + + float x_coord, y_coord; + pan_action.get_press_coords (0, out x_coord, out y_coord); + + origin_x = current_x = x_coord; + origin_y = current_y = y_coord; + + var time = pan_action.get_last_event (0).get_time (); + + var handled = on_gesture_detected (build_gesture (), time); + + if (!handled) { + reset (); + return false; + } + + travel_distance = get_travel_distance_func (); + + on_begin (0, time); + + return true; + } + + public override void prepare_gesture_handling () { + modal_proxy = wm.push_modal (actor); + } + + private void on_gesture_end () { + on_end (calculate_percentage (), Meta.CURRENT_TIME); + + reset (); + } + + private void reset () { + if (modal_proxy != null) { + wm.pop_modal (modal_proxy); + modal_proxy = null; + } + + direction = GestureDirection.UNKNOWN; + last_n_points = 0; + last_x_coord = 0; + last_y_coord = 0; + } + + private bool on_pan (Clutter.PanAction pan_action, Clutter.Actor actor, bool interpolate) { + var time = pan_action.get_last_event (0).get_time (); + + float x, y; + pan_action.get_motion_coords (0, out x, out y); + + if (pan_action.get_n_current_points () == last_n_points) { + current_x += x - last_x_coord; + current_y += y - last_y_coord; + } + + last_x_coord = x; + last_y_coord = y; + last_n_points = pan_action.get_n_current_points (); + + on_update (calculate_percentage (), time); + + return true; + } + + private double calculate_percentage () { + float current, origin; + if (pan_axis == X_AXIS) { + current = direction == RIGHT ? float.max (current_x, origin_x) : float.min (current_x, origin_x); + origin = origin_x; + } else { + current = direction == DOWN ? float.max (current_y, origin_y) : float.min (current_y, origin_y); + origin = origin_y; + } + + return (current - origin).abs () / travel_distance; + } + + private Gesture build_gesture () { + float delta_x, delta_y; + ((Clutter.GestureAction) pan_action).get_motion_delta (0, out delta_x, out delta_y); + + pan_axis = delta_x.abs () > delta_y.abs () ? Clutter.PanAxis.X_AXIS : Clutter.PanAxis.Y_AXIS; + + if (pan_axis == X_AXIS) { + direction = delta_x > 0 ? GestureDirection.RIGHT : GestureDirection.LEFT; + } else { + direction = delta_y > 0 ? GestureDirection.DOWN : GestureDirection.UP; + } + + return new Gesture () { + type = Clutter.EventType.TOUCHPAD_SWIPE, + direction = direction, + fingers = (int) pan_action.get_n_current_points (), + performed_on_device_type = Clutter.InputDeviceType.TOUCHSCREEN_DEVICE, + origin_x = origin_x, + origin_y = origin_y + }; + } +} diff --git a/src/Gestures/ScrollBackend.vala b/src/Gestures/ScrollBackend.vala index ed933a243..ae5d45d19 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..03fd2597b 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. */ @@ -197,12 +192,16 @@ public class Gala.ToucheggBackend : Object { signal_params.get ("(uudiut)", out type, out direction, out percentage, out fingers, out performed_on_device_type, out elapsed_time); + if (performed_on_device_type == TOUCHSCREEN) { + return; + } + var delta = percentage * DELTA_MULTIPLIER; 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/InternalUtils.vala b/src/InternalUtils.vala index 3578ef3d2..cf376678a 100644 --- a/src/InternalUtils.vala +++ b/src/InternalUtils.vala @@ -336,5 +336,14 @@ namespace Gala { return { 0, 0, (int) screen_width, (int) screen_height }; } } + + public static float travel_distance_from_primary (Meta.Display display, Clutter.Orientation orientation) { + var geom = display.get_monitor_geometry (display.get_primary_monitor ()); + if (orientation == HORIZONTAL) { + return geom.width; + } else { + return geom.height; + } + } } } diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index 50deea0a9..8d6fc9284 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -53,6 +53,8 @@ namespace Gala { } } + private float workspaces_travel_distance; + public MultitaskingView (WindowManager wm) { Object (wm: wm); } @@ -70,12 +72,19 @@ namespace Gala { multitasking_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION); multitasking_gesture_tracker.enable_touchpad (); + multitasking_gesture_tracker.enable_pan (wm, display.get_stage (), () => InternalUtils.travel_distance_from_primary (wm.get_display (), VERTICAL)); 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_pan (wm, this, () => workspaces_travel_distance); 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 ((gesture, timestamp) => { + var direction = workspace_gesture_tracker.settings.get_natural_scroll_direction (gesture); + switch_workspace_with_gesture (direction, timestamp); + }); workspaces = new Clutter.Actor (); @@ -290,39 +299,32 @@ 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 || + gesture.type == TOUCHPAD_SWIPE && gesture.fingers == 1 && (gesture.direction == RIGHT || gesture.direction == LEFT)) { + return true; } + + return false; } - private void switch_workspace_with_gesture (Meta.MotionDirection direction) { + private void switch_workspace_with_gesture (Meta.MotionDirection direction, uint32 timestamp) { if (switching_workspace_in_progress) { return; } @@ -363,6 +365,8 @@ namespace Gala { } } + workspaces_travel_distance = (initial_x - target_x).abs (); + if (!is_nudge_animation && active_icon_group.get_transition ("backdrop-opacity") != null) { active_icon_group.remove_transition ("backdrop-opacity"); } @@ -380,7 +384,7 @@ namespace Gala { switching_workspace_with_gesture = true; if (target_workspace != null) { - target_workspace.activate (display.get_current_time ()); + target_workspace.activate (timestamp); } GestureTracker.OnUpdate on_animation_update = (percentage) => { @@ -692,9 +696,6 @@ namespace Gala { } if (opening) { - modal_proxy = wm.push_modal (this); - modal_proxy.set_keybinding_filter (keybinding_filter); - wm.background_group.hide (); wm.window_group.hide (); wm.top_window_group.hide (); @@ -764,6 +765,13 @@ namespace Gala { dock_clones.destroy_all_children (); wm.pop_modal (modal_proxy); + + multitasking_gesture_tracker.enable_pan (wm, wm.get_display ().get_stage (), () => InternalUtils.travel_distance_from_primary (wm.get_display (), VERTICAL)); + } else { + modal_proxy = wm.push_modal (this); + modal_proxy.set_keybinding_filter (keybinding_filter); + // We now have to listen to events on this because it's now modal i.e. the stage doesn't get the events anymore + multitasking_gesture_tracker.enable_pan (wm, this, () => InternalUtils.travel_distance_from_primary (wm.get_display (), VERTICAL)); } animating = false; diff --git a/src/Widgets/WorkspaceClone.vala b/src/Widgets/WorkspaceClone.vala index 2dfe0c991..aeac825e6 100644 --- a/src/Widgets/WorkspaceClone.vala +++ b/src/Widgets/WorkspaceClone.vala @@ -180,6 +180,13 @@ namespace Gala { }); background = new FramedBackground (wm); background.add_action (background_click_action); + // For some reason the click action doesn't respond to button releases anymore + // once the background was touched. This should hopefully be fixed with the + // new Clutter.Gestures that are being introduced upstream + background.button_release_event.connect (() => { + selected (true); + return Clutter.EVENT_STOP; + }); window_container = new WindowCloneContainer (wm, gesture_tracker, scale_factor) { width = monitor_geometry.width, diff --git a/src/WindowManager.vala b/src/WindowManager.vala index a7fc980a1..de9a1f2b5 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -128,6 +128,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"}; @@ -184,6 +185,9 @@ namespace Gala { screensaver.active_changed.connect (update_input_area); stage = display.get_stage () as Clutter.Stage; + + gesture_tracker.enable_pan (this, stage, () => InternalUtils.travel_distance_from_primary (get_display (), HORIZONTAL)); + var background_settings = new GLib.Settings ("org.gnome.desktop.background"); var color = background_settings.get_string ("primary-color"); stage.background_color = Clutter.Color.from_string (color); @@ -539,57 +543,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; } } @@ -855,16 +843,16 @@ namespace Gala { modal_stack.offer_head (proxy); + proxy.grab = stage.grab (actor); + // modal already active if (modal_stack.size >= 2) return proxy; - unowned Meta.Display display = get_display (); - update_input_area (); - proxy.grab = stage.grab (actor); if (modal_stack.size == 1) { + unowned Meta.Display display = get_display (); display.disable_unredirect (); } diff --git a/src/Zoom.vala b/src/Zoom.vala index c3c27f5be..ea95cc40c 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, wm.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) { diff --git a/src/meson.build b/src/meson.build index e2e61807b..5e942075a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -36,6 +36,7 @@ gala_bin_sources = files( 'Gestures/Gesture.vala', 'Gestures/GestureSettings.vala', 'Gestures/GestureTracker.vala', + 'Gestures/PanBackend.vala', 'Gestures/ScrollBackend.vala', 'Gestures/ToucheggBackend.vala', 'HotCorners/Barrier.vala',