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 |