summaryrefslogtreecommitdiff
path: root/cmd/plctl/plctl.ha
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/plctl/plctl.ha')
-rw-r--r--cmd/plctl/plctl.ha190
1 files changed, 190 insertions, 0 deletions
diff --git a/cmd/plctl/plctl.ha b/cmd/plctl/plctl.ha
new file mode 100644
index 0000000..d559b4d
--- /dev/null
+++ b/cmd/plctl/plctl.ha
@@ -0,0 +1,190 @@
+use fmt;
+use strings;
+use os;
+use os::exec;
+use io;
+use encoding::utf8;
+
+type listcmd = enum {
+ PLAYERS,
+ TITLES,
+};
+
+def PLAYERSARGS: [2]str = ["playerctl", "-l"];
+def TITLESARGS: [4]str = ["playerctl", "-a", "metadata", "title"];
+
+type mapping = struct {
+ player: str,
+ title: str,
+};
+
+type err = !(!exec::exit_status | utf8::invalid | io::error | exec::error | nomem);
+type simpleerr = !(exec::error | !exec::exit_status | nomem);
+type mismatch = !void;
+
+export fn main() void = {
+ const map = getmap()!;
+ //for (let mapp .. map) {
+ // fmt::println(mapp.player)!;
+ // fmt::println(mapp.title)!;
+ //};
+ defer finish(map);
+
+ if (len(map) == 0) {
+ return;
+ };
+
+ if (len(map) == 1) {
+ toggle(map[0])!;
+ return;
+ };
+
+ const m = match (choice(map)) {
+ case let m: mapping =>
+ yield m;
+ case void =>
+ return;
+ case let e: err =>
+ handleerr(e);
+ };
+
+ toggle(m)!;
+};
+
+fn toggle(m: mapping) (void | simpleerr) = {
+ let cmd = exec::cmd("playerctl", "-p", m.player, "play-pause")?;
+
+ let proc = exec::start(&cmd)?;
+
+ let status = exec::wait(&proc)?;
+ const st = match (exec::check(&status)) {
+ case let e: !exec::exit_status =>
+ return e;
+ case void => void;
+ };
+};
+
+fn choice(map: []mapping) (mapping | void | err) = {
+ let cmd = exec::cmd("wmenu")?;
+
+ const (read, write) = exec::pipe();
+ const (read1, write1) = exec::pipe();
+ exec::addfile(&cmd, os::stdin_file, read1)?;
+ exec::addfile(&cmd, os::stdout_file, write)?;
+ let proc = exec::start(&cmd)?;
+
+ io::close(read1)?;
+ io::close(write)?;
+ for (let mapp .. map) {
+ io::writeall(write1, strings::toutf8(mapp.title))?;
+ io::writeall(write1, strings::toutf8("\n"))?;
+ };
+ io::close(write1)?;
+
+ let data = io::drain(read)?;
+ defer free(data);
+ io::close(read)?;
+ let status = exec::wait(&proc)?;
+
+ const st = match (exec::check(&status)) {
+ case let e: !exec::exit_status =>
+ return e;
+ case void => void;
+ };
+
+ let s = strings::trim(strings::fromutf8(data)?);
+ for (let mapp .. map) {
+ if (mapp.title == s) {
+ return mapp;
+ };
+ };
+ return void;
+};
+
+fn finish(map: []mapping) void = {
+ for (let mapp .. map) {
+ free(mapp.player);
+ free(mapp.title);
+ };
+ free(map);
+};
+
+fn handleerr(e: err) never = {
+ match (e) {
+ case let e: utf8::invalid =>
+ fmt::fatal(utf8::strerror(e));
+ case let e: io::error =>
+ fmt::fatal(io::strerror(e));
+ case let e: exec::error =>
+ fmt::fatal(exec::strerror(e));
+ case nomem =>
+ fmt::fatal("No memory left");
+ case let e: !exec::exit_status =>
+ fmt::fatal(exec::exitstr(e));
+ };
+};
+
+fn getmap() ([]mapping | err | mismatch) = {
+ const pls = match (getlist(listcmd::PLAYERS)) {
+ case let s: []str =>
+ yield s;
+ case let e: err =>
+ handleerr(e);
+ };
+ defer strings::freeall(pls);
+
+ const titles = match (getlist(listcmd::TITLES)) {
+ case let s: []str =>
+ yield s;
+ case let e: err =>
+ handleerr(e);
+ };
+ defer strings::freeall(titles);
+
+ if (len(pls) != len(titles)) {
+ return mismatch;
+ };
+
+ let map: []mapping = [];
+ for (let i = 0z; i < len(pls); i += 1) {
+ append(map, mapping {
+ player = strings::dup(pls[i])?,
+ title = strings::dup(titles[i])?,
+ })?;
+ };
+ return map;
+};
+
+fn argslistcmd(lcmd: listcmd) []str = switch (lcmd) {
+case listcmd::PLAYERS =>
+ yield strings::dupall(PLAYERSARGS)!;
+case listcmd::TITLES =>
+ yield strings::dupall(TITLESARGS)!;
+};
+
+fn getlist(lcmd: listcmd) ([]str | err) = {
+ const args = argslistcmd(lcmd);
+ defer strings::freeall(args);
+ let cmd = exec::cmd(args[0], args[1..]...)?;
+
+ const (read, write) = exec::pipe();
+ exec::addfile(&cmd, os::stdout_file, write)?;
+ let proc = exec::start(&cmd)?;
+ io::close(write)?;
+
+ let data = io::drain(read)?;
+ defer free(data);
+ io::close(read)?;
+ let status = exec::wait(&proc)?;
+
+ const st = match (exec::check(&status)) {
+ case let e: !exec::exit_status =>
+ return e;
+ case void => void;
+ };
+
+ const s = strings::fromutf8(data)?;
+ const spl =strings::split(strings::trim(s), "\n")?;
+ defer free(spl);
+ return strings::dupall(spl)?;
+};