mirror of
https://github.com/gosticks/PaperWM.git
synced 2026-02-16 13:52:49 +00:00
231 lines
6.3 KiB
JavaScript
231 lines
6.3 KiB
JavaScript
const Extension = imports.misc.extensionUtils.extensions['paperwm@hedning:matrix.org']
|
||
const Gdk = imports.gi.Gdk;
|
||
var Meta = imports.gi.Meta;
|
||
|
||
var screen = global.screen;
|
||
var display = global.display;
|
||
|
||
var debug_all = false; // Turn off by default
|
||
var debug_filter = {};
|
||
function debug() {
|
||
let keyword = arguments[0];
|
||
let filter = debug_filter[keyword];
|
||
if (filter === false)
|
||
return;
|
||
if (debug_all || filter === true)
|
||
print(Array.prototype.join.call(arguments, " | "));
|
||
}
|
||
|
||
function warn(...args) {
|
||
print("WARNING:", ...args);
|
||
}
|
||
|
||
function assert(condition, message, ...dump) {
|
||
if (!condition) {
|
||
throw new Error(message + "\n", dump);
|
||
}
|
||
}
|
||
|
||
function print_stacktrace(error) {
|
||
let trace;
|
||
if (!error) {
|
||
trace = (new Error()).stack.split("\n")
|
||
// Remove _this_ frame
|
||
trace.splice(0, 1);
|
||
} else {
|
||
trace = error.stack.split("\n");
|
||
}
|
||
// Remove some uninteresting frames
|
||
let filtered = trace.filter((frame) => {
|
||
return frame !== "wrapper@resource:///org/gnome/gjs/modules/lang.js:178"
|
||
});
|
||
let args = [...arguments];
|
||
args.splice(0, 1, "stacktrace:"+(args[0] ? args[0] : ""))
|
||
// Use non-breaking space to encode new lines (otherwise every frame is
|
||
// prefixed by timestamp)
|
||
let nl = " ";
|
||
args.push(nl+filtered.join(nl))
|
||
debug.apply(null, args);
|
||
}
|
||
|
||
function framestr(rect) {
|
||
return "[ x:"+rect.x + ", y:" + rect.y + " w:" + rect.width + " h:"+rect.height + " ]";
|
||
}
|
||
|
||
/**
|
||
* Look up the function by name at call time. This makes it convenient to
|
||
* redefine the function without re-registering all signal handler, keybindings,
|
||
* etc. (this is like a function symbol in lisp)
|
||
*/
|
||
function dynamic_function_ref(handler_name, owner_obj) {
|
||
owner_obj = owner_obj || window;
|
||
return function() {
|
||
owner_obj[handler_name].apply(this, arguments);
|
||
}
|
||
}
|
||
|
||
function swap(array, i, j) {
|
||
let temp = array[i];
|
||
array[i] = array[j];
|
||
array[j] = temp;
|
||
}
|
||
function in_bounds(array, i) {
|
||
return i >= 0 && i < array.length;
|
||
}
|
||
|
||
function isPointInsideActor(actor, x, y) {
|
||
return (actor.x <= x && x <= actor.x+actor.width)
|
||
&& (actor.y <= y && y <= actor.y+actor.height);
|
||
}
|
||
|
||
|
||
//// Debug and development utils
|
||
|
||
const Tiling = Extension.imports.tiling;
|
||
|
||
function setDevGlobals() {
|
||
// Accept the risk of this interfering with existing code for now
|
||
metaWindow = display.focus_window;
|
||
meta_window = display.focus_window;
|
||
workspace = screen.get_active_workspace();
|
||
actor = metaWindow.get_compositor_private();
|
||
space = Tiling.spaces.spaceOfWindow(metaWindow);
|
||
}
|
||
|
||
/**
|
||
* Visualize the frame and buffer bounding boxes of a meta window
|
||
*/
|
||
function toggleWindowBoxes(metaWindow) {
|
||
metaWindow = metaWindow || display.focus_window;
|
||
|
||
if(metaWindow._paperDebugBoxes) {
|
||
metaWindow._paperDebugBoxes.forEach(box => {
|
||
box.destroy();
|
||
});
|
||
delete metaWindow._paperDebugBoxes;
|
||
return [];
|
||
}
|
||
|
||
let frame = metaWindow.get_frame_rect()
|
||
let inputFrame = metaWindow.get_buffer_rect()
|
||
let actor = metaWindow.get_compositor_private();
|
||
|
||
makeFrameBox = function({x, y, width, height}, color) {
|
||
let frameBox = new imports.gi.St.Widget();
|
||
frameBox.set_position(x, y)
|
||
frameBox.set_size(width, height)
|
||
frameBox.set_style("border: 2px" + color + " solid");
|
||
return frameBox;
|
||
}
|
||
|
||
let boxes = [];
|
||
|
||
boxes.push(makeFrameBox(frame, "red"));
|
||
boxes.push(makeFrameBox(inputFrame, "blue"));
|
||
|
||
if(inputFrame.x !== actor.x || inputFrame.y !== actor.y
|
||
|| inputFrame.width !== actor.width || inputFrame.height !== actor.height) {
|
||
boxes.push(makeFrameBox(actor, "yellow"));
|
||
}
|
||
|
||
boxes.forEach(box => global.stage.add_actor(box));
|
||
|
||
metaWindow._paperDebugBoxes = boxes;
|
||
return boxes;
|
||
}
|
||
|
||
var markNewClonesSignalId = null;
|
||
function toggleCloneMarks() {
|
||
// NB: doesn't clean up signal on disable
|
||
|
||
function markCloneOf(metaWindow) {
|
||
if (metaWindow.clone)
|
||
metaWindow.clone.opacity = 190;
|
||
}
|
||
function unmarkCloneOf(metaWindow) {
|
||
if (metaWindow.clone)
|
||
metaWindow.clone.opacity = 255;
|
||
}
|
||
|
||
let windows = display.get_tab_list(Meta.TabList.NORMAL_ALL, null);
|
||
|
||
if (markNewClonesSignalId) {
|
||
display.disconnect(markNewClonesSignalId);
|
||
markNewClonesSignalId = null;
|
||
windows.forEach(unmarkCloneOf);
|
||
} else {
|
||
markNewClonesSignalId = display.connect_after(
|
||
"window-created", (_, mw) => markCloneOf(mw))
|
||
|
||
windows.forEach(markCloneOf);
|
||
}
|
||
}
|
||
|
||
|
||
function sum(array) {
|
||
return array.reduce((a, b) => a + b, 0);
|
||
}
|
||
|
||
function setWorkspaceName(name, workspace) {
|
||
let i;
|
||
if (workspace === undefined) {
|
||
i = screen.get_active_workspace_index();
|
||
} else {
|
||
i = workspace.index();
|
||
}
|
||
let settings = new Gio.Settings({ schema_id:
|
||
'org.gnome.desktop.wm.preferences'});
|
||
let names = settings.get_strv('workspace-names');
|
||
|
||
let oldName = names[i];
|
||
names[i] = name;
|
||
settings.set_strv('workspace-names', names);
|
||
|
||
return oldName;
|
||
}
|
||
|
||
function getPointerPosition() {
|
||
let display = Gdk.Display.get_default();
|
||
let deviceManager = display.get_device_manager();
|
||
let pointer = deviceManager.get_client_pointer();
|
||
let [gdkscreen, x, y] = pointer.get_position();
|
||
return [x, y, gdkscreen, pointer]
|
||
}
|
||
|
||
function warpPointer(x, y) {
|
||
let display = Gdk.Display.get_default();
|
||
let deviceManager = display.get_device_manager();
|
||
let pointer = deviceManager.get_client_pointer();
|
||
pointer.warp(Gdk.Screen.get_default(), x, y)
|
||
}
|
||
|
||
class Signals extends Map {
|
||
static get [Symbol.species]() { return Map; }
|
||
|
||
connect(object, signal, handler) {
|
||
let signals = this.get(object);
|
||
if (!signals) {
|
||
signals = [];
|
||
this.set(object, signals);
|
||
}
|
||
let id = object.connect(signal, handler);
|
||
signals.push(id);
|
||
return id;
|
||
}
|
||
|
||
disconnect(object) {
|
||
let ids = this.get(object);
|
||
if (ids) {
|
||
ids.forEach(id => object.disconnect(id));
|
||
this.delete(object);
|
||
}
|
||
}
|
||
|
||
destroy() {
|
||
for (let [object, signals] of this) {
|
||
signals.forEach(id => object.disconnect(id));
|
||
this.delete(object);
|
||
}
|
||
}
|
||
}
|