diff --git a/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/busUtils.js b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/busUtils.js new file mode 100644 index 0000000..6839c8c --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/busUtils.js @@ -0,0 +1,172 @@ +/* busUtils.js +* +* Copyright (C) 2022 kosmospredanie, efosmark +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; + +export const Methods = Object.freeze({ + 'verify': 0, + 'temporary': 1, + 'persistent': 2 +}); + +export const Monitor = class Monitor { + constructor(variant) { + let unpacked = variant.unpack(); + this.connector = unpacked[0].unpack()[0].unpack(); + + let modes = unpacked[1].unpack(); + for (let i = 0; i < modes.length; i++) { + let mode = modes[i].unpack(); + let id = mode[0].unpack(); + let mode_props = mode[6].unpack(); + if ('is-current' in mode_props) { + let is_current = mode_props['is-current'].unpack().get_boolean(); + if (is_current) { + this.current_mode_id = id; + break; + } + } + } + + let props = unpacked[2].unpack(); + if ('is-underscanning' in props) { + this.is_underscanning = props['is-underscanning'].unpack().get_boolean(); + } else { + this.is_underscanning = false; + } + if ('is-builtin' in props) { + this.is_builtin = props['is-builtin'].unpack().get_boolean(); + } else { + this.is_builtin = false; + } + } +} + +export const LogicalMonitor = class LogicalMonitor { + constructor(variant) { + let unpacked = variant.unpack(); + 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(); + // [ [connector, vendor, product, serial]* ] + this.monitors = unpacked[5].deep_unpack(); + this.properties = unpacked[6].unpack(); + for (let key in this.properties) { + this.properties[key] = this.properties[key].unpack().unpack(); + } + } +} + +export const DisplayConfigState = class DisplayConfigState { + constructor(result) { + let unpacked = result.unpack(); + + this.serial = unpacked[0].unpack(); + + this.monitors = []; + let monitors = unpacked[1].unpack(); + monitors.forEach(monitor_packed => { + let monitor = new Monitor(monitor_packed); + this.monitors.push(monitor); + }); + + this.logical_monitors = []; + let logical_monitors = unpacked[2].unpack(); + logical_monitors.forEach(lmonitor_packed => { + let lmonitor = new LogicalMonitor(lmonitor_packed); + this.logical_monitors.push(lmonitor); + }); + + this.properties = unpacked[3].unpack(); + for (let key in this.properties) { + this.properties[key] = this.properties[key].unpack().unpack(); + } + } + + get builtin_monitor() { + for (let i = 0; i < this.monitors.length; i++) { + let monitor = this.monitors[i]; + if (monitor.is_builtin) { + return monitor; + } + } + return null; + } + + get_monitor(connector) { + for (let i = 0; i < this.monitors.length; i++) { + let monitor = this.monitors[i]; + if (monitor.connector === connector) { + return monitor; + } + } + return null; + } + + get_logical_monitor_for(connector) { + for (let i = 0; i < this.logical_monitors.length; i++) { + let lmonitor = this.logical_monitors[i]; + for (let j = 0; j < lmonitor.monitors.length; j++) { + let lm_connector = lmonitor.monitors[j][0]; + if (connector === lm_connector) { + return lmonitor; + } + } + } + return null; + } + + pack_to_apply(method) { + let packing = [this.serial, method, [], {}]; + let logical_monitors = packing[2]; + let properties = packing[3]; + + this.logical_monitors.forEach(lmonitor => { + let lmonitor_pack = [ + lmonitor.x, + lmonitor.y, + lmonitor.scale, + lmonitor.transform, + lmonitor.primary, + [] + ]; + let monitors = lmonitor_pack[5]; + for (let i = 0; i < lmonitor.monitors.length; i++) { + let connector = lmonitor.monitors[i][0]; + let monitor = this.get_monitor(connector); + monitors.push([ + connector, + monitor.current_mode_id, + { + 'enable_underscanning': new GLib.Variant('b', monitor.is_underscanning) + } + ]); + } + logical_monitors.push(lmonitor_pack); + }); + + 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); + } +} diff --git a/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/extension.js b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/extension.js new file mode 100644 index 0000000..326f527 --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/extension.js @@ -0,0 +1,225 @@ +/* extension.js +* Copyright (C) 2023 kosmospredanie, shyzus, Shinigaminai +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'; + +import Gio from 'gi://Gio'; + +import * as SystemActions from 'resource:///org/gnome/shell/misc/systemActions.js'; + +import * as Rotator from './rotator.js' + +const ORIENTATION_LOCK_SCHEMA = 'org.gnome.settings-daemon.peripherals.touchscreen'; +const ORIENTATION_LOCK_KEY = 'orientation-lock'; + +// Orientation names must match those provided by net.hadess.SensorProxy +const Orientation = Object.freeze({ + 'normal': 0, + 'left-up': 1, + 'bottom-up': 2, + 'right-up': 3 +}); + +class SensorProxy { + constructor(rotate_cb) { + this._rotate_cb = rotate_cb; + this._proxy = null; + this._enabled = false; + this._watcher_id = Gio.bus_watch_name( + Gio.BusType.SYSTEM, + 'net.hadess.SensorProxy', + Gio.BusNameWatcherFlags.NONE, + this.appeared.bind(this), + this.vanished.bind(this) + ); + } + + destroy() { + Gio.bus_unwatch_name(this._watcher_id); + if (this._enabled) this.disable(); + this._proxy = null; + } + + enable() { + this._enabled = true; + if (this._proxy === null) return; + this._proxy.call_sync('ClaimAccelerometer', null, Gio.DBusCallFlags.NONE, -1, null); + } + + disable() { + this._enabled = false; + if (this._proxy === null) return; + this._proxy.call_sync('ReleaseAccelerometer', null, Gio.DBusCallFlags.NONE, -1, null); + } + + appeared(connection, name, name_owner) { + this._proxy = Gio.DBusProxy.new_for_bus_sync( + Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, null, + 'net.hadess.SensorProxy', '/net/hadess/SensorProxy', 'net.hadess.SensorProxy', + null); + this._proxy.connect('g-properties-changed', this.properties_changed.bind(this)); + if (this._enabled) { + this._proxy.call_sync('ClaimAccelerometer', null, Gio.DBusCallFlags.NONE, -1, null); + } + } + + vanished(connection, name) { + this._proxy = null; + } + + properties_changed(proxy, changed, invalidated) { + if (!this._enabled) return; + let properties = changed.deep_unpack(); + for (let [name, value] of Object.entries(properties)) { + if (name != 'AccelerometerOrientation') continue; + let target = value.unpack(); + this._rotate_cb(target); + } + } +} + +class ScreenAutorotate { + constructor(settings) { + this._system_actions = SystemActions.getDefault(); + this._settings = settings; + this._system_actions_backup = null; + this._override_system_actions(); + this._orientation_settings = new Gio.Settings({ schema_id: ORIENTATION_LOCK_SCHEMA }); + this._orientation_settings.connect('changed::' + ORIENTATION_LOCK_KEY, this._orientation_lock_changed.bind(this)); + + this._sensor_proxy = new SensorProxy(this.rotate_to.bind(this)); + + this._state = false; // enabled or not + + let locked = this._orientation_settings.get_boolean(ORIENTATION_LOCK_KEY); + if (!locked) this.enable(); + } + + _override_system_actions() { + this._system_actions_backup = { + '_updateOrientationLock': this._system_actions._updateOrientationLock + }; + + this._system_actions._updateOrientationLock = function() { + this._actions.get('lock-orientation').available = true; + this.notify('can-lock-orientation'); + }; + + this._system_actions._updateOrientationLock(); + } + + _restore_system_actions() { + if (this._system_actions_backup === null) return; + this._system_actions._updateOrientationLock = this._system_actions_backup['_updateOrientationLock']; + this._system_actions._updateOrientationLock(); + this._system_actions_backup = null; + } + + _orientation_lock_changed() { + let locked = this._orientation_settings.get_boolean(ORIENTATION_LOCK_KEY); + if (this._state == locked) { + this.toggle(); + } + } + + destroy() { + this._sensor_proxy.destroy(); + this._orientation_settings = null; + this._restore_system_actions(); + } + + toggle() { + if (this._state) { + this.disable(); + } else { + this.enable(); + } + } + enable() { + this._sensor_proxy.enable(); + this._state = true; + } + + disable() { + this._sensor_proxy.disable(); + this._state = false; + } + + rotate_to(orientation) { + const sensor = Orientation[orientation]; + const invert_horizontal_direction = this._settings.get_boolean('invert-horizontal-rotation-direction'); + const invert_vertical_direction = this._settings.get_boolean('invert-vertical-rotation-direction'); + const offset = this._settings.get_int('orientation-offset'); + let target = sensor; // Default to sensor output. + + switch (sensor) { + case 0: + // sensor reports landscape + if (invert_horizontal_direction) { + target = 2; + } + break; + case 1: + // sensor reports portrait + if (invert_vertical_direction) { + target = 3; + } + break; + case 2: + // sensor reports landscape-inverted + if (invert_horizontal_direction) { + target = 0; + } + break; + case 3: + // sensor reports portrait-inverted + if (invert_vertical_direction) { + target = 1; + } + break; + } + + target = (target + offset) % 4; + if (this._settings.get_boolean('debug-logging')) { + console.debug(`sensor=${Orientation[orientation]}`); + console.debug(`offset=${offset}`); + console.debug(`target=${target}`); + } + Rotator.rotate_to(target); + } +} + +export default class ScreenAutoRotateExtension extends Extension { + enable() { + this._settings = this.getSettings(); + this._ext = new ScreenAutorotate(this._settings); + } + + disable() { + /* + Comment for unlock-dialog usage: + The unlock-dialog sesson-mode is usefull for this extension as it allows + the user to rotate their screen or lock rotation after their device may + have auto-locked. This provides the ability to log back in regardless of + the orientation of the device in tablet mode. + */ + this._settings = null; + this._ext.destroy(); + this._ext = null; + } +} + diff --git a/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/metadata.json b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/metadata.json new file mode 100644 index 0000000..8a53079 --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/metadata.json @@ -0,0 +1,17 @@ +{ + "_generated": "Generated by SweetTooth, do not edit", + "description": "Enable screen rotation regardless of touch mode. Fork of Screen Autorotate by Kosmospredanie.", + "gettext-domain": "gnome-shell-extension-screen-rotate", + "name": "Screen Rotate", + "session-modes": [ + "unlock-dialog", + "user" + ], + "settings-schema": "org.gnome.shell.extensions.screen-rotate", + "shell-version": [ + "45" + ], + "url": "https://github.com/shyzus/gnome-shell-extension-screen-autorotate", + "uuid": "screen-rotate@shyzus.github.io", + "version": 15 +} \ No newline at end of file diff --git a/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/prefs.js b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/prefs.js new file mode 100644 index 0000000..862423e --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/prefs.js @@ -0,0 +1,124 @@ +/* prefs.js +* Copyright (C) 2023 kosmospredanie, shyzus, Shinigaminai +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import Gtk from 'gi://Gtk'; +import Gio from 'gi://Gio'; +import Adw from 'gi://Adw'; + +import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export default class MyExtensionPreferences extends ExtensionPreferences { + + fillPreferencesWindow(window) { + window._settings = this.getSettings(); + + const page = new Adw.PreferencesPage(); + window.add(page); + + const orientationGroup = new Adw.PreferencesGroup(); + orientationGroup.set_title('Orientation Settings') + page.add(orientationGroup); + + const debugGroup = new Adw.PreferencesGroup(); + debugGroup.set_title('Debug Settings'); + page.add(debugGroup); + + const invertHorizontalRow = new Adw.ActionRow({ + title: 'Invert horizontal rotation' + }); + orientationGroup.add(invertHorizontalRow); + + const invertVerticalRow = new Adw.ActionRow({ + title: 'Invert vertical rotation' + }); + orientationGroup.add(invertVerticalRow); + + const flipOrientationRow = new Adw.ActionRow({ + title: 'Flip orientation', + subtitle: 'e.g: Landscape to Portrait. Default is Landscape' + }); + orientationGroup.add(flipOrientationRow); + + const setOffsetRow = new Adw.ActionRow({ + title: 'Set orientation offset', + subtitle: 'Valid offset range: 0 to 3. Default is 0\nExperiment with this in case\ + orientation is incorrect due to the display being mounted in a non-landscape orientation\ + e.g PineTab2 or GPD Pocket 3' + }); + + orientationGroup.add(setOffsetRow); + + const toggleLoggingRow = new Adw.ActionRow({ + title: 'Enable debug logging', + subtitle: 'Use "journalctl /usr/bin/gnome-shell -f" to see log output.' + }); + debugGroup.add(toggleLoggingRow); + + const invertHorizontalRotationSwitch = new Gtk.Switch({ + active: window._settings.get_boolean('invert-horizontal-rotation-direction'), + valign: Gtk.Align.CENTER, + }); + + const invertVerticalRotationSwitch = new Gtk.Switch({ + active: window._settings.get_boolean('invert-vertical-rotation-direction'), + valign: Gtk.Align.CENTER, + }); + + const flipOrientationSwitch = new Gtk.Switch({ + active: window._settings.get_boolean('flip-orientation'), + valign: Gtk.Align.CENTER, + }); + + const setOffsetSpinButton = Gtk.SpinButton.new_with_range(0, 3, 1); + setOffsetSpinButton.value = window._settings.get_int('orientation-offset'); + + const toggleLoggingSwitch = new Gtk.Switch({ + active: window._settings.get_boolean('debug-logging'), + valign: Gtk.Align.CENTER + }); + + window._settings.bind('invert-horizontal-rotation-direction', + invertHorizontalRotationSwitch, 'active', Gio.SettingsBindFlags.DEFAULT); + + window._settings.bind('invert-vertical-rotation-direction', + invertVerticalRotationSwitch, 'active', Gio.SettingsBindFlags.DEFAULT); + + window._settings.bind('flip-orientation', + flipOrientationSwitch, 'active', Gio.SettingsBindFlags.DEFAULT); + + window._settings.bind('orientation-offset', + setOffsetSpinButton, 'value', Gio.SettingsBindFlags.DEFAULT); + + window._settings.bind('debug-logging', + toggleLoggingSwitch, 'active', Gio.SettingsBindFlags.DEFAULT); + + invertHorizontalRow.add_suffix(invertHorizontalRotationSwitch); + invertHorizontalRow.activatable_widget = invertHorizontalRotationSwitch; + + invertVerticalRow.add_suffix(invertVerticalRotationSwitch); + invertVerticalRow.activatable_widget = invertVerticalRotationSwitch; + + flipOrientationRow.add_suffix(flipOrientationSwitch); + flipOrientationRow.activatable_widget = flipOrientationSwitch; + + setOffsetRow.add_suffix(setOffsetSpinButton); + setOffsetRow.activatable_widget = setOffsetSpinButton; + + toggleLoggingRow.add_suffix(toggleLoggingSwitch); + toggleLoggingRow.activatable_widget = toggleLoggingSwitch; + } +} \ No newline at end of file diff --git a/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/rotator.js b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/rotator.js new file mode 100644 index 0000000..5f6ff13 --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/rotator.js @@ -0,0 +1,75 @@ +/* rotator.js +* +* Copyright (C) 2022 kosmospredanie, shyzus +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +import Gio from 'gi://Gio'; + +import * as BusUtils from './busUtils.js'; +const connection = Gio.DBus.session; + +export function call_dbus_method(method, params = null, handler) { + if (handler != undefined || handler != null) { + connection.call( + 'org.gnome.Mutter.DisplayConfig', + '/org/gnome/Mutter/DisplayConfig', + 'org.gnome.Mutter.DisplayConfig', + method, + params, + null, + Gio.DBusCallFlags.NONE, + -1, + null, handler); + } else { + connection.call( + 'org.gnome.Mutter.DisplayConfig', + '/org/gnome/Mutter/DisplayConfig', + 'org.gnome.Mutter.DisplayConfig', + method, + params, + null, + Gio.DBusCallFlags.NONE, + -1, + null); + } + +} + +export function get_state() { + return new Promise((resolve, reject) => { + call_dbus_method('GetCurrentState', null, (connection, res) => { + try { + let reply = connection.call_finish(res); + let configState = new BusUtils.DisplayConfigState(reply) + resolve(configState); + } catch (err) { + reject(err); + } + + }); + }) +} + +export function rotate_to(transform) { + this.get_state().then(state => { + let builtin_monitor = state.builtin_monitor; + let logical_monitor = state.get_logical_monitor_for(builtin_monitor.connector); + logical_monitor.transform = transform; + let variant = state.pack_to_apply(BusUtils.Methods['temporary']); + call_dbus_method('ApplyMonitorsConfig', variant); + }).catch(err => { + console.error(err); + }) +} diff --git a/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/schemas/gschemas.compiled b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/schemas/gschemas.compiled new file mode 100644 index 0000000..6833ea0 Binary files /dev/null and b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/schemas/gschemas.compiled differ diff --git a/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/schemas/org.gnome.shell.extensions.screen-rotate.gschema.xml b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/schemas/org.gnome.shell.extensions.screen-rotate.gschema.xml new file mode 100644 index 0000000..f391aff --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/schemas/org.gnome.shell.extensions.screen-rotate.gschema.xml @@ -0,0 +1,46 @@ + + + + + + + false + Invert horizontal rotation direction of the screen + + + false + Invert vertical rotation direction of the screen + + + false + Alter orientation. (e.g: Landscape to Portrait. Default is Landscape.) + + + 0 + Experiment with this offset in case orientation is incorrect. + + + false + Toggle debug logging. + + + +