Files
dotfiles/gnome/.local/share/gnome-shell/extensions/screen-rotate@shyzus.github.io/extension.js

226 lines
6.6 KiB
JavaScript

/* 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 <https://www.gnu.org/licenses/>.
*/
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;
}
}