| File: | blib/lib/Linux/Input/Capabilities/Dev.pm |
| Coverage: | 86.9% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Linux::Input::Capabilities::Dev; | ||||||
| 2 | #ABSTRACT: Capabilities of a single input device | ||||||
| 3 | 2 2 | 20 3 | use 5.028; | ||||
| 4 | 2 2 2 | 6 2 26 | use strict; | ||||
| 5 | 2 2 2 | 4 1 37 | use warnings; | ||||
| 6 | 2 2 2 | 465 9336 4 | use Moo; | ||||
| 7 | 2 2 2 | 1736 1037 55 | use Bit::Vector; | ||||
| 8 | 2 2 2 | 7 2 57 | use Carp; | ||||
| 9 | 2 2 2 | 5 2 114 | use File::chdir; | ||||
| 10 | 2 2 2 | 378 19889 72 | use File::Slurper qw(read_text); | ||||
| 11 | 2 2 2 | 736 2 554 | use Linux::Input::Capabilities::Constants qw(%_REVERSE_TABLE); | ||||
| 12 | 2 2 2 | 385 18021 4 | use namespace::autoclean; | ||||
| 13 | 2 2 2 | 87 12 80 | use warnings::register; | ||||
| 14 | 2 2 2 | 6 2 7 | use autodie qw(opendir readdir closedir); | ||||
| 15 | |||||||
| 16 | has name => ( | ||||||
| 17 | is => 'rwp', | ||||||
| 18 | init_arg => undef, | ||||||
| 19 | ); | ||||||
| 20 | |||||||
| 21 | has _bits => ( | ||||||
| 22 | is => 'rw', | ||||||
| 23 | trigger => sub { shift->_clear_decoded }, | ||||||
| 24 | ); | ||||||
| 25 | |||||||
| 26 | has _decoded => ( | ||||||
| 27 | is => 'ro', | ||||||
| 28 | lazy => 1, | ||||||
| 29 | clearer => '_clear_decoded', | ||||||
| 30 | 1 | 8 | builder => sub { +{ } }, | ||||
| 31 | ); | ||||||
| 32 | |||||||
| 33 | has _dir => ( | ||||||
| 34 | is => 'ro', | ||||||
| 35 | init_arg => 'dir', | ||||||
| 36 | required => 1, | ||||||
| 37 | ); | ||||||
| 38 | |||||||
| 39 | sub BUILD { | ||||||
| 40 | 6 | 0 | 960 | my ($self, $args) = @_; | |||
| 41 | 6 | 12 | local $CWD = $CWD; | ||||
| 42 | |||||||
| 43 | 6 | 130 | push @CWD, $self->_dir; | ||||
| 44 | 6 | 273 | chomp(my $name = read_text('name')); | ||||
| 45 | 6 | 217 | $self->_set_name($name); | ||||
| 46 | |||||||
| 47 | 6 | 7 | push @CWD, 'capabilities'; | ||||
| 48 | 6 | 250 | opendir my $dh, '.'; | ||||
| 49 | 6 | 490 | my @entries = readdir $dh; | ||||
| 50 | 6 | 452 | closedir $dh; | ||||
| 51 | |||||||
| 52 | 6 | 408 | my %bits; | ||||
| 53 | 6 | 7 | foreach my $ent (@entries) { | ||||
| 54 | 66 | 82 | next if $ent =~ /^\./; | ||||
| 55 | 54 | 101 | if ($ent =~ /^(abs|ev|ff|key|led|msc|rel|snd|sw)$/n) { | ||||
| 56 | 54 | 58 | $bits{$ent} = _parse_bits(read_text($ent)); | ||||
| 57 | } else { | ||||||
| 58 | 0 | 0 | warnif("Unknown sysfs capability type '$ent' ignored; module may need updating to support it"); | ||||
| 59 | } | ||||||
| 60 | } | ||||||
| 61 | |||||||
| 62 | 6 | 52 | $self->_bits(\%bits); | ||||
| 63 | } | ||||||
| 64 | |||||||
| 65 | sub get_supported { | ||||||
| 66 | 2 | 1 | 4 | my ($self, $type) = @_; | |||
| 67 | 2 | 2 | local $_; | ||||
| 68 | |||||||
| 69 | 2 | 14 | my $v = $self->_bits->{$type} | ||||
| 70 | or croak "Unknown type '$type'"; | ||||||
| 71 | |||||||
| 72 | 2 | 24 | unless (exists $self->_decoded->{$type}) { | ||||
| 73 | $self->_decoded->{$type} = [ | ||||||
| 74 | 2 | 74 | map $_REVERSE_TABLE{uc $type}{$_} // "#UNKNOWN#$_", | ||||
| 75 | grep $v->contains($_), | ||||||
| 76 | 0 .. ($v->Size - 1)]; | ||||||
| 77 | } | ||||||
| 78 | |||||||
| 79 | 2 | 17 | return $self->_decoded->{$type}; | ||||
| 80 | } | ||||||
| 81 | |||||||
| 82 | sub is_supported { | ||||||
| 83 | 3 | 1 | 13 | my ($self, $type, $report) = @_; | |||
| 84 | |||||||
| 85 | 3 | 19 | my $v = $self->_bits->{$type} | ||||
| 86 | or croak "Unknown type '$type'"; | ||||||
| 87 | |||||||
| 88 | 3 | 32 | return $v->Size > $report && $v->contains($report); | ||||
| 89 | } | ||||||
| 90 | |||||||
| 91 | sub count_supported { | ||||||
| 92 | 7 | 1 | 34 | my ($self, $type, @reports) = @_; | |||
| 93 | 7 | 6 | local $_; | ||||
| 94 | |||||||
| 95 | 7 | 38 | my $v = $self->_bits->{$type} | ||||
| 96 | or croak "Unknown type '$type'"; | ||||||
| 97 | |||||||
| 98 | 7 | 30 | my $sz = $v->Size; | ||||
| 99 | 7 | 307 | return scalar grep $sz > $_ && $v->contains($_), @reports; | ||||
| 100 | } | ||||||
| 101 | |||||||
| 102 | sub _parse_bits { | ||||||
| 103 | 54 | 1443 | my ($text) = @_; | ||||
| 104 | 54 | 58 | local $_; | ||||
| 105 | |||||||
| 106 | # Kernel formats this as groups separated by spaces. Each group is | ||||||
| 107 | # 32 or 64 bits long, depending on architecture. At least on Debian | ||||||
| 108 | # i386 and amd64, it matches Config longsize, and even better | ||||||
| 109 | # Bit::Vector->Word_Bits. | ||||||
| 110 | # | ||||||
| 111 | # However, the kernel doesn't print leading 0s, so we have to pad it | ||||||
| 112 | # to get it to a nice hex string. | ||||||
| 113 | 54 | 103 | my $group_nybbles = Bit::Vector->Word_Bits() / 4; | ||||
| 114 | 54 | 172 | my @groups = map '0'x($group_nybbles-length($_)).$_, split(/\s+/, $text); | ||||
| 115 | 54 | 77 | my $size = @groups * Bit::Vector->Word_Bits(); | ||||
| 116 | 54 | 57 | my $hex = join('', @groups); | ||||
| 117 | |||||||
| 118 | # sanity check because if we messed up, we're about to produce | ||||||
| 119 | # nonsense. This should never happen. | ||||||
| 120 | 54 | 74 | if ($size / 4 != length($hex)) { | ||||
| 121 | 0 0 0 | 0 0 0 | die "BUG: Hex isn't expected length; expected ${\($size/4)} got ${\length($hex)} for $hex"; | ||||
| 122 | } | ||||||
| 123 | |||||||
| 124 | 54 | 135 | return Bit::Vector->new_Hex($size, $hex); | ||||
| 125 | } | ||||||
| 126 | |||||||
| 127 | 1; | ||||||
| 128 | |||||||