Skip to content

Instantly share code, notes, and snippets.

@unclechu
Last active December 28, 2017 09:40
Show Gist options
  • Select an option

  • Save unclechu/ef3e19cc902f015ce7f484ae685bc74c to your computer and use it in GitHub Desktop.

Select an option

Save unclechu/ef3e19cc902f015ce7f484ae685bc74c to your computer and use it in GitHub Desktop.
kbd.pl6
#!/usr/bin/env perl6
use v6.c;
close $*IN;
my $HOME := IO::Path.new: '/home/unclechu';
my $BINDIR := $HOME.child: '.local/bin';
my $LOCKFILE := %*ENV{'HOME'}.IO.child: '.kbd.pl6.lock';
sub MAIN( Bool :f(:$force) = False
, Bool :v(:$verbose) = False
, Bool :$internal-bg = False
)
{
my &s = &safe-spawn.assuming: $verbose;
if $internal-bg.not {
if !$force && $LOCKFILE.f && how-old-is-lockfile() < 60 {
note "Couldn't start, lock file exists: '$LOCKFILE'!\n"
, "Start with [-f|--force] to force start.";
exit 1;
} else {
$LOCKFILE.IO.open(:w).close;
}
s True, (
qw<sudo -->,
$HOME.child("dev/apple-keyboard/apply-apple-keyboard-configs.sh").Str
);
s True, qw<setxkbmap -layout us,ru -option grp:shifts_toggle>;
s True, qw<numlockx off>;
s True, qw<numlockx on>;
"'$*PROGRAM' will do last part in background…".note if $verbose;
given Proc::Async.new(
:w, $*EXECUTABLE, $*PROGRAM, |@*ARGS.append: '--internal-bg')
{ .start; .close-stdin; exit 0; }
} else {
"'$*PROGRAM' is waiting for promises…".note if $verbose;
await Promise.allof(
start {
"'$*PROGRAM' is restarting 'xbindkeys' in own thread…".note if $verbose;
s False, qw<killall xbindkeys>;
await Promise.in: 1;
s True, qw<xbindkeys>;
"'$*PROGRAM' is done with restarting 'xbindkeys'.".note if $verbose;
},
start {
"'$*PROGRAM' is restarting 'xlib-keys-hack' in own thread…".note
if $verbose;
s False, qw<killall xlib-keys-hack.sh>;
s False, qw<killall xlib-keys-hack>;
await Promise.in: 1;
walking-zombie $verbose, $BINDIR.child: 'xlib-keys-hack.sh';
"'$*PROGRAM' is done with restarting 'xlib-keys-hack'.".note if $verbose;
}
);
"'$*PROGRAM' is done waiting for promises.".note if $verbose;
"'$*PROGRAM' is removing its lockfile: '$LOCKFILE'…".note if $verbose;
$LOCKFILE.unlink;
"'$*PROGRAM' is done.".note if $verbose;
}
}
### helpers ###
sub walking-zombie(Bool $verbose, *@run-args) {
my $cmd = @run-args.join: ' ';
"'$*PROGRAM' is spawning zombie: '$cmd'…".note if $verbose;
given Proc::Async.new(:w, |@run-args)
{ .stdout.tap.close; .stderr.tap.close; .start; .close-stdin; }
"'$*PROGRAM' is done with spawning zombie: '$cmd'…".note if $verbose;
}
sub safe-spawn(Bool $verbose, Bool $warn, *@run-args) {
my $timeout := 2;
my $cmd = @run-args.join: ' ';
("'$*PROGRAM' is spawning: '$cmd'…").note if $verbose;
my &runit = sub (Proc::Async $proc) {
my $promise = $proc.start;
$proc.close-stdin;
return ($proc, $promise);
};
try {
my ($proc, $promise) = runit Proc::Async.new(:w, |@run-args);
await Promise.anyof(
$promise,
Promise.in($timeout).then: {
my $what = (@run-args[0] == 'killall') ?? 'dying' !! 'killing it';
"'$*PROGRAM' command '$cmd' is out of time, $what…".note if $verbose;
$proc.kill: SIGKILL;
if @run-args[0] == 'killall' {
my @args = @run-args;
@args.shift;
@args.prepend('killall', '-KILL');
$cmd = @args.join: ' ';
"'$*PROGRAM' is spawning new killing command '$cmd'…".note
if $verbose;
my ($proc, $promise) = runit Proc::Async.new(:w, |@args);
await Promise.anyof(
$promise,
Promise.in($timeout).then: {
"'$*PROGRAM' command '$cmd' is out of time, dying…".note
if $verbose;
$proc.kill: SIGKILL;
exit 1;
}
);
} else {
exit 1;
}
}
);
CATCH {
default {
"'$*PROGRAM' is failed spawning: '$cmd', error: $_.".note if $verbose;
warn $_ if $warn;
}
}
}
"'$*PROGRAM' is done with spawning: '$cmd'.".note if $verbose;
}
sub how-old-is-lockfile() of Duration {
(run 'stat', $LOCKFILE, :out).out.slurp ~~ /Modify\:\s (.+?) \n/;
$_ = $0.split(' ');
$_ = "$_[0]T" ~ $_[1].split('.')[0] ~ $_[2].subst(/(\d\d)(\d\d)/, {"$0:$1"});
DateTime.now - DateTime.new: $_;
}
# vim:et:ts=2:sts=2:sw=2:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment