diff --git a/extension.ts b/extension.ts index b14453a..c02cc27 100644 --- a/extension.ts +++ b/extension.ts @@ -26,6 +26,7 @@ export default class aerospike extends Extension { disable() { this.windowManager.disable() + this.removeKeybindings() } diff --git a/justfile b/justfile index c694103..97e24fa 100644 --- a/justfile +++ b/justfile @@ -33,7 +33,7 @@ run: install-and-run: install run live-debug: - journalctl /usr/bin/gnome-shell -f -o cat + journalctl /usr/bin/gnome-shell -f -o cat | tee debug.log #pack: build # gnome-extensions pack dist \ diff --git a/src/monitor.ts b/src/monitor.ts index 751a270..9749cbe 100644 --- a/src/monitor.ts +++ b/src/monitor.ts @@ -45,6 +45,13 @@ export default class MonitorManager { } } + disconnectSignals(): void { + this._windows.forEach((window) => { + window.disconnectWindowSignals(); + } + ) + } + removeAllWindows(): void { this._windows.clear() } @@ -72,7 +79,6 @@ export default class MonitorManager { return; } this._tileHorizontally(windows, workArea) - } diff --git a/src/window.ts b/src/window.ts index 6265660..02dd895 100644 --- a/src/window.ts +++ b/src/window.ts @@ -1,8 +1,12 @@ import Meta from 'gi://Meta'; -import GLib from "gi://GLib"; +import St from "gi://St"; import Clutter from "gi://Clutter"; +import GObject from "gi://GObject"; +import GLib from "gi://GLib"; +import * as Main from "resource:///org/gnome/shell/ui/main.js"; import {IWindowManager} from "./windowManager.js"; import {Logger} from "./utils/logger.js"; +import MaximizeFlags = Meta.MaximizeFlags; export type Signal = { name: string; @@ -36,7 +40,6 @@ export class WindowWrapper { const windowId = this._window.get_id(); - // Handle window destruction const destroyId = this._window.connect('unmanaging', window => { Logger.log("REMOVING WINDOW", windowId); @@ -52,52 +55,15 @@ export class WindowWrapper { }); this._signals.push({name: 'notify::has-focus', id: focusId}); - // Track window movement using position-changed signal - let lastPositionChangeTime = 0; - let dragInProgress = false; - - // const positionChangedId = this._window.connect('position-changed', window => { - // Logger.log("position-changed", window.get_id()); - // Logger.log(window.get_monitor()) - // // const currentTime = Date.now(); - // // const [x, y, _] = global.get_pointer(); - // // - // // // If this is the first move or it's been a while since the last move, consider it the start of a drag - // // if (!dragInProgress) { - // // dragInProgress = true; - // // Logger.log(`Window drag started for window ${windowId}. Mouse position: ${x}, ${y}`); - // // } - // // - // // // Update the time of the last position change - // // lastPositionChangeTime = currentTime; - // // - // // // Set a timeout to detect when dragging stops (when position changes stop coming in) - // // GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { - // // const timeSinceLastMove = Date.now() - lastPositionChangeTime; - // // // If it's been more than 200ms since the last move and we were dragging, consider the drag ended - // // if (timeSinceLastMove >= 200 && dragInProgress) { - // // dragInProgress = false; - // // const [endX, endY, _] = global.get_pointer(); - // // Logger.log(`Window drag ended for window ${windowId}. Mouse position: ${endX}, ${endY}`); - // // } - // // return GLib.SOURCE_REMOVE; // Remove the timeout - // // }); - // }); - // this._signals.push({name: 'position-changed', id: positionChangedId}); // Handle minimization const minimizeId = this._window.connect('notify::minimized', () => { if (this._window.minimized) { Logger.log(`Window minimized: ${windowId}`); - // Remove window from managed windows temporarily - // windowManager.removeFromTree(this._window); - // If this was the active window, find a new one - // Retile remaining windows windowManager.handleWindowMinimized(this); } else if (!this._window.minimized) { Logger.log(`Window unminimized: ${windowId}`); - // windowManager.addWindow(this._window); windowManager.handleWindowUnminimized(this); } @@ -125,107 +91,56 @@ export class WindowWrapper { this._window.disconnect(signal.id); } } catch (e) { - // Window might already be gone + Logger.warn("error disconnecting signal", signal, e); } }); } } resizeWindow(x: number, y: number, width: number, height: number) { - // First, ensure window is not maximized or fullscreen - if (this._window.get_maximized()) { - Logger.log("WINDOW MAXIMIZED") - this._window.unmaximize(Meta.MaximizeFlags.BOTH); - } + Logger.info(this._window.allows_move()) + Logger.info(this._window.allows_resize()) - if (this._window.is_fullscreen()) { - Logger.log("WINDOW IS FULLSCREEN") - this._window.unmake_fullscreen(); - } - // Logger.log("WINDOW", this._window.get_window_type(), this._window.allows_move()); - // Logger.log("MONITOR INFO", getUsableMonitorSpace(this._window)); - // Logger.log("NEW_SIZE", x, y, width, height); - // win.move_resize_frame(false, 50, 50, 300, 300); - this._window.move_resize_frame(false, x, y, width, height); - // Logger.log("RESIZED WINDOW", this._window.get_frame_rect().height, this._window.get_frame_rect().width, this._window.get_frame_rect().x, this._window.get_frame_rect().y); - } + if (this._window.get_maximized() == MaximizeFlags.BOTH || this._window.is_fullscreen() || this._window.is_monitor_sized()) { + Logger.info("is monitor sized?", this._window.is_monitor_sized()); + Logger.info("is monitor sized?", this._window.is_fullscreen()); + Logger.info("is monitor sized?", this._window.get_maximized()); + Logger.info("is monitor sized?", this._window.get_maximized() == MaximizeFlags.BOTH); - safelyResizeWindow(x: number, y: number, width: number, height: number): void { - Logger.log("SAFELY RESIZE", x, y, width, height); - const actor = this._window.get_compositor_private(); - - if (!actor) { - Logger.log("No actor available, can't resize safely yet"); + Logger.info("window is fullscreen or maximized and will not be resized", this._window) return; + // Logger.log("WINDOW IS FULLSCREEN") + // this._window.unmake_fullscreen(); } - -// Set a flag to track if the resize has been done - let resizeDone = false; - -// Connect to the first-frame signal - const id = actor.connect('first-frame', () => { - // Disconnect the signal handler - actor.disconnect(id); - - if (!resizeDone) { - resizeDone = true; - - // Add a small delay - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => { - try { - this.resizeWindow(x, y, width, height); - } catch (e) { - console.error("Error resizing window:", e); - } - return GLib.SOURCE_REMOVE; - }); - } - }); - -// Fallback timeout in case the first-frame signal doesn't fire -// (for windows that are already mapped) - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => { - if (!resizeDone) { - resizeDone = true; - try { - this.resizeWindow(x, y, width, height); - } catch (e) { - console.error("Error resizing window (fallback):", e); - } - } - return GLib.SOURCE_REMOVE; - }); + this._window.move_resize_frame(false, x, y, width, height); } - // if (!this._window) return; - // this._window.unmaximize(Meta.MaximizeFlags.HORIZONTAL); - // this._window.unmaximize(Meta.MaximizeFlags.VERTICAL); - // this._window.unmaximize(Meta.MaximizeFlags.BOTH); - // - // let windowActor = this._window.get_compositor_private() as Clutter.Actor; - // if (!windowActor) return; - // windowActor.remove_all_transitions(); - // - // this._window.move_frame(true, x, y); - // this._window.move_resize_frame(true, x, y, width, height); + // This is meant to be an exact copy of Forge's move function, renamed to maintain your API + safelyResizeWindow(x: number, y: number, width: number, height: number): void { + // Keep minimal logging + Logger.log("SAFELY RESIZE", x, y, width, height); + + // Simple early returns like Forge + if (!this._window) return; + + // Skip the this._window.grabbed check since we confirmed it doesn't exist in Meta.Window + + // Unmaximize in all directions - no try/catch to match Forge + this._window.unmaximize(Meta.MaximizeFlags.HORIZONTAL); + this._window.unmaximize(Meta.MaximizeFlags.VERTICAL); + this._window.unmaximize(Meta.MaximizeFlags.BOTH); + + // Get actor and return early if not available - no try/catch + const windowActor = this._window.get_compositor_private() as Clutter.Actor; + if (!windowActor) return; + + // Remove transitions - no try/catch + windowActor.remove_all_transitions(); + + // Move and resize in exact order as Forge - no try/catch + this._window.move_frame(true, x, y); + this._window.move_resize_frame(true, x, y, width, height); + } } - -function getUsableMonitorSpace(window: Meta.Window) { - // Get the current workspace - const workspace = window.get_workspace(); - - // Get the monitor index that this window is on - const monitorIndex = window.get_monitor(); - - // Get the work area - const workArea = workspace.get_work_area_for_monitor(monitorIndex); - - return { - x: workArea.x, - y: workArea.y, - width: workArea.width, - height: workArea.height - }; -} \ No newline at end of file diff --git a/src/windowManager.ts b/src/windowManager.ts index 9a590a8..5380091 100644 --- a/src/windowManager.ts +++ b/src/windowManager.ts @@ -7,6 +7,7 @@ import * as Main from "resource:///org/gnome/shell/ui/main.js"; import Mtk from "@girs/mtk-16"; import {Logger} from "./utils/logger.js"; import MonitorManager from "./monitor.js"; +import {MessageTray} from "@girs/gnome-shell/ui/messageTray"; export interface IWindowManager { @@ -23,13 +24,14 @@ export interface IWindowManager { syncActiveWindow(): number | null; } + const _UNUSED_MONITOR_ID = -1 export default class WindowManager implements IWindowManager { _displaySignals: number[]; _windowManagerSignals: number[]; _workspaceManagerSignals: number[]; - _shieldScreenSignals: number[]; _overviewSignals: number[]; + _activeWindowId: number | null; _grabbedWindowMonitor: number; _monitors: Map; @@ -42,42 +44,28 @@ export default class WindowManager implements IWindowManager { this._windowManagerSignals = []; this._workspaceManagerSignals = []; this._overviewSignals = []; - this._shieldScreenSignals = []; this._activeWindowId = null; this._grabbedWindowMonitor = _UNUSED_MONITOR_ID; this._monitors = new Map(); this._sessionProxy = null; this._lockedSignalId = null; - this._isScreenLocked = false; + this._isScreenLocked = false; // Initialize to unlocked state } public enable(): void { Logger.log("Starting Aerospike Window Manager"); - this.captureExistingWindows(); // Connect window signals - this.instantiateDisplaySignals() + this.instantiateDisplaySignals(); const mon_count = global.display.get_n_monitors(); for (let i = 0; i < mon_count; i++) { this._monitors.set(i, new MonitorManager(i)); } - } - public disable(): void { - Logger.log("DISABLED AEROSPIKE WINDOW MANAGER!") - // Disconnect the focus signal and remove any existing borders - this.disconnectDisplaySignals(); - this.removeAllWindows(); + this.captureExistingWindows(); } - removeAllWindows(): void { - this._monitors.forEach((monitor: MonitorManager) => { - monitor.removeAllWindows(); - }) - } - - instantiateDisplaySignals(): void { this._displaySignals.push( global.display.connect("grab-op-begin", (display, window, op) => { @@ -95,7 +83,7 @@ export default class WindowManager implements IWindowManager { global.display.connect("showing-desktop-changed", () => { Logger.log("SHOWING DESKTOP CHANGED"); }), - global.display.connect("workareas-changed", () => { + global.display.connect("workareas-changed", (display) => { Logger.log("WORK AREAS CHANGED"); }), global.display.connect("in-fullscreen-changed", () => { @@ -103,20 +91,12 @@ export default class WindowManager implements IWindowManager { }), ) - this._windowManagerSignals = [ - // global.window_manager.connect("minimize", (_source, window) => { - // Logger.log("MINIMIZING WINDOW") - // }), - // global.window_manager.connect("unminimize", (_source, window) => { - // Logger.log("WINDOW UNMINIMIZED"); - // }), global.window_manager.connect("show-tile-preview", (_, _metaWindow, _rect, _num) => { Logger.log("SHOW TITLE PREVIEW!") }), ]; - this._workspaceManagerSignals = [ global.workspace_manager.connect("showing-desktop-changed", () => { Logger.log("SHOWING DESKTOP CHANGED AT WORKSPACE LEVEL"); @@ -132,7 +112,6 @@ export default class WindowManager implements IWindowManager { }), ]; - this._overviewSignals = [ Main.overview.connect("hiding", () => { // this.fromOverview = true; @@ -155,34 +134,50 @@ export default class WindowManager implements IWindowManager { }), ]; - // Main.screenShield; - // Handler for lock event - this._shieldScreenSignals.push(Main.screenShield.connect('lock-screen', () => { - console.log('Session locked at:', new Date().toISOString()); - }), Main.screenShield.connect('unlock-screen', () => { - console.log('Session unlocked at:', new Date().toISOString()); - }) - ); + } - // Handler for unlock event + public disable(): void { + Logger.log("DISABLED AEROSPIKE WINDOW MANAGER!") + // Disconnect the focus signal and remove any existing borders + this.disconnectSignals(); + this.removeAllWindows(); + } - // this._signalsBound = true; + removeAllWindows(): void { + this._monitors.forEach((monitor: MonitorManager) => { + monitor.removeAllWindows(); + }) + } + + disconnectSignals(): void { + this.disconnectDisplaySignals(); + this.disconnectMonitorSignals(); + } + + disconnectMonitorSignals(): void { + this._monitors.forEach((monitor: MonitorManager) => { + monitor.disconnectSignals(); + }) } disconnectDisplaySignals(): void { this._displaySignals.forEach((signal) => { - global.disconnect(signal) + global.display.disconnect(signal) }) this._windowManagerSignals.forEach((signal) => { - global.disconnect(signal) + global.window_manager.disconnect(signal) }) this._workspaceManagerSignals.forEach((signal) => { - global.disconnect(signal) + global.workspace_manager.disconnect(signal) + }) + this._overviewSignals.forEach((signal) => { + Main.overview.disconnect(signal) }) } + handleGrabOpBegin(display: Meta.Display, window: Meta.Window, op: Meta.GrabOp): void { Logger.log("Grab Op Start"); Logger.log(display, window, op) @@ -195,7 +190,6 @@ export default class WindowManager implements IWindowManager { Logger.log("primary display", display.get_primary_monitor()) var rect = window.get_frame_rect() Logger.info("Release Location", window.get_monitor(), rect.x, rect.y, rect.width, rect.height) - this._tileMonitors(); const old_mon_id = this._grabbedWindowMonitor; const new_mon_id = window.get_monitor(); @@ -215,6 +209,7 @@ export default class WindowManager implements IWindowManager { } new_mon.addWindow(wrapped) } + this._tileMonitors(); Logger.info("monitor_start and monitor_end", this._grabbedWindowMonitor, window.get_monitor()); } @@ -286,17 +281,6 @@ export default class WindowManager implements IWindowManager { this._monitors.get(window.get_monitor())?.addWindow(wrapper) } - // public UnmanageWindow(window: Meta.Window) { - // this._windows.delete(window.get_id()); - // this._unmanagedWindows.add(window.get_id()) - // } - // - // public ManageWindow(window: Meta.Window) { - // this._windows.set(window.get_id(), { - // window, - // }) - // } - _tileMonitors(): void { for (const monitor of this._monitors.values()) { monitor._tileWindows() @@ -329,47 +313,6 @@ export default class WindowManager implements IWindowManager { * @returns The window ID of the active window, or null if no window is active */ public syncActiveWindow(): number | null { - // // Get the active workspace - // const workspace = global.workspace_manager.get_active_workspace(); - // - // // Check if there is an active window - // const activeWindow = global.display.get_focus_window(); - // - // if (!activeWindow) { - // Logger.log("No active window found in GNOME"); - // this._activeWindowId = null; - // return null; - // } - // - // // Get the window ID - // const windowId = activeWindow.get_id(); - // - // // Check if this window is being managed by our extension - // if (this._windows.has(windowId)) { - // Logger.log(`Setting active window to ${windowId}`); - // this._activeWindowId = windowId; - // return windowId; - // } else { - // Logger.log(`Window ${windowId} is not managed by this extension`); - // - // // Try to find a managed window on the current workspace to make active - // const managedWindows = Array.from(this._windows.entries()) - // .filter(([_, wrapper]) => - // wrapper.window && wrapper.window.get_workspace() === workspace); - // - // if (managedWindows.length > 0) { - // // Take the first managed window on this workspace - // const firstWindowId = managedWindows[0][0]; - // Logger.log(`Using managed window ${firstWindowId} as active instead`); - // this._activeWindowId = firstWindowId; - // return firstWindowId; - // } - // - // // No managed windows on this workspace - // Logger.log("No managed windows found on the active workspace"); - // this._activeWindowId = null; - // return null; - // } return null; }