mirror of
https://github.com/Bismuth-Forge/bismuth.git
synced 2024-09-17 11:37:10 +03:00
Initial implementation
This commit is contained in:
parent
fde5461eb7
commit
0a4905baa2
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Eon S. Jeon
|
||||
Copyright (c) 2018 Eon S. Jeon <esjeon@hyunmu.am>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
Kröhnkite
|
||||
=========
|
||||
|
||||
A dynamic tiling extension for KWin.
|
||||
|
10
src/combine.sh
Executable file
10
src/combine.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -xe
|
||||
|
||||
(
|
||||
cat engine.js;
|
||||
cat main.js;
|
||||
) \
|
||||
| grep -v '^\s*\/\/' \
|
||||
> combined.js
|
179
src/engine.js
Normal file
179
src/engine.js
Normal file
@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2018 Eon S. Jeon <esjeon@hyunmu.am>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
function Screen(id) {
|
||||
this.id = id;
|
||||
this.layout = layout_tile; //null;
|
||||
this.opts = {}
|
||||
}
|
||||
|
||||
function Tile(client) {
|
||||
this.client = client;
|
||||
this.isNew = true;
|
||||
this.isError = false;
|
||||
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
}
|
||||
|
||||
// TODO: declare Layout class (`layout.js`?)
|
||||
// TODO: layouts in separate file(s)
|
||||
function layout_tile(tiles, areaWidth, areaHeight, opts) {
|
||||
if(!opts.tile_ratio) opts.tile_ratio = 0.45;
|
||||
if(!opts.tile_nmaster) opts.tile_nmaster = 1;
|
||||
|
||||
var masterCount, masterWidth, masterHeight;
|
||||
var stackCount , stackWidth, stackHeight, stackX;
|
||||
|
||||
if(tiles.length <= opts.tile_nmaster) {
|
||||
masterCount = tiles.length;
|
||||
masterWidth = areaWidth;
|
||||
masterHeight = Math.floor(areaHeight / masterCount);
|
||||
|
||||
stackCount = stackWidth = stackHeight = stackX = 0;
|
||||
} else {
|
||||
masterCount = opts.tile_nmaster;
|
||||
masterWidth = Math.floor(areaWidth * (1 - opts.tile_ratio));
|
||||
masterHeight = Math.floor(areaHeight / masterCount);
|
||||
|
||||
stackCount = tiles.length - masterCount;
|
||||
stackWidth = areaWidth - masterWidth;
|
||||
stackHeight = Math.floor(areaHeight / stackCount);
|
||||
stackX = masterWidth + 1;
|
||||
}
|
||||
|
||||
for(var i = 0; i < masterCount; i++) {
|
||||
tiles[i].x = 0;
|
||||
tiles[i].y = masterHeight * i;
|
||||
tiles[i].width = masterWidth;
|
||||
tiles[i].height = masterHeight;
|
||||
}
|
||||
|
||||
for(var i = 0; i < stackCount; i++) {
|
||||
var j = masterCount + i;
|
||||
tiles[j].x = stackX;
|
||||
tiles[j].y = stackHeight * i;
|
||||
tiles[j].width = stackWidth;
|
||||
tiles[j].height = stackHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function TilingEngine() {
|
||||
var self = this;
|
||||
|
||||
self.tiles = Array();
|
||||
self.screens = Array();
|
||||
|
||||
self.manage = function(client, delayed) {
|
||||
var delayed = delayed? true: false;
|
||||
|
||||
if(client.specialWindow)
|
||||
return false;
|
||||
|
||||
self.tiles.push(new Tile(client));
|
||||
|
||||
if(!delayed)
|
||||
self.arrange();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
self.unmanage = function(client) {
|
||||
self.tiles = self.tiles
|
||||
.filter(function(t) {
|
||||
return t.client != client && !t.isError;
|
||||
});
|
||||
self.arrange();
|
||||
}
|
||||
|
||||
self.arrange = function() {
|
||||
self.screens.forEach(function(screen) {
|
||||
if(screen.layout === null)
|
||||
return;
|
||||
|
||||
var desktop = workspace.currentDesktop;
|
||||
|
||||
// TODO: move direct calls to KWin to driver.
|
||||
var area = workspace.clientArea(KWin.PlacementArea, screen.id, desktop);
|
||||
var visibles = self.tiles
|
||||
.filter(function(t) {
|
||||
var c = t.client
|
||||
try {
|
||||
// TODO: test KWin::Toplevel properties...?
|
||||
return (
|
||||
(!c.minimized) &&
|
||||
(c.desktop == desktop || c.desktop == -1) &&
|
||||
(c.screen == screen.id)
|
||||
);
|
||||
} catch(e) {
|
||||
t.isError = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// TODO: fullscreen handling
|
||||
|
||||
screen.layout(visibles, area.width, area.height, {});
|
||||
|
||||
// TODO: move direct calls to KWin to driver.
|
||||
visibles.forEach(function(tile) {
|
||||
tile.client.geometry = {
|
||||
x: tile.x,
|
||||
y: tile.y,
|
||||
width: tile.width,
|
||||
height: tile.height,
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
self.arrangeClient = function(client) {
|
||||
// TODO: move direct calls to KWin to driver.
|
||||
self.tiles.forEach(function(tile) {
|
||||
if(tile.client != client) return;
|
||||
|
||||
if(client.geometry.x == tile.x)
|
||||
if(client.geometry.y == tile.y)
|
||||
if(client.geometry.width == tile.width)
|
||||
if(client.geometry.height == tile.height)
|
||||
return;
|
||||
|
||||
tile.client.geometry = {
|
||||
x: tile.x,
|
||||
y: tile.y,
|
||||
width: tile.width,
|
||||
height: tile.height,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.addScreen = function(screenId) {
|
||||
self.screens.push(new Screen(screenId));
|
||||
}
|
||||
|
||||
self.removeScreen = function(screenId) {
|
||||
self.screens = self.screens
|
||||
.filter(function(screen) {
|
||||
return screen.id !== screenId;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
92
src/main.js
Normal file
92
src/main.js
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2018 Eon S. Jeon <esjeon@hyunmu.am>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
function KWinDriver() {
|
||||
var self = this;
|
||||
var engine = null;
|
||||
|
||||
self._onClientAdded = function(client) {
|
||||
print("clientAdded " + client);
|
||||
// TODO: check resourceClasses for some windows
|
||||
if(engine.manage(client)) {
|
||||
client.desktopChanged.connect(engine.arrange);
|
||||
}
|
||||
client.geometryChanged.connect(function() {
|
||||
if(client.move || client.resize) return;
|
||||
print("geometryChanged " + client);
|
||||
engine.arrangeClient(client);
|
||||
});
|
||||
client.moveResizedChanged.connect(function() {
|
||||
if(client.move || client.resize) return;
|
||||
engine.arrange();
|
||||
});
|
||||
print(" -> numTiles=" + engine.tiles.length);
|
||||
};
|
||||
|
||||
self._onClientRemoved = function(client) {
|
||||
/* XXX: This is merely an attempt to remove the exited client.
|
||||
* Sometimes, the client is not found in the tile list, and causes an
|
||||
* exception in `engine.arrange`.
|
||||
*/
|
||||
print("clientRemoved " + client);
|
||||
engine.unmanage(client);
|
||||
print(" -> numTiles=" + engine.tiles.length);
|
||||
};
|
||||
|
||||
self._onNumberScreensChanged = function(count) {
|
||||
print("numberScreenChanged " + count);
|
||||
while(engine.screens.length < count)
|
||||
engine.addScreen(engine.screens.length);
|
||||
while(engine.screens.length > count)
|
||||
engine.removeScreen(engine.screens.length - 1);
|
||||
};
|
||||
|
||||
self.main = function() {
|
||||
engine = new TilingEngine(self);
|
||||
|
||||
workspace.clientAdded.connect(self._onClientAdded);
|
||||
workspace.clientRemoved.connect(self._onClientRemoved);
|
||||
workspace.numberScreensChanged.connect(self._onNumberScreensChanged);
|
||||
|
||||
workspace.clientMinimized.connect(engine.arrange);
|
||||
workspace.clientUnminimized.connect(engine.arrange);
|
||||
workspace.currentDesktopChanged.connect(engine.arrange);
|
||||
|
||||
// TODO: store screen size in engine?
|
||||
workspace.screenResized.connect(engine.arrange);
|
||||
|
||||
// TODO: handle workspace.clientMaximizeSet signal
|
||||
// TODO: handle workspace.clientFullScreenSet signal
|
||||
// TODO: handle workspace.currentActivityChanged signal
|
||||
// TODO: handle workspace.activitiesChanged signal
|
||||
// TODO: handle workspace.activityAdded signal
|
||||
// TODO: handle workspace.activityRemoved signal
|
||||
// TODO: handle workspace.numberDesktopsChanged signal(???)
|
||||
|
||||
workspace.clientManaging.connect(function(client) {
|
||||
print("clientManaging " + client);
|
||||
});
|
||||
|
||||
self._onNumberScreensChanged(workspace.numScreens);
|
||||
workspace.clientList().map(self._onClientAdded);
|
||||
}
|
||||
}
|
||||
|
||||
(new KWinDriver()).main();
|
Loading…
Reference in New Issue
Block a user