mortie 7 роки тому
джерело
коміт
8e0ddc4748
9 змінених файлів з 333 додано та 64 видалено
  1. 20
    0
      README.md
  2. 21
    50
      dedaemon.js
  3. 9
    0
      js/async.js
  4. 56
    0
      js/parse-conf.js
  5. 23
    1
      modules/display/index.js
  6. 17
    11
      modules/input/index.js
  7. 183
    0
      modules/process/index.js
  8. 3
    2
      modules/wallpaper/index.js
  9. 1
    0
      package.json

+ 20
- 0
README.md Переглянути файл

@@ -0,0 +1,20 @@
# DEDaemon

DEDaemon is a daemon to give some of the perks of a full desktop environment to
those of us running window managers.

## Installation

Run `npm install -g dedaemon` as root.

## Usage

`dedaemon <config file>`

e.g:

`dedaemon ~/.config/dedaemon.hcnf`

You probably want to run that on startup. If you're running i3wm, that means
adding `exec --no-startup-id dedaemon ~/.config/dedaemon.hcnf` to
`~/.i3/config`.

+ 21
- 50
dedaemon.js Переглянути файл

@@ -1,55 +1,23 @@
#!/usr/bin/env node

var syscheck = require("./js/syscheck");
var parseConf = require("./js/parse-conf");
var async = require("./js/async");

var modules = {
display: require("./modules/display"),
// display: require("./modules/display"),
input: require("./modules/input"),
wallpaper: require("./modules/wallpaper")
wallpaper: require("./modules/wallpaper"),
process: require("./modules/process"),
};

