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

Feature: move mouse with focus to the last position in window #310

Merged
merged 6 commits into from
Oct 31, 2023
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
14 changes: 12 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ restart:
fi

log:
journalctl -o cat -n 0 -f "$$(which gnome-shell)" | grep -v warning
journalctl -o cat -n 0 -f "$$(which gnome-shell)" | grep -v -E 'warning|g_variant'

journal:
journalctl -b 0 -r --since "1 hour ago"
Expand All @@ -107,7 +107,17 @@ test-shell:
env GNOME_SHELL_SLOWDOWN_FACTOR=2 \
MUTTER_DEBUG_DUMMY_MODE_SPECS=1500x1000 \
MUTTER_DEBUG_DUMMY_MONITOR_SCALES=1 \
dbus-run-session -- gnome-shell --nested --wayland
dbus-run-session -- gnome-shell --nested --wayland --wayland-display=wayland-forge

# Usage:
# make test-shell-open &
# make test-shell-open CMD=gnome-text-editor
# make test-shell-open CMD=gnome-terminal ARGS='--app-id app.x'
# make test-shell-open CMD=firefox ARGS='--safe-mode' ENVVARS='MOZ_DBUS_REMOTE=1 MOZ_ENABLE_WAYLAND=1'
#
test-shell-open: CMD=nautilus
test-shell-open:
GDK_BACKEND=wayland WAYLAND_DISPLAY=wayland-forge $(ENVVARS) $(CMD) $(ARGS)&

format:
npm run format
24 changes: 18 additions & 6 deletions TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ On navigating `Appearance` parent item,
## Window Effects

When changing Preferences on Appearance > Colors:

- [ ] - Tiled Focus Hint updates border size and color
- [ ] - Tiled Focus Hint color updates also updates preview tiled hint
- [ ] - Tiled Focus Hint color updates also updates overview and workspace thumbnail hints
Expand All @@ -46,16 +47,27 @@ When changing Preferences on Appearance > Colors:

## Tiling Mode

When dragging a window,
When dragging a window:

- [ ] - Should show a preview hint where the window would be tiled. If Tile Modifier is set, Super or Ctrl or Alt would show preview otherwise shows preview automatically:
- [ ] - For split layout, should show preview hint left/right on horizontal, top/bottom on vertical following the mouse pointer.
- [ ] - For tabbed layout, should show preview hint same size as the current front window.
- [ ] - For stacked layout, should show preview hint same size as the current front window.
- [ ] - There should be the following preview hint regions: LEFT, TOP, RIGHT, BOTTOM and CENTER
- [ ] - For split layout, should show preview hint left/right on horizontal, top/bottom on vertical following the mouse pointer.
- [ ] - For tabbed layout, should show preview hint same size as the current front window.
- [ ] - For stacked layout, should show preview hint same size as the current front window.
- [ ] - There should be the following preview hint regions: LEFT, TOP, RIGHT, BOTTOM and CENTER
- [ ] - On dropping, should tile the window on the preview hint position shown before dropping.
- [ ] - On dropping to a different monitor, should tile based on the preview hint position shown unless empty monitor.
- [ ] - Empty monitors will not show a preview hint.
- [ ] - Empty monitors will not show a preview hint.

## Floating Mode

## Layout Mode

## Focus

While alternating between windows, the mouse cursor position should follow the focus:

