-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeybinder.pl
executable file
·166 lines (120 loc) · 4.28 KB
/
keybinder.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#! /usr/bin/env perl
use strict;
use warnings;
use YAML;
use Data::Dumper;
use Linux::Input;
use List::Util qw(first any all);
use Getopt::Long;
use Pod::Usage;
my $inputs_config_file = 'inputs.yaml';
my $commands_config_file = 'commands.yaml';
my $verbose = 0;
my $help = 0;
GetOptions(
"inputs_file|i=s" => \$inputs_config_file,
"commands_file|c=s" => \$commands_config_file,
"verbose|v" => \$verbose,
"help|h" => \$help,
) or pod2usage(2);
pod2usage(-verbose => 2, -exitval=>1) if $help;
# Load config from YAML-Files
my @inputs;
eval {
@inputs = @{ YAML::LoadFile($inputs_config_file) };
};
if ($!) {
print STDERR "Can't open $inputs_config_file: $!\n";
exit 2;
}
my %commands;
eval {
%commands = %{ YAML::LoadFile($commands_config_file) };
};
if ($!) {
print STDERR "Can't open $commands_config_file: $!\n";
exit 2;
}
# open all inputs and store information in the inputs structure
my $selector = IO::Select->new();
foreach my $input (@inputs) {
# create input device
my $device = Linux::Input->new( $input->{device} );
# add it to the selector
$selector->add( $device->fh );
# store information in structure
$input->{dev} = $device;
# create reverse hash for faster set/get of modifier values
$input->{modifier_by_name} = { map { $_->{name} => { key => $_->{key}, value => 0 } } @{ $input->{modifier} } };
}
print "setup complete, start mainloop\n" if $verbose;
# endless loop
# wait for event
while ( my @fh = $selector->can_read ) {
# loop over all filehandles and poll events
foreach my $fh (@fh) {
# serach the matching input device
my $input = first { $_->{dev}->fh() == $fh } @inputs;
# poll event from input
my @events = $input->{dev}->poll();
foreach my $event (@events) {
if ( $event->{type} == 1 ) {
my $code = $event->{code};
# look for modifiers in the config
my @modifiers = grep { $_->{key} == $code } @{ $input->{modifier} };
# look for key in config
my @keys = grep { $_->{key} == $code } @{ $input->{keys} };
# no matching key found -> print the code and event value
if ( !@keys && !@modifiers ) {
print "code=$code, value=$event->{value}\n" if $verbose;
}
# for all modifiers: set the value in the modifier structure
foreach my $modifier (@modifiers) {
$input->{modifier_by_name}->{ $modifier->{name} }->{value} = $event->{value};
}
# there may be more than one key with this code, so loop over them
foreach my $key (@keys) {
# key press?
if ( $event->{value} == 1 ) {
# check the modifier of the key. Only if all match, the key is processed
if ( all { $input->{modifier_by_name}->{$_}->{value} == ( $key->{modifier}->{$_} // 0 ) } keys %{ $input->{modifier_by_name} } ) {
print "Found: $key->{name}\n" if $verbose;
# execute commands defined for this key
for my $command ( @{ $commands{ $key->{name} } } ) {
print "Exec: $command\n" if $verbose;
system("$command");
}
}
}
}
}
}
}
}
__END__
=head1 NAME
keybinder.pl
=head1 SYNOPSIS
keybinder.pl [--inputs_file <file>] [--commands_file <file>]
Options:
-h help
-v verbose
-i <file> inputs file
-c <file> commands file
=head1 OPTIONS
=over 8
=item B<--inputs_file | -i>
YAML file with definition of inputs. Default: inputs.yaml
=item B<--commands_file | -c>
YAML file with definition of commands. Default: commands.yaml
=item B<--help | -h>
Print this help message and exits.
=item B<--verbose | -v>
Print verbose messages.
=back
=head1 DESCRIPTION
B<keybinder.pl> reads a linux input device, recognizes the pressed keys with optional modifiers and
executes programms when a configured key is pressed.
=head1 AUTHOR
Dirk Melchers (dirk@tuxdiver.de)
=cut