var config = {
display: [
{
name: "*",
resolution: "max",
rate: "max",
where: { left_of: "primary" },
},
],

input: [
{
type: "pointer",
name: "*",
options: [,
[ "libinput Tapping Enabled", 1 ],
],
},

{
type: "pointer",
name: "Razer Razer Naga",
options: [
[ "libinput Accel Speed", "-0.8" ],
],
},

{
type: "keyboard",
name: "*",
commands: [
"xset r rate 200 60",
"setxkbmap dvorak -option ctrl:swapcaps -option altwin:swap_alt_win",
],
},
],

wallpaper: {
path: "/home/martin/background.jpg",
},
if (!process.argv[2]) {
console.log("Usage:", process.argv[1], "<config>");
process.exit(1);
}

var config = parseConf(process.argv[2]);

function createLogger(name) {
function log(pre, msg) {
console.error(pre+msg.join(" "));
@@ -67,21 +35,21 @@ function startAll() {
var mod = modules[i];
var conf = config[i] || {};

if (conf instanceof Array && conf.length === 0)
return;

mod.start(conf, createLogger(i), modules);
});
}

function stopAll(cb) {
var keys = Object.keys(modules);
var next = async(keys.length, cb);
keys.forEach(i => modules[i].stop(next));
}

var cbs = keys.length;
function next() {
cbs -= 1;
if (cbs === 0)
cb();
}

keys.forEach(i => modules.stop(next));
function onTerm() {
stopAll(() => process.exit(1));
}

syscheck(ok => {
@@ -90,3 +58,6 @@ syscheck(ok => {
else
console.error("Missing binaries, exiting.");
});

process.on("SIGTERM", onTerm);
process.on("SIGINT", onTerm);

+ 9
- 0
js/async.js Переглянути файл

@@ -0,0 +1,9 @@
module.exports = async;

function async(num, cb) {
return function() {
num -= 1;
if (num === 0)
cb();
}
}

+ 56
- 0
js/parse-conf.js Переглянути файл

@@ -0,0 +1,56 @@
var hconfig = require("hconfig");

module.exports = parse;

var configStructure = {
display: {
count: "many",
props: {
name: "string",
resolution: "string",
rate: "string",
where: "object",
},
},
input: {
count: "many",
props: {
name: "string",
type: "string",
commands: "array",
options: "array",
},
},
wallpaper: {
count: "once",
props: {
name: "null",
path: "string",
},
},
process: {
count: "many",
props: {
name: "string",
run: "array",
"in": [ "string", "null" ],
env: [ "object", "null" ],
restart: "bool",
as: [ "string", "null" ],
},
},
}

function parse(file) {
try {
return hconfig.parseConfFile(
file, configStructure);
} catch (err) {
if (err.hconfigParseError) {
console.error(err.message);
process.exit(1);
} else {
throw err;
}
}
}

+ 23
- 1
modules/display/index.js Переглянути файл

@@ -1,3 +1,5 @@
var udev = require("udev");

exports.start = start;
exports.stop = stop;
exports.event = event;
@@ -6,10 +8,27 @@ var conf;
var logger;
var modules;

var monitor;

function onchange(dev, evt) {
if (evt === "add")
logger.info("display added");
else if (evt === "change")
logger.info("display changed");
else
logger.info(dev);

//modules.wallpaper.event("reload");
}

function start(conf_, logger_, modules_) {
conf = conf_ || conf;
logger = logger_ || logger;
modules = modules_ || modules;

monitor = udev.monitor("drm");
monitor.on("add", dev => onchange(dev, "add"));
monitor.on("change", dev => onchange(dev, "change"));
}

function stop(cb) {
@@ -17,5 +36,8 @@ function stop(cb) {
}

function event(name, ...params) {
logger.info("Event", name, params.toString());
switch (name) {
default:
logger.warn("Unknown event:", name);
}
}

+ 17
- 11
modules/input/index.js Переглянути файл

@@ -59,8 +59,8 @@ var runCmds = debounce(function() {

// Devices which aren't keyboards or mice with names aren't interesting
function filter(dev) {
return dev.NAME && dev.SUBSYSTEM === "input" &&
(dev.ID_INPUT_KEYBOARD || dev.ID_INPUT_MOUSE);
return dev.NAME &&
(dev.ID_INPUT_KEYBOARD || dev.ID_INPUT_MOUSE || dev.ID_INPUT_TOUCHPAD);
}

// name can be either an array or a string
@@ -94,13 +94,16 @@ function onchange(dev, evt) {
if (!filter(dev))
return;

var isKeyboard = !!dev.ID_INPUT_KEYBOARD;
var isPointer = !!(dev.ID_INPUT_MOUSE || dev.ID_INPUT_TOUCHPAD);

// Find out what to log
var inputType;
if (dev.ID_INPUT_KEYBOARD && dev.ID_INPUT_MOUSE)
inputType = "keyboard/mouse";
else if (dev.ID_INPUT_KEYBOARD)
if (isKeyboard && isPointer)
inputType = "keyboard/pointer";
else if (isKeyboard)
inputType = "keyboard";
else if (dev.ID_INPUT_MOUSE)
else if (isPointer)
inputType = "mouse";

// Log add/change
@@ -111,9 +114,9 @@ function onchange(dev, evt) {

// Run through and apply relevant rules
conf.forEach(entry => {
if (entry.type === "pointer" && !dev.ID_INPUT_MOUSE)
if (entry.type === "pointer" && !isPointer)
return;
if (entry.type === "keyboard" && !dev.ID_INPUT_KEYBOARD)
if (entry.type === "keyboard" && !isKeyboard)
return;
if (!nameMatches(dev, entry.name))
return;
@@ -148,9 +151,9 @@ function start(conf_, logger_, modules_) {
logger = logger_ || logger;
modules = modules_ || modules;

udev.list().forEach(dev => onchange(dev, "init"));
udev.list("input").forEach(dev => onchange(dev, "init"));

monitor = udev.monitor();
monitor = udev.monitor("input");
monitor.on("add", dev => onchange(dev, "add"));
monitor.on("change", dev => onchange(dev, "change"));
}
@@ -161,5 +164,8 @@ function stop(cb) {
}

function event(name, ...params) {
logger.info("Event", name, params.toString());
switch (name) {
default:
logger.warn("Unknown event: "+name);
}
}

+ 183
- 0
modules/process/index.js Переглянути файл

@@ -0,0 +1,183 @@
var spawn = require("child_process").spawn;
var async = require("../../js/async");

exports.start = start;
exports.stop = stop;
exports.event = event;

var conf;
var logger;
var modules;

class Process {
constructor(id, cmd, options) {
this.id = id;
this.restart = options.restart;
this.stopping = false;
this.running = false;
this.restarts = 0;

this.name = cmd[0];
cmd.shift();
this.args = cmd;
this.childOpts = {
env: options.env,
cwd: options.cwd,
};

this.info = logger.info.bind(logger, this.id+":");
this.warn = logger.warn.bind(logger, this.id+":");
}

logOutput(stream, data) {
data.toString()
.split("\n")
.map(line => line.trim())
.forEach(line => {
if (line === "") return;
this.info(stream+":", line);
});
}

onexit() {
if (!this.restart)
return;

if (this.restarts < 2) {
this.restarts += 1;
var restarts = this.restarts;
this.info("Restarting in 2 seconds.");
setTimeout(() => {
this.start();
this.restarts = restarts;
}, 2000);
} else {
this.warn("Not restarting anymore after 2 restarts.");
}
}

start() {
this.stopping = false;
this.running = true;
this.restarts = 0;

this.child = spawn(this.name, this.args, this.childOpts);

this.child.stdout.on("data",
d => this.logOutput("stdout", d));
this.child.stderr.on("data",
d => this.logOutput("stderr", d));

this.child.once("error", err => {
if (!this.stopping)
this.warn("Failed to start:", err);

this.onexit();
});
this.child.once("close", code => {
this.running = false;
if (this.stopping)
return;

if (code === 0)
this.info("Exited with status code 0");
else
this.warn("Exited with status code", code);
this.onexit();
});
}

stop(cb) {
this.stopping = true;
if (!this.running)
return cb();

this.info("Sending SIGTERM.");
this.child.kill("SIGTERM");

setTimeout(() => {
if (this.running) {
this.info("Sending SIGKILL.");
this.child.kill("SIGKILL");
}

cb();
}, 1000);
}
}

class ProcessGroup {
constructor(id, cmds, options) {
this.procs = [];
cmds.forEach(cmd => {
var name = cmd[0];
this.procs.push(new Process(id+"("+name+")", cmd, options));
});
}

start() {
this.procs.forEach(p => p.start());
}

stop(cb) {
var next = async(this.procs.length, cb);
this.procs.forEach(p => p.stop(next));
}
}

var procs = {};

function start(conf_, logger_, modules_) {
conf = conf_ || conf;
logger = logger_ || logger;
modules = modules_ || modules;

conf.forEach(proc => {
if (procs[proc.name])
return logger.warn("Igonring duplicate process: "+proc.name);

var env = null;
if (proc.env) {
env = {};
for (var i in process.env) {
env[i] = process.env[i];
}
for (var i in proc.env) {
env[i] = proc.env[i];
}
}

var opts = {
cwd: proc.in,
env: env,
restart: !!proc.restart,
};

var p;
if (!proc.as || proc.as === "process") {
p = new Process(proc.name, proc.run, opts);
} else if (proc.as === "group") {
p = new ProcessGroup(proc.name, proc.run, opts);
} else {
return logger.warn(
proc.name+":",
"Invalid 'as' attribute:",
proc.as);
}
p.start();
procs[proc.name] = p;
});
}

function stop(cb) {
var keys = Object.keys(procs);
var next = async(keys.length, cb);
keys.forEach(i => procs[i].stop(next));
}

function event(name, ...params) {
switch (name) {
default:
logger.warn("Unknown event: "+name);
}
}

+ 3
- 2
modules/wallpaper/index.js Переглянути файл

@@ -78,11 +78,12 @@ function stop(cb) {
}

function event(name, ...params) {
logger.info("Event", name, params.toString());

switch (name) {
case "reload":
runFeh();
break;

default:
logger.warn("Unknown event:", name);
}
}

+ 1
- 0
package.json Переглянути файл

@@ -13,6 +13,7 @@
"author": "Martin Dørum Nygaard <martid0311@gmail.com> (http://mort.coffee)",
"license": "ISC",
"dependencies": {
"hconfig": "^0.4.0",
"udev": "^0.4.0"
},
"bin": {

Завантаження…
Відмінити
Зберегти