- [ ] - when moving the focus to another window with keyboard or mouse.
- [ ] - when swapping two windows positions.
- [ ] - when alternating to a new window using `Alt+Tab` or `Super+Tab`.
- [ ] - when exiting on Overview mode.
- [ ] - at the same position it was previouly on the window.
26 changes: 21 additions & 5 deletions lib/extension/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class Node extends GObject.Object {
this.tab = null;
this.decoration = null;
this.app = null;
this.pointer = null;

if (this.isWindow()) {
// When destroy() is called on Meta.Window, it might not be
Expand Down Expand Up @@ -1561,7 +1562,23 @@ export class Tree extends Node {
}

debugTree() {
// this.debugChildNodes(this);
}

debugChildNodes(node) {
this.debugNode(this);
node.childNodes.forEach((child) => {
this.debugChildNodes(child);
});
}

debugParentNodes(node) {
if (node) {
if (node.parentNode) {
this.debugParentNodes(node.parentNode);
}
this.debugNode(node);
}
}

debugNode(node) {
Expand All @@ -1576,7 +1593,7 @@ export class Tree extends Node {

let attributes = "";

if (node.isWindow()) {
if (node.isWindow && node.isWindow()) {
let metaWindow = node.nodeValue;
attributes += `class:'${metaWindow.get_wm_class()}',title:'${
metaWindow.title
Expand All @@ -1590,6 +1607,9 @@ export class Tree extends Node {

if (node.rect) {
attributes += `,rect:${node.rect.width}x${node.rect.height}+${node.rect.x}+${node.rect.y}`;
const pointerCoord = global.get_pointer();
const pointerInside = Utils.rectContainsPoint(node.rect, pointerCoord) ? 'yes' : 'no';
attributes += `,pointer:${pointerInside}`;
}

if (level !== 0) Logger.debug(`${spacing}|`);
Expand All @@ -1598,10 +1618,6 @@ export class Tree extends Node {
node.index !== null ? node.index : "-"
} @${attributes}`
);

node.childNodes.forEach((child) => {
this.debugNode(child);
});
}

findParent(childNode, parentNodeType) {
Expand Down
98 changes: 93 additions & 5 deletions lib/extension/window.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class WindowManager extends GObject.Object {
this._tree = new Tree(this);
this.eventQueue = new Queue();
this.theme = this.ext.theme;
this.lastFocusedWindow = null;
Logger.info("forge initialized");
}

Expand Down Expand Up @@ -350,6 +351,7 @@ export class WindowManager extends GObject.Object {
const focusNodeWindow = this.tree.findNode(this.focusMetaWindow);
this.updateStackedFocus(focusNodeWindow);
this.updateTabbedFocus(focusNodeWindow);
this.movePointerWith(focusNodeWindow);
},
};
this.queueEvent(eventObj);
Expand Down Expand Up @@ -499,6 +501,7 @@ export class WindowManager extends GObject.Object {
if (prev) prev.parentNode.lastTabFocus = prev.nodeValue;
this.renderTree("move-tabbed-queue");
}
this.movePointerWith(focusNodeWindow);
}
},
});
Expand All @@ -523,6 +526,7 @@ export class WindowManager extends GObject.Object {
focusNodeWindow.nodeValue.raise();
this.updateTabbedFocus(focusNodeWindow);
this.updateStackedFocus(focusNodeWindow);
this.movePointerWith(focusNodeWindow);
this.renderTree("swap", true);
break;
case "Split":
Expand Down Expand Up @@ -679,6 +683,7 @@ export class WindowManager extends GObject.Object {
);
let lastActiveNodeWindow = this.tree.findNode(lastActiveWindow);
this.tree.swapPairs(lastActiveNodeWindow, focusNodeWindow);
this.movePointerWith(focusNodeWindow);
this.renderTree("swap-last-active");
}
break;
Expand Down Expand Up @@ -914,7 +919,7 @@ export class WindowManager extends GObject.Object {
try {
nodeWindow.tab.remove_style_class_name("window-tabbed-tab-active");
} catch (e) {
Logger.warn(e);
// Logger.warn(e);
}
}
}
Expand Down Expand Up @@ -1425,6 +1430,8 @@ export class WindowManager extends GObject.Object {
this.updateDecorationLayout();
this.updateStackedFocus();
this.updateTabbedFocus();
let focusNodeWindow = this.tree.findNode(this.focusMetaWindow);
this.movePointerWith(focusNodeWindow);
},
});
let focusNodeWindow = this.tree.findNode(this.focusMetaWindow);
Expand Down Expand Up @@ -1495,6 +1502,8 @@ export class WindowManager extends GObject.Object {
.get_workspace()
.activate_with_focus(metaWindow, global.display.get_current_time());
this.moveCenter(metaWindow);
} else {
this.movePointerWith(metaWindow);
}
}
}
Expand Down Expand Up @@ -1777,10 +1786,26 @@ export class WindowManager extends GObject.Object {
*/
movePointerWith(nodeWindow) {
if (!nodeWindow || !nodeWindow._data) return;
let rect = nodeWindow._data.get_frame_rect();
global.get_pointer();
const seat = Clutter.get_default_backend().get_default_seat();
seat.warp_pointer(rect.x, rect.y);
if (this.ext.settings.get_boolean("move-pointer-focus-enabled")) {
this.storePointerLastPosition(this.lastFocusedWindow);
}
if (this.canMovePointerInsideNodeWindow(nodeWindow)) {
this.warpPointerToNodeWindow(nodeWindow);
}
this.lastFocusedWindow = nodeWindow;
this.tree.debugParentNodes(nodeWindow);
}

warpPointerToNodeWindow(nodeWindow) {
const newCoord = this.getPointerPositionInside(nodeWindow);
if (newCoord && newCoord.x && newCoord.y) {
const seat = Clutter.get_default_backend().get_default_seat();
if (seat) {
const wmTitle = nodeWindow.nodeValue.get_title();
Logger.debug(`moved pointer to [${wmTitle}] at (${newCoord.x},${newCoord.y})`);
seat.warp_pointer(newCoord.x, newCoord.y);
}
}
}

getPointer() {
Expand Down Expand Up @@ -2147,6 +2172,69 @@ export class WindowManager extends GObject.Object {
}
}

canMovePointerInsideNodeWindow(nodeWindow) {
if (nodeWindow && nodeWindow._data) {
const metaWindow = nodeWindow.nodeValue;
const metaRect = metaWindow.get_frame_rect();
const pointerCoord = global.get_pointer();
return metaRect
// xdg-copy creates a 1x1 pixel window to capture mouse events.
&& metaRect.width > 8
&& metaRect.height > 8
&& !Utils.rectContainsPoint(metaRect, pointerCoord)
&& !metaWindow.minimized
&& !Main.overview.visible
&& !this.pointerIsOverParentDecoration(nodeWindow, pointerCoord)
;
}
return false;
}

pointerIsOverParentDecoration(nodeWindow, pointerCoord) {
if (pointerCoord && nodeWindow && nodeWindow.parentNode) {
let node = nodeWindow.parentNode;
if (node.isTabbed() || node.isStacked()) {
return Utils.rectContainsPoint(node.rect, pointerCoord);
}
}
return false;
}

getPointerPositionInside(nodeWindow) {
if (nodeWindow && nodeWindow._data) {
const metaWindow = nodeWindow.nodeValue;
const metaRect = metaWindow.get_frame_rect();
// on: last position of cursor inside window
// on: titlebar: near to app toolbars, menubar, tabs, etc...
let [wx, wy] = nodeWindow.pointer
? [nodeWindow.pointer.x, nodeWindow.pointer.y]
: [metaRect.width / 2, 8];
let px = wx >= metaRect.width ? metaRect.width - 8 : wx;
let py = wy >= metaRect.height ? metaRect.height - 8 : wy;
return {
x: metaRect.x + px,
y: metaRect.y + py,
};
}
return null;
}

storePointerLastPosition(nodeWindow) {
if (nodeWindow && nodeWindow._data) {
const metaWindow = nodeWindow.nodeValue;
const metaRect = metaWindow.get_frame_rect();
const pointerCoord = global.get_pointer();
if (Utils.rectContainsPoint(metaRect, pointerCoord)) {
let px = pointerCoord[0] - metaRect.x;
let py = pointerCoord[1] - metaRect.y;
if (px > 0 && py > 0) {
nodeWindow.pointer = { x: px, y: py, };
Logger.debug(`stored pointer for [${metaWindow.get_title()}] at (${px},${py})`);
}
}
}
}

findNodeWindowAtPointer(focusNodeWindow) {
let pointerCoord = global.get_pointer();

Expand Down
13 changes: 13 additions & 0 deletions lib/prefs/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,19 @@ export class SettingsPage extends PreferencesPage {
],
});

this.add_group({
title: _("Behavior"),
children: [
new SwitchRow({
title: _("Move Pointer to the Focused Window"),
subtitle: _("Move the pointer when focusing or swapping via keyboard"),
experimental: true,
settings,
bind: "move-pointer-focus-enabled",
}),
]
});

if (!production) {
this.add_group({
title: _("Logger"),
Expand Down