204 lines
4.9 KiB
Raku
Executable file
204 lines
4.9 KiB
Raku
Executable file
#!/usr/bin/env raku
|
||
|
||
#| Sets up buttons etc on drawing tablets
|
||
unit sub MAIN(
|
||
#| Don't run the commands, just print them (implies -v)
|
||
Bool :n(:$dry-run),
|
||
#| Don't turn on the screens
|
||
Bool :S(:$no-screens),
|
||
#| Print each command before running it
|
||
Bool :v(:$verbose) is copy,
|
||
#| Print the output of queries too (implies -v)
|
||
Bool :V(:$very-verbose),
|
||
);
|
||
|
||
$verbose ||= $dry-run || $very-verbose;
|
||
|
||
|
||
sub say-prefix(:$prefix = '### ', |c) {
|
||
say $prefix, |c
|
||
}
|
||
|
||
sub say-vv(:$prefix = '### ', |c) {
|
||
say-prefix :$prefix, |c if $very-verbose
|
||
}
|
||
|
||
sub say-v(:$prefix = '### ', |c) {
|
||
say-prefix :$prefix, |c if $verbose
|
||
}
|
||
|
||
sub quote(Str() $_) {
|
||
when '' { '""' }
|
||
when /\s/ { qq|"$_"| }
|
||
default { $_ }
|
||
}
|
||
|
||
sub say-cmd(*@cmd, Bool() :$query!) {
|
||
my $prefix = $dry-run && !$query ?? "# " !! "> ";
|
||
say-v :$prefix, @cmd»."e.join(' ');
|
||
}
|
||
|
||
sub query(*@cmd) {
|
||
say-cmd @cmd, :query;
|
||
my @out = run(|@cmd, :out :!in).out.lines(:close);
|
||
say-vv :prefix('< '), $_ for @out;
|
||
@out
|
||
}
|
||
|
||
sub update(*@cmd) {
|
||
say-cmd @cmd, :!query;
|
||
unless $dry-run { run(|@cmd, :!out :!in :err) }
|
||
Nil
|
||
}
|
||
|
||
|
||
class Subdev { ... }
|
||
|
||
class Tablet {
|
||
has Str $.name;
|
||
|
||
my @devices = lazy query <xsetwacom --list devices>;
|
||
|
||
method Str { $.name }
|
||
|
||
method find($name) {
|
||
for @devices {
|
||
when /^ $<name>=(.* $name .*) «Pen»/ {
|
||
return self.new: name => $<name>.trim;
|
||
}
|
||
}
|
||
fail "!!! Tablet '$name' not found";
|
||
}
|
||
|
||
method subdev($type, $name) { Subdev.new: tablet => self, :$type, :$name; }
|
||
|
||
method pad() { self.subdev: 'Pad', 'pad' }
|
||
method stylus() { self.subdev: 'Pen', 'stylus' }
|
||
method eraser() { self.subdev: 'Pen', 'eraser' }
|
||
method cursor() { self.subdev: 'Pen', 'cursor' }
|
||
}
|
||
|
||
class Subdev {
|
||
has Tablet $.tablet; has Str $.type; has Str $.name;
|
||
|
||
method Str { "$.tablet $.type $.name" }
|
||
|
||
method set(*%params-args) {
|
||
for %params-args.kv -> $param, $args {
|
||
update <xsetwacom --set>, self, $param, |$args;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
sub key($k, *%mods where *.values.all) {
|
||
my @mods = %mods.keys.map: {
|
||
when /:i alt [_ | \-]? gr/ { 'ISO_Level3_Shift' }
|
||
default { $_ }
|
||
};
|
||
"key " ~ reduce { "+$^b $^a -$^b" }, $k, |@mods
|
||
}
|
||
|
||
|
||
sub external-connected {
|
||
for query <xrandr --listmonitors> {
|
||
when /^\h* \d+ ':' \h* '+'? '*'? $<name>=(\S+)/ {
|
||
my $name = ~$<name>;
|
||
return $name unless $name ~~ /eDP/;
|
||
}
|
||
}
|
||
}
|
||
|
||
################################################################################
|
||
|
||
my $did-something = False;
|
||
|
||
my $external = external-connected;
|
||
|
||
with $external { say-v "using display '$_'" }
|
||
|
||
with Tablet.find: 'Intuos4' {
|
||
say-v "Setting up \"$_\"";
|
||
$did-something = True;
|
||
|
||
my $screen-dir = query(<find /sys/devices -name wacom_led>)[0];
|
||
with $screen-dir {
|
||
say-v "wacom_led directory: {quote($screen-dir)}";
|
||
} else {
|
||
fail "!!! wacom_led directory not found";
|
||
}
|
||
|
||
for .stylus, .eraser, .cursor {
|
||
.set: :Rotate<half> :Area<0 0 65024 36576>; # full 65024x40640
|
||
|
||
if defined $external { .set: :MapToOutput($external); }
|
||
}
|
||
|
||
for .stylus, .eraser {
|
||
.set: :Button(3, key(:ctrl, 'z'));
|
||
}
|
||
|
||
my %buttons = (
|
||
# assuming left handed
|
||
# circle
|
||
1 => {map => key('tab')},
|
||
# upper four, top to bottom
|
||
13 => {map => key('shift'), screen => 7, label => "🠙 \xF0E2"}, # rotate
|
||
12 => {map => key('ctrl'), screen => 6, label => "⌃ \xF002"}, # magnifying glass
|
||
11 => {map => key('m'), screen => 5, label => '⌽'},
|
||
10 => {map => key('5'), screen => 4, label => '⦜'},
|
||
# lower four
|
||
9 => {map => key('.'), screen => 3, label => '●'},
|
||
8 => {map => key(','), screen => 2, label => '⋅'},
|
||
3 => {map => key('insert'), screen => 1, label => '⏥'},
|
||
2 => {map => key(:ctrl, <'>), screen => 0, label => '𝞪'},
|
||
);
|
||
|
||
update <sudo i4oled-chgrp>, $screen-dir;
|
||
|
||
spurt "$screen-dir/buttons_luminance", ($no-screens ?? 0 !! 8);
|
||
|
||
given .pad {
|
||
.set: :AbsWheelDown(key('shift')) :AbsWheelUp(key('shift'));
|
||
|
||
for %buttons.kv -> $button, % (:$map, :$screen, :$label) {
|
||
.set: :Button($button, $map);
|
||
|
||
next if $no-screens or not defined $label & $screen;
|
||
|
||
my $path = "$screen-dir/button{$screen}_rawimg";
|
||
fail "!!! no such device $path" unless $path.IO.f;
|
||
update <i4oled -l -t>, $label, '-d', $path;
|
||
}
|
||
}
|
||
}
|
||
|
||
with Tablet.find: 'H420' {
|
||
say-v "Setting up \"$_\"";
|
||
$did-something = True;
|
||
|
||
given .stylus {
|
||
.set: :Rotate<half> :Area<0 0 8320 4680> # full: 8340x4680
|
||
:PressureCurve<0 50 50 100> :Button(3, key(:ctrl, 'z'));
|
||
|
||
# FIXME do this properly
|
||
.set: :MapToOutput<960x540+480+270>;
|
||
|
||
if defined $external { .set: :MapToOutput($external); }
|
||
}
|
||
|
||
my %buttons = (
|
||
# top to bottom assuming left handed
|
||
3 => key(:ctrl, 'z'),
|
||
2 => key(:ctrl:shift, 'z'),
|
||
1 => key('tab'),
|
||
);
|
||
|
||
for %buttons.kv -> $b, $map {
|
||
.pad.set: :Button($b, $map);
|
||
}
|
||
}
|
||
|
||
say "Didn't find any tablets" unless $did-something;
|
||
|
||
# vim: set ft=raku :
|