sway workspace indicator

This commit is contained in:
end-4
2024-01-25 15:53:57 +07:00
parent fb2cb6ed7e
commit c5744aa2fe
4 changed files with 482 additions and 369 deletions
+1
View File
@@ -64,6 +64,7 @@ export default {
}; };
// Stuff that don't need to be toggled. And they're async so ugh... // Stuff that don't need to be toggled. And they're async so ugh...
// Bar().catch(print);
forMonitors(Bar); forMonitors(Bar);
forMonitors(BarCornerTopleft); forMonitors(BarCornerTopleft);
forMonitors(BarCornerTopright); forMonitors(BarCornerTopright);
+304 -321
View File
@@ -1,309 +1,303 @@
"use strict"; import GLib from 'gi://GLib';
var __extends = (this && this.__extends) || (function () { import Gio from 'gi://Gio';
var extendStatics = function (d, b) { import Service from "resource:///com/github/Aylur/ags/service.js";
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || const SIS = GLib.getenv('SWAYSOCK');
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b); export const PAYLOAD_TYPE = {
}; MESSAGE_RUN_COMMAND: 0,
return function (d, b) { MESSAGE_GET_WORKSPACES: 1,
if (typeof b !== "function" && b !== null) MESSAGE_SUBSCRIBE: 2,
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); MESSAGE_GET_OUTPUTS: 3,
extendStatics(d, b); MESSAGE_GET_TREE: 4,
function __() { this.constructor = d; } MESSAGE_GET_MARKS: 5,
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); MESSAGE_GET_BAR_CONFIG: 6,
}; MESSAGE_GET_VERSION: 7,
})(); MESSAGE_GET_BINDING_NODES: 8,
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { MESSAGE_GET_CONFIG: 9,
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } MESSAGE_SEND_TICK: 10,
return new (P || (P = Promise))(function (resolve, reject) { MESSAGE_SYNC: 11,
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } MESSAGE_GET_BINDING_STATE: 12,
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } MESSAGE_GET_INPUTS: 100,
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } MESSAGE_GET_SEATS: 101,
step((generator = generator.apply(thisArg, _arguments || [])).next()); EVENT_WORKSPACE: 0x80000000,
}); EVENT_MODE: 0x80000002,
}; EVENT_WINDOW: 0x80000003,
var __generator = (this && this.__generator) || function (thisArg, body) { EVENT_BARCONFIG_UPDATE: 0x80000004,
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; EVENT_BINDING: 0x80000005,
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; EVENT_SHUTDOWN: 0x80000006,
function verb(n) { return function (v) { return step([n, v]); }; } EVENT_TICK: 0x80000007,
function step(op) { EVENT_BAR_STATE_UPDATE: 0x80000014,
if (f) throw new TypeError("Generator is already executing."); EVENT_INPUT: 0x80000015,
while (g && (g = 0, op[0] && (_ = 0)), _) try { }
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value]; const Client_Event = {
switch (op[0]) { change: undefined,
case 0: case 1: t = op; break; container: undefined,
case 4: _.label++; return { value: op[1], done: false }; }
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue; const Workspace_Event = {
default: change: undefined,
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } current: undefined,
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } old: undefined,
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop(); const Geometry = {
_.trys.pop(); continue; x: undefined,
} y: undefined,
op = body.call(thisArg, _); width: undefined,
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } height: undefined,
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; }
//NOTE: not all properties are listed here
export const Node = {
id: undefined,
name: undefined,
type: undefined,
border: undefined,
current_border_width: undefined,
layout: undefined,
orientation: undefined,
percent: undefined,
rect: undefined,
window_rect: undefined,
deco_rect: undefined,
geometry: undefined,
urgent: undefined,
sticky: undefined,
marks: undefined,
focused: undefined,
active: undefined,
focus: undefined,
nodes: undefined,
floating_nodes: undefined,
representation: undefined,
fullscreen_mode: undefined,
app_id: undefined,
pid: undefined,
visible: undefined,
shell: undefined,
output: undefined,
inhibit_idle: undefined,
idle_inhibitors: {
application: undefined,
user: undefined,
},
window: undefined,
window_properties: {
title: undefined,
class: undefined,
instance: undefined,
window_role: undefined,
window_type: undefined,
transient_for: undefined,
} }
}; }
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { export class SwayActiveClient extends Service {
if (ar || !(i in from)) { static {
if (!ar) ar = Array.prototype.slice.call(from, 0, i); Service.register(this, {}, {
ar[i] = from[i]; 'id': ['int'],
} 'name': ['string'],
'class': ['string'],
});
} }
return to.concat(ar || Array.prototype.slice.call(from));
}; _id = 0;
var _a, _b, _c, _d; _name = '';
Object.defineProperty(exports, "__esModule", { value: true }); _class = '';
exports.sway = exports.Sway = exports.SwayActives = exports.SwayActiveID = exports.SwayActiveClient = void 0;
var _1 = require("gi://GLib"); get id() { return this._id; }
var _2 = require("gi://Gio"); get name() { return this._name; }
var service_js_1 = require("../service.js"); get class() { return this._class; }
var SIS = _1.default.getenv('SWAYSOCK');
var SwayActiveClient = /** @class */ (function (_super) { updateProperty(prop, value) {
__extends(SwayActiveClient, _super); if (!['id', 'name', 'class'].includes(prop)) return;
function SwayActiveClient() { super.updateProperty(prop, value);
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._id = 0;
_this._name = '';
_this._class = '';
return _this;
}
Object.defineProperty(SwayActiveClient.prototype, "id", {
get: function () { return this._id; },
enumerable: false,
configurable: true
});
Object.defineProperty(SwayActiveClient.prototype, "name", {
get: function () { return this._name; },
enumerable: false,
configurable: true
});
Object.defineProperty(SwayActiveClient.prototype, "class", {
get: function () { return this._class; },
enumerable: false,
configurable: true
});
SwayActiveClient.prototype.updateProperty = function (prop, value) {
_super.prototype.updateProperty.call(this, prop, value);
this.emit('changed'); this.emit('changed');
};
return SwayActiveClient;
}(service_js_1.default));
exports.SwayActiveClient = SwayActiveClient;
_a = SwayActiveClient;
(function () {
service_js_1.default.register(_a, {}, {
'id': ['int'],
'name': ['string'],
'class': ['string'],
});
})();
var SwayActiveID = /** @class */ (function (_super) {
__extends(SwayActiveID, _super);
function SwayActiveID() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._id = 1;
_this._name = '';
return _this;
} }
Object.defineProperty(SwayActiveID.prototype, "id", { }
get: function () { return this._id; },
enumerable: false, export class SwayActiveID extends Service {
configurable: true static {
}); Service.register(this, {}, {
Object.defineProperty(SwayActiveID.prototype, "name", { 'id': ['int'],
get: function () { return this._name; }, 'name': ['string'],
enumerable: false, });
configurable: true }
});
SwayActiveID.prototype.update = function (id, name) { _id = 0;
_super.prototype.updateProperty.call(this, 'id', id); _name = '';
_super.prototype.updateProperty.call(this, 'name', name);
get id() { return this._id; }
get name() { return this._name; }
update(id, name) {
super.updateProperty('id', id);
super.updateProperty('name', name);
this.emit('changed'); this.emit('changed');
}; }
return SwayActiveID; }
}(service_js_1.default));
exports.SwayActiveID = SwayActiveID; export class SwayActives extends Service {
_b = SwayActiveID; static {
(function () { Service.register(this, {}, {
service_js_1.default.register(_b, {}, { 'client': ['jsobject'],
'id': ['int'], 'monitor': ['jsobject'],
'name': ['string'], 'workspace': ['jsobject'],
}); });
})(); }
var SwayActives = /** @class */ (function (_super) {
__extends(SwayActives, _super); _client = new SwayActiveClient;
function SwayActives() { _monitor = new SwayActiveID;
var _this = _super.call(this) || this; _workspace = new SwayActiveID;
_this._client = new SwayActiveClient;
_this._monitor = new SwayActiveID; constructor() {
_this._workspace = new SwayActiveID; super();
['client', 'workspace', 'monitor'].forEach(function (obj) {
_this["_".concat(obj)].connect('changed', function () { (['client', 'workspace', 'monitor']).forEach(obj => {
_this.notify(obj); this[`_${obj}`].connect('changed', () => {
_this.emit('changed'); this.notify(obj);
this.emit('changed');
}); });
}); });
return _this;
} }
Object.defineProperty(SwayActives.prototype, "client", {
get: function () { return this._client; }, get client() { return this._client; }
enumerable: false, get monitor() { return this._monitor; }
configurable: true get workspace() { return this._workspace; }
}); }
Object.defineProperty(SwayActives.prototype, "monitor", {
get: function () { return this._monitor; }, export class Sway extends Service {
enumerable: false, static {
configurable: true Service.register(this, {}, {
}); 'active': ['jsobject'],
Object.defineProperty(SwayActives.prototype, "workspace", { 'monitors': ['jsobject'],
get: function () { return this._workspace; }, 'workspaces': ['jsobject'],
enumerable: false, 'clients': ['jsobject'],
configurable: true });
}); }
return SwayActives;
}(service_js_1.default)); _decoder = new TextDecoder();
exports.SwayActives = SwayActives; _encoder = new TextEncoder();
_c = SwayActives; _socket;
(function () {
service_js_1.default.register(_c, {}, { _active;
'client': ['jsobject'], _monitors;
'monitor': ['jsobject'], _workspaces;
'workspace': ['jsobject'], _clients;
});
})(); get active() { return this._active; }
var Sway = /** @class */ (function (_super) { get monitors() { return Array.from(this._monitors.values()); }
__extends(Sway, _super); get workspaces() { return Array.from(this._workspaces.values()); }
function Sway() { get clients() { return Array.from(this._clients.values()); }
var _this = this;
getMonitor(id) { return this._monitors.get(id); }
getWorkspace(name) { return this._workspaces.get(name); }
getClient(id) { return this._clients.get(id); }
msg(payload) { this._send(PAYLOAD_TYPE.MESSAGE_RUN_COMMAND, payload); }
constructor() {
if (!SIS) if (!SIS)
console.error('Sway is not running'); console.error('Sway is not running');
_this = _super.call(this) || this; super();
_this._decoder = new TextDecoder();
_this._encoder = new TextEncoder(); this._active = new SwayActives();
_this._active = new SwayActives(); this._monitors = new Map();
_this._monitors = new Map(); this._workspaces = new Map();
_this._workspaces = new Map(); this._clients = new Map();
_this._clients = new Map();
var socket = new _2.default.SocketClient().connect(new _2.default.UnixSocketAddress({ this._socket = new Gio.SocketClient().connect(new Gio.UnixSocketAddress({
path: "".concat(SIS), path: `${SIS}`,
}), null); }), null);
_this._watchSocket(socket.get_input_stream());
_this._output_stream = socket.get_output_stream(); this._watchSocket(this._socket.get_input_stream());
_this.send(4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */, ''); this._send(PAYLOAD_TYPE.MESSAGE_GET_TREE, '');
_this.send(2 /* PAYLOAD_TYPE.MESSAGE_SUBSCRIBE */, JSON.stringify(['window', 'workspace'])); this._send(PAYLOAD_TYPE.MESSAGE_SUBSCRIBE, JSON.stringify(['window', 'workspace']));
_this._active.connect('changed', function () { return _this.emit('changed'); });
['monitor', 'workspace', 'client'].forEach(function (active) { this._active.connect('changed', () => this.emit('changed'));
return _this._active.connect("notify::".concat(active), function () { return _this.notify('active'); }); ['monitor', 'workspace', 'client'].forEach(active =>
}); this._active.connect(`notify::${active}`, () => this.notify('active')));
return _this;
} }
Object.defineProperty(Sway.prototype, "active", {
get: function () { return this._active; }, _send(payloadType, payload) {
enumerable: false, const pb = this._encoder.encode(payload);
configurable: true const type = new Uint32Array([payloadType]);
}); const pl = new Uint32Array([pb.length]);
Object.defineProperty(Sway.prototype, "monitors", { const magic_string = this._encoder.encode('i3-ipc');
get: function () { return Array.from(this._monitors.values()); }, const data = new Uint8Array([
enumerable: false, ...magic_string,
configurable: true ...(new Uint8Array(pl.buffer)),
}); ...(new Uint8Array(type.buffer)),
Object.defineProperty(Sway.prototype, "workspaces", { ...pb]);
get: function () { return Array.from(this._workspaces.values()); }, this._socket.get_output_stream().write(data, null);
enumerable: false, }
configurable: true
}); _watchSocket(stream) {
Object.defineProperty(Sway.prototype, "clients", { stream.read_bytes_async(14, GLib.PRIORITY_DEFAULT, null, (_, resultHeader) => {
get: function () { return Array.from(this._clients.values()); }, const data = stream.read_bytes_finish(resultHeader).get_data();
enumerable: false,
configurable: true
});
Sway.prototype.getMonitor = function (id) { return this._monitors.get(id); };
Sway.prototype.getWorkspace = function (name) { return this._workspaces.get(name); };
Sway.prototype.getClient = function (id) { return this._clients.get(id); };
Sway.prototype.send = function (payloadType, payload) {
var pb = this._encoder.encode(payload);
var type = new Uint32Array([payloadType]);
var pl = new Uint32Array([pb.length]);
var magic_string = this._encoder.encode('i3-ipc');
var data = new Uint8Array(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], magic_string, true), (new Uint8Array(pl.buffer)), true), (new Uint8Array(type.buffer)), true), pb, true));
this._output_stream.write(data, null);
};
Sway.prototype._watchSocket = function (stream) {
var _this = this;
stream.read_bytes_async(14, _1.default.PRIORITY_DEFAULT, null, function (_, resultHeader) {
var data = stream.read_bytes_finish(resultHeader).get_data();
if (!data) if (!data)
return; return;
var payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0]; const payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0];
var payloadType = new Uint32Array(data.slice(10, 14).buffer)[0]; const payloadType = new Uint32Array(data.slice(10, 14).buffer)[0];
stream.read_bytes_async(payloadLength, _1.default.PRIORITY_DEFAULT, null, function (_, resultPayload) { stream.read_bytes_async(
var data = stream.read_bytes_finish(resultPayload).get_data(); payloadLength,
if (!data) GLib.PRIORITY_DEFAULT,
return; null,
_this._onEvent(payloadType, JSON.parse(_this._decoder.decode(data))); (_, resultPayload) => {
_this._watchSocket(stream); const data = stream.read_bytes_finish(resultPayload).get_data();
}); if (!data)
return;
this._onEvent(payloadType, JSON.parse(this._decoder.decode(data)));
this._watchSocket(stream);
});
}); });
}; }
Sway.prototype._onEvent = function (event_type, event) {
return __awaiter(this, void 0, void 0, function () { async _onEvent(event_type, event) {
return __generator(this, function (_e) { if (!event)
if (!event) return;
return [2 /*return*/]; try {
try { switch (event_type) {
switch (event_type) { case PAYLOAD_TYPE.EVENT_WORKSPACE:
case 2147483648 /* PAYLOAD_TYPE.EVENT_WORKSPACE */: this._handleWorkspaceEvent(event);
this._handleWorkspaceEvent(event); break;
break; case PAYLOAD_TYPE.EVENT_WINDOW:
case 2147483651 /* PAYLOAD_TYPE.EVENT_WINDOW */: this._handleWindowEvent(event);
this._handleWindowEvent(event); break;
break; case PAYLOAD_TYPE.MESSAGE_GET_TREE:
case 4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */: this._handleTreeMessage(event);
this._handleTreeMessage(event); break;
break; default:
default: break;
break; }
} } catch (error) {
} logError(error);
catch (error) { }
logError(error); this.emit('changed');
} }
this.emit('changed');
return [2 /*return*/]; _handleWorkspaceEvent(workspaceEvent) {
}); const workspace = workspaceEvent.current;
});
};
Sway.prototype._handleWorkspaceEvent = function (workspaceEvent) {
var workspace = workspaceEvent.current;
switch (workspaceEvent.change) { switch (workspaceEvent.change) {
case 'init': case 'init':
this._workspaces.set(workspace.name, workspace); this._workspaces.set(workspace.name, workspace);
this.notify('workspaces');
break; break;
case 'empty': case 'empty':
this._workspaces.delete(workspace.name); this._workspaces.delete(workspace.name);
this.notify('workspaces');
break; break;
case 'focus': case 'focus':
this._active.workspace.update(workspace.id, workspace.name); this._active.workspace.update(workspace.id, workspace.name);
this._active.monitor.update(1, workspace.output); this._active.monitor.update(1, workspace.output);
this._workspaces.set(workspace.name, workspace); this._workspaces.set(workspace.name, workspace);
this._workspaces.set(workspaceEvent.old.name, workspaceEvent.old); this._workspaces.set(workspaceEvent.old.name, workspaceEvent.old);
this.notify('workspaces');
break; break;
case 'rename': case 'rename':
if (this._active.workspace.id === workspace.id) if (this._active.workspace.id === workspace.id)
this._active.workspace.updateProperty('name', workspace.name); this._active.workspace.updateProperty('name', workspace.name);
this._workspaces.set(workspace.name, workspace); this._workspaces.set(workspace.name, workspace);
this.notify('workspaces');
break; break;
case 'reload': case 'reload':
break; break;
@@ -311,34 +305,36 @@ var Sway = /** @class */ (function (_super) {
case 'urgent': case 'urgent':
default: default:
this._workspaces.set(workspace.name, workspace); this._workspaces.set(workspace.name, workspace);
this.notify('workspaces');
} }
}; this.notify('workspaces');
Sway.prototype._handleWindowEvent = function (clientEvent) { }
var _e;
var client = clientEvent.container; _handleWindowEvent(clientEvent) {
var id = client.id; const client = clientEvent.container;
const id = client.id;
switch (clientEvent.change) { switch (clientEvent.change) {
case 'new': case 'new':
this._clients.set(id, client);
this.notify('clients');
break;
case 'close': case 'close':
this._clients.delete(id); case 'floating':
this.notify('clients'); case 'move':
// Refresh tree since client events don't contain the relevant information
// to be able to modify `workspace.nodes` or `workspace.floating_nodes`.
// There has to be a better way than this though :/
this._send(PAYLOAD_TYPE.MESSAGE_GET_TREE, '');
break; break;
case 'focus': case 'focus':
if (this._active.client.id === id) if (this._active.client.id === id)
return; return;
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
var current_active = this._clients.get(this._active.client.id); const current_active = this._clients.get(this._active.client.id);
if (current_active) if (current_active)
current_active.focused = false; current_active.focused = false;
this._active.client.updateProperty('id', id); this._active.client.updateProperty('id', id);
this._active.client.updateProperty('name', client.name); this._active.client.updateProperty('name', client.name);
this._active.client.updateProperty('class', client.shell === 'xwayland' this._active.client.updateProperty('class', client.shell === 'xwayland'
? ((_e = client.window_properties) === null || _e === void 0 ? void 0 : _e.class) || '' ? client.window_properties?.class || ''
: client.app_id); : client.app_id,
);
break; break;
case 'title': case 'title':
if (client.focused) if (client.focused)
@@ -347,43 +343,39 @@ var Sway = /** @class */ (function (_super) {
this.notify('clients'); this.notify('clients');
break; break;
case 'fullscreen_mode': case 'fullscreen_mode':
case 'move':
case 'floating':
case 'urgent': case 'urgent':
case 'mark': case 'mark':
default: default:
this._clients.set(id, client); this._clients.set(id, client);
this.notify('clients'); this.notify('clients');
} }
}; }
Sway.prototype._handleTreeMessage = function (node) {
var _this = this; _handleTreeMessage(node) {
var _e;
switch (node.type) { switch (node.type) {
case 'root': case 'root':
this._workspaces.clear(); this._workspaces.clear();
this._clients.clear(); this._clients.clear();
this._monitors.clear(); this._monitors.clear();
node.nodes.map(function (n) { return _this._handleTreeMessage(n); }); node.nodes.map(n => this._handleTreeMessage(n));
['workspaces', 'clients', 'monitors'].forEach(function (t) {
_this.notify(t);
});
break; break;
case 'output': case 'output':
this._monitors.set(node.id, node); this._monitors.set(node.id, node);
if (node.active) if (node.active)
this._active.monitor.updateProperty('name', node.name); this._active.monitor.update(node.id, node.name);
node.nodes.map(function (n) { return _this._handleTreeMessage(n); }); node.nodes.map(n => this._handleTreeMessage(n));
this.notify('monitors'); this.notify('monitors');
break; break;
case 'workspace': case 'workspace':
this._workspaces.set(node.name, node); this._workspaces.set(node.name, node);
// I think I'm missing something. There has to be a better way. // I think I'm missing something. There has to be a better way.
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
var hasFocusedChild_1 = function (n) { return n.nodes.some(function (c) { return c.focused || hasFocusedChild_1(c); }); }; const hasFocusedChild =
if (hasFocusedChild_1(node)) (n) => n.nodes.some(c => c.focused || hasFocusedChild(c));
if (node.focused || hasFocusedChild(node))
this._active.workspace.update(node.id, node.name); this._active.workspace.update(node.id, node.name);
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
node.nodes.map(n => this._handleTreeMessage(n));
this.notify('workspaces'); this.notify('workspaces');
break; break;
case 'con': case 'con':
@@ -393,25 +385,16 @@ var Sway = /** @class */ (function (_super) {
this._active.client.updateProperty('id', node.id); this._active.client.updateProperty('id', node.id);
this._active.client.updateProperty('name', node.name); this._active.client.updateProperty('name', node.name);
this._active.client.updateProperty('class', node.shell === 'xwayland' this._active.client.updateProperty('class', node.shell === 'xwayland'
? ((_e = node.window_properties) === null || _e === void 0 ? void 0 : _e.class) || '' ? node.window_properties?.class || ''
: node.app_id); : node.app_id,
);
} }
node.nodes.map(function (n) { return _this._handleTreeMessage(n); }); node.nodes.map(n => this._handleTreeMessage(n));
this.notify('clients'); this.notify('clients');
break; break;
} }
}; }
return Sway; }
}(service_js_1.default));
exports.Sway = Sway; export const sway = new Sway;
_d = Sway; export default sway;
(function () {
service_js_1.default.register(_d, {}, {
'active': ['jsobject'],
'monitors': ['jsobject'],
'workspaces': ['jsobject'],
'clients': ['jsobject'],
});
})();
exports.sway = new Sway;
exports.default = exports.sway;
+5 -2
View File
@@ -11,8 +11,11 @@ const OptionalWorkspaces = async () => {
try { try {
return (await import('./workspaces_hyprland.js')).default(); return (await import('./workspaces_hyprland.js')).default();
} catch { } catch {
// return (await import('./workspaces_sway.js')).default(); try {
return null; return (await import('./workspaces_sway.js')).default();
} catch {
return null;
}
} }
}; };
+172 -46
View File
@@ -1,58 +1,184 @@
const { GLib, Gdk, Gtk } = imports.gi;
const Lang = imports.lang;
const Cairo = imports.cairo;
const Pango = imports.gi.Pango;
const PangoCairo = imports.gi.PangoCairo;
import Widget from "resource:///com/github/Aylur/ags/widget.js"; import Widget from "resource:///com/github/Aylur/ags/widget.js";
import Sway from "../../services/sway.js"; import Sway from "../../services/sway.js";
import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
import options from "../../options.js"; const { execAsync, exec } = Utils;
import { range } from "../../utils.js"; const { Box, DrawingArea, EventBox } = Widget;
const NUM_OF_WORKSPACES = 10;
const dummyWs = Box({ className: 'bar-ws' }); // Not shown. Only for getting size props
const dummyActiveWs = Box({ className: 'bar-ws bar-ws-active' }); // Not shown. Only for getting size props
const dummyOccupiedWs = Box({ className: 'bar-ws bar-ws-occupied' }); // Not shown. Only for getting size props
const dispatch = (arg) => Utils.execAsync(`swaymsg workspace ${arg}`); const switchToWorkspace = (arg) => Utils.execAsync(`swaymsg workspace ${arg}`).catch(print);
const switchToRelativeWorkspace = (self, num) =>
execAsync([`${App.configDir}/scripts/sway/swayToRelativeWs.sh`, `${num}`]).catch(print);
const Workspaces = () => { const WorkspaceContents = (count = 10) => {
const ws = options.workspaces.value || 20; return DrawingArea({
return Widget.Box({ css: `transition: 90ms cubic-bezier(0.1, 1, 0, 1);`,
children: range(ws).map((i) => attribute: {
Widget.Button({ initialized: false,
setup: (btn) => (btn.id = i), workspaceMask: 0,
on_clicked: () => dispatch(i), updateMask: (self) => {
child: Widget.Label({ if (self.attribute.initialized) return; // We only need this to run once
label: `${i}`, const workspaces = Sway.workspaces;
class_name: "indicator", let workspaceMask = 0;
vpack: "center", // console.log('----------------')
}), for (let i = 0; i < workspaces.length; i++) {
setup: (self) => self.hook(Sway, (btn) => { const ws = workspaces[i];
btn.toggleClassName("active", Sway.active.workspace.name == i); // console.log(ws.name, ',', ws.num);
btn.toggleClassName( if (!Number(ws.name)) return;
"occupied", const id = Number(ws.name);
Sway.getWorkspace(`${i}`)?.nodes.length > 0, if (id <= 0) continue; // Ignore scratchpads
); if (id > count) return; // Not rendered
}), if (workspaces[i].windows > 0) {
workspaceMask |= (1 << id);
}
}
self.attribute.workspaceMask = workspaceMask;
self.attribute.initialized = true;
},
toggleMask: (self, occupied, name) => {
if (occupied) self.attribute.workspaceMask |= (1 << parseInt(name));
else self.attribute.workspaceMask &= ~(1 << parseInt(name));
},
},
setup: (area) => area
.hook(Sway.active.workspace, (area) => {
area.setCss(`font-size: ${Sway.active.workspace.name}px;`)
}) })
), .hook(Sway, (self) => self.attribute.updateMask(self), 'notify::workspaces')
setup: (self) => self.hook(Sway.active.workspace, // .hook(Hyprland, (self, name) => self.attribute.toggleMask(self, true, name), 'workspace-added')
(box) => box.children.map((btn) => { // .hook(Hyprland, (self, name) => self.attribute.toggleMask(self, false, name), 'workspace-removed')
btn.visible = Sway.workspaces.some( .on('draw', Lang.bind(area, (area, cr) => {
(ws) => ws.name == btn.id, const allocation = area.get_allocation();
); const { width, height } = allocation;
})
),
});
};
export default () => Widget.EventBox({ const workspaceStyleContext = dummyWs.get_style_context();
class_name: "workspaces panel-button", const workspaceDiameter = workspaceStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
child: Widget.Box({ const workspaceRadius = workspaceDiameter / 2;
// its nested like this to keep it consistent with other PanelButton widgets const workspaceFontSize = workspaceStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 4 * 3;
child: Widget.EventBox({ const workspaceFontFamily = workspaceStyleContext.get_property('font-family', Gtk.StateFlags.NORMAL);
on_scroll_up: () => dispatch("next"), const wsbg = workspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
on_scroll_down: () => dispatch("prev"), const wsfg = workspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL);
class_name: "eventbox",
// binds: [["child", options.workspaces, "value", Workspaces]], const occupiedWorkspaceStyleContext = dummyOccupiedWs.get_style_context();
setup: (self) => self const occupiedbg = occupiedWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
.hook(options.workspaces, (self) => Selection.child = Workspaces(), "value") const occupiedfg = occupiedWorkspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL);
,
}), const activeWorkspaceStyleContext = dummyActiveWs.get_style_context();
const activebg = activeWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
const activefg = activeWorkspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL);
area.set_size_request(workspaceDiameter * count, -1);
const widgetStyleContext = area.get_style_context();
const activeWs = widgetStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
const activeWsCenterX = -(workspaceDiameter / 2) + (workspaceDiameter * activeWs);
const activeWsCenterY = height / 2;
// Font
const layout = PangoCairo.create_layout(cr);
const fontDesc = Pango.font_description_from_string(`${workspaceFontFamily[0]} ${workspaceFontSize}`);
layout.set_font_description(fontDesc);
cr.setAntialias(Cairo.Antialias.BEST);
// Get kinda min radius for number indicators
layout.set_text("0".repeat(count.toString().length), -1);
const [layoutWidth, layoutHeight] = layout.get_pixel_size();
const indicatorRadius = Math.max(layoutWidth, layoutHeight) / 2 * 1.2; // a bit smaller than sqrt(2)*radius
const indicatorGap = workspaceRadius - indicatorRadius;
// Draw workspace numbers
for (let i = 1; i <= count; i++) {
if (area.attribute.workspaceMask & (1 << i)) {
// Draw bg highlight
cr.setSourceRGBA(occupiedbg.red, occupiedbg.green, occupiedbg.blue, occupiedbg.alpha);
const wsCenterX = -(workspaceRadius) + (workspaceDiameter * i);
const wsCenterY = height / 2;
if (!(area.attribute.workspaceMask & (1 << (i - 1)))) { // Left
cr.arc(wsCenterX, wsCenterY, workspaceRadius, 0.5 * Math.PI, 1.5 * Math.PI);
cr.fill();
}
else {
cr.rectangle(wsCenterX - workspaceRadius, wsCenterY - workspaceRadius, workspaceRadius, workspaceRadius * 2)
cr.fill();
}
if (!(area.attribute.workspaceMask & (1 << (i + 1)))) { // Right
cr.arc(wsCenterX, wsCenterY, workspaceRadius, -0.5 * Math.PI, 0.5 * Math.PI);
cr.fill();
}
else {
cr.rectangle(wsCenterX, wsCenterY - workspaceRadius, workspaceRadius, workspaceRadius * 2)
cr.fill();
}
// Set color for text
cr.setSourceRGBA(occupiedfg.red, occupiedfg.green, occupiedfg.blue, occupiedfg.alpha);
}
else
cr.setSourceRGBA(wsfg.red, wsfg.green, wsfg.blue, wsfg.alpha);
layout.set_text(`${i}`, -1);
const [layoutWidth, layoutHeight] = layout.get_pixel_size();
const x = -workspaceRadius + (workspaceDiameter * i) - (layoutWidth / 2);
const y = (height - layoutHeight) / 2;
cr.moveTo(x, y);
// cr.showText(text);
PangoCairo.show_layout(cr, layout);
cr.stroke();
}
// Draw active ws
// base
cr.setSourceRGBA(activebg.red, activebg.green, activebg.blue, activebg.alpha);
cr.arc(activeWsCenterX, activeWsCenterY, indicatorRadius, 0, 2 * Math.PI);
cr.fill();
// inner decor
cr.setSourceRGBA(activefg.red, activefg.green, activefg.blue, activefg.alpha);
cr.arc(activeWsCenterX, activeWsCenterY, indicatorRadius * 0.2, 0, 2 * Math.PI);
cr.fill();
}))
,
})
}
export default () => EventBox({
onScrollUp: (self) => switchToRelativeWorkspace(self, -1),
onScrollDown: (self) => switchToRelativeWorkspace(self, +1),
onMiddleClickRelease: () => App.toggleWindow('overview'),
onSecondaryClickRelease: () => App.toggleWindow('osk'),
attribute: { clicked: false },
child: Box({
homogeneous: true,
className: 'bar-group-margin',
children: [Box({
className: 'bar-group bar-group-standalone bar-group-pad',
css: 'min-width: 2px;',
children: [
WorkspaceContents(10),
]
})]
}), }),
setup: (self) => { setup: (self) => {
console.log('[LOG] Sway workspace module loaded') self.add_events(Gdk.EventMask.POINTER_MOTION_MASK);
self.on('motion-notify-event', (self, event) => {
if (!self.attribute.clicked) return;
const [_, cursorX, cursorY] = event.get_coords();
const widgetWidth = self.get_allocation().width;
const wsId = Math.ceil(cursorX * NUM_OF_WORKSPACES / widgetWidth);
switchToWorkspace(wsId);
})
self.on('button-press-event', (self, event) => {
if (!(event.get_button()[1] === 1)) return; // We're only interested in left-click here
self.attribute.clicked = true;
const [_, cursorX, cursorY] = event.get_coords();
const widgetWidth = self.get_allocation().width;
const wsId = Math.ceil(cursorX * NUM_OF_WORKSPACES / widgetWidth);
switchToWorkspace(wsId);
})
self.on('button-release-event', (self) => self.attribute.clicked = false);
} }
}); });