diff --git a/.envrc b/.envrc deleted file mode 100644 index 8392d15..0000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1af4b5d..00c1844 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -.direnv/ -node_modules/ \ No newline at end of file +.direnv/ \ No newline at end of file diff --git a/extension.ts b/extension.ts index f4ac73f..0edfd6c 100644 --- a/extension.ts +++ b/extension.ts @@ -1,25 +1,14 @@ import { Extension } from "resource:///org/gnome/shell/extensions/extension.js" -import { setMonitorTransform } from "./monitorDBusUtils.js" +import * as Main from "resource:///org/gnome/shell/ui/main.js" +import * as MessageTray from "resource:///org/gnome/shell/ui/messageTray.js" -export default class AutoRotate extends Extension { - _listenerId?: number +export default class MyExtension extends Extension { enable() { - this._listenerId = global.display.connect( - "in-fullscreen-changed", - () => { - const monitors = global.display.get_n_monitors() - const isFullscreen = - [...new Array(monitors)].findIndex((_, index) => - global.display.get_monitor_in_fullscreen(index) - ) != -1 - - setMonitorTransform(isFullscreen ? 1 : 0) - } + Main.notify( + "Simple Notification", + "A notification with a title and body" ) } - disable() { - if (this._listenerId != null) - global.display.disconnect(this._listenerId) - } + disable() {} } diff --git a/flake.nix b/flake.nix index c458e4c..2a7728b 100644 --- a/flake.nix +++ b/flake.nix @@ -20,10 +20,6 @@ in { _module.args.pkgs = import inputs.nixpkgs {inherit system;}; - devShells.default = pkgs.mkShell { - buildInputs = with pkgs; [nodejs]; - }; - packages.default = pkgs.buildNpmPackage (finalAttrs: { pname = name; version = "1.0.0"; @@ -50,7 +46,7 @@ }; }); - apps.default = { + apps.install = { type = "app"; program = let file = "/tmp/${uuid}.zip"; @@ -58,7 +54,6 @@ pkgs.writeShellScriptBin "build" '' env -C ${self'.packages.default}/${extDir} ${lib.getExe pkgs.zip} ${file} -FS9r . gnome-extensions install --force ${file} - dbus-run-session -- gnome-shell --nested --wayland ''; }; }; diff --git a/monitorDBusUtils.ts b/monitorDBusUtils.ts deleted file mode 100644 index 00d4b62..0000000 --- a/monitorDBusUtils.ts +++ /dev/null @@ -1,310 +0,0 @@ -// All credit goes to mityax, code taken from here, thank you: https://github.com/mityax/gnome-extension-touchup/blob/main/src/features/screenRotateUtils/floatingScreenRotateButtonFeature.ts - -import Gio from "gi://Gio" -import GLib from "gi://GLib" - -export const Methods = Object.freeze({ - verify: 0, - temporary: 1, - persistent: 2, -}) - -export function callDbusMethod( - method: string, - handler: Gio.AsyncReadyCallback | null, - params: GLib.Variant | null = null -): void { - if (handler !== null && handler !== undefined) { - Gio.DBus.session.call( - "org.gnome.Mutter.DisplayConfig", - "/org/gnome/Mutter/DisplayConfig", - "org.gnome.Mutter.DisplayConfig", - method, - params, - null, - Gio.DBusCallFlags.NONE, - -1, - null, - handler - ) - } else { - Gio.DBus.session.call( - "org.gnome.Mutter.DisplayConfig", - "/org/gnome/Mutter/DisplayConfig", - "org.gnome.Mutter.DisplayConfig", - method, - params, - null, - Gio.DBusCallFlags.NONE, - -1, - null - ) - } -} - -export function setMonitorTransform( - transform: LogicalMonitorTransform, - targetMonitor?: Monitor -): void { - DisplayConfigState.getCurrent().then((state) => { - targetMonitor ??= state.builtinMonitor ?? state.monitors[0] - const logicalMonitor = state.getLogicalMonitorFor( - targetMonitor.connector - ) - if (logicalMonitor) { - logicalMonitor.transform = transform as any - callDbusMethod( - "ApplyMonitorsConfig", - null, - state.packToApply(Methods.temporary) - ) - } - }) -} - -/** - * Possible transform values: - * - 0: normal - * - 1: 90° - * - 2: 180° - * - 3: 270° - * - 4: flipped - * - 5: 90° flipped - * - 6: 180° flipped - * - 7: 270° flipped - */ -export type LogicalMonitorTransform = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 - -export class LogicalMonitor { - x: number - y: number - scale: number - transform: LogicalMonitorTransform - primary: boolean - monitors: [string, string, string, string][] - properties: Record - - constructor(variant: GLib.Variant) { - const unpacked = variant.unpack() as any[] - this.x = unpacked[0].unpack() - this.y = unpacked[1].unpack() - this.scale = unpacked[2].unpack() - this.transform = unpacked[3].unpack() - this.primary = unpacked[4].unpack() - this.monitors = unpacked[5].deep_unpack() - this.properties = unpacked[6].unpack() - - for (const key in this.properties) { - this.properties[key] = this.properties[key].unpack().unpack() - } - } -} - -export class Monitor { - connector: string - vendorName: string - productName: string - productSerial: string - currentModeId: number | null = null - isUnderscanning: boolean = false - isBuiltin: boolean = false - - constructor(variant: GLib.Variant) { - // variant.deepUnpack() yields (in Gnome 48): - // (see for docs: https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml#L385) - // [ - // [ // - [0] - meta information - // "LVDS1", // - [0][0] - connector - // "MetaProducts Inc.", // vendor name - // "MetaMonitor", // product name - // "0xC0FFEE-1" // product serial - // ], - // [ // - [1] - "modes" - // [ - // "1400x1000@60.000", // - [1][0] - // 1400, - // 1000, - // 60, - // 1, - // [ // - [1][1] - // 1, // - [1][1][0] - // 1.25, - // 1.5037593841552734, - // 1.7543859481811523 - // ], - // { - // "is-current": {}, - // "is-preferred": {} - // } - // ] - // ], - // { // - [2] - "props" - // "is-builtin": {}, - // "display-name": {}, - // "is-for-lease": {}, - // "color-mode": {}, - // "supported-color-modes": {} - // } - // ] - - const unpacked = variant.deepUnpack() as any[] - - this.connector = unpacked[0][0] - this.vendorName = unpacked[0][1] - this.productName = unpacked[0][2] - this.productSerial = unpacked[0][3] - - const modes = unpacked[1] - for (const modeVariant of modes) { - const mode = modeVariant - const id = mode[0] - const modeProps = mode[6] - if ("is-current" in modeProps) { - const isCurrent = modeProps["is-current"].get_boolean() - if (isCurrent) { - this.currentModeId = id - break - } - } - } - - const props = unpacked[2] - if ("is-underscanning" in props) { - this.isUnderscanning = props["is-underscanning"].get_boolean() - } - if ("is-builtin" in props) { - this.isBuiltin = props["is-builtin"].get_boolean() - } - } - - /** - * This method makes no guarantees about the returned string, except that it will uniquely - * identify this physical monitor, even after disconnecting and reconnecting it. - * - * This is at the moment done by monitor metadata by constructing a tuple of (vendor name, - * product name, serial). - */ - constructMonitorId(): string { - return `${this.vendorName}::${this.productName}::${this.productSerial}` - } -} - -export class DisplayConfigState { - serial: number - monitors: Monitor[] = [] - logicalMonitors: LogicalMonitor[] = [] - properties: Record - - private constructor(result: GLib.Variant) { - const unpacked = result.unpack() as any[] - this.serial = unpacked[0].unpack() - - const monitorVariants = unpacked[1].unpack() - for (const monitorPacked of monitorVariants) { - this.monitors.push(new Monitor(monitorPacked)) - } - - const logicalMonitorVariants = unpacked[2].unpack() - for (const logicalMonitorPacked of logicalMonitorVariants) { - this.logicalMonitors.push(new LogicalMonitor(logicalMonitorPacked)) - } - - this.properties = unpacked[3].unpack() - for (const key in this.properties) { - this.properties[key] = this.properties[key].unpack().unpack() - } - } - - static async getCurrent(): Promise { - return new Promise((resolve, reject) => { - callDbusMethod("GetCurrentState", (conn, res) => { - try { - const reply = conn?.call_finish(res)! - const configState = new DisplayConfigState(reply) - resolve(configState) - } catch (err) { - reject(err) - } - }) - }) - } - - get builtinMonitor(): Monitor { - return ( - this.monitors.find((monitor) => monitor.isBuiltin) ?? - this.monitors[0] - ) - } - - getMonitor(connector: string): Monitor | null { - return ( - this.monitors.find((monitor) => monitor.connector === connector) || - null - ) - } - - getLogicalMonitorFor(connector: string): LogicalMonitor | null { - return ( - this.logicalMonitors.find((logMonitor) => - logMonitor.monitors.some( - (lmMonitor) => connector === lmMonitor[0] - ) - ) || null - ) - } - - setPrimaryMonitor(monitor: LogicalMonitor) { - this.logicalMonitors.forEach((m) => (m.primary = false)) - monitor.primary = true - - callDbusMethod( - "ApplyMonitorsConfig", - null, - this.packToApply(Methods.temporary) - ) - } - - packToApply(method: number): GLib.Variant { - const packing = [this.serial, method, [], {}] - const logicalMonitors = packing[2] as any[] - const properties: Record = packing[3] - - this.logicalMonitors.forEach((logicalMonitor) => { - const lmonitorPack = [ - logicalMonitor.x, - logicalMonitor.y, - logicalMonitor.scale, - logicalMonitor.transform, - logicalMonitor.primary, - [], - ] - const monitors = lmonitorPack[5] as any[] - for (const logMonitor of logicalMonitor.monitors) { - const connector = logMonitor[0] - const monitor = this.getMonitor(connector) - if (monitor) { - monitors.push([ - connector, - monitor.currentModeId, - { - enable_underscanning: new GLib.Variant( - "b", - monitor.isUnderscanning - ), - }, - ]) - } - } - logicalMonitors.push(lmonitorPack) - }) - - if ("layout-mode" in this.properties) { - properties["layout-mode"] = new GLib.Variant( - "b", - this.properties["layout-mode"] - ) - } - - return new GLib.Variant("(uua(iiduba(ssa{sv}))a{sv})", packing) - } -}