File Coverage

blib/lib/Text/TypingEffort.pm
Criterion Covered Total %
statement 105 105 100.0
branch 52 52 100.0
condition 11 11 100.0
subroutine 12 12 100.0
pod 3 6 50.0
total 183 186 98.3


line stmt bran cond sub pod time code
1             package Text::TypingEffort;
2              
3 12     12   335562 use 5.006;
  12         49  
  12         458  
4 12     12   125 use strict;
  12         25  
  12         407  
5 12     12   58 use warnings;
  12         28  
  12         363  
6 12     12   60 use Carp;
  12         23  
  12         41881  
7              
8             require Exporter;
9              
10             our @ISA = qw(Exporter);
11             our @EXPORT_OK = qw(
12             effort
13             layout
14             register_layout
15             );
16             our $VERSION = '0.25';
17              
18             our %basis; # stores the basis for our calculations
19             our %layouts; # stores the keyboard layouts
20              
21             =head1 NAME
22              
23             Text::TypingEffort - Calculate the effort required to type a given text
24              
25             =head1 SYNOPSIS
26              
27             use Text::TypingEffort qw/effort/;
28            
29             my $effort = effort("The quick brown fox jumps over the lazy dog");
30              
31             C<$effort> will be a hashref something like this
32              
33             $effort = {
34             characters => 43, # the number of characters in the text
35             presses => 44, # key presses need to type the text
36             distance => 950, # millimeters the fingers moved while typing
37             energy => 2.2..., # the energy (Joules) used while typing
38             };
39              
40             =head1 DESCRIPTION
41              
42             Text::TypingEffort is used to calculate how much physical effort was
43             required to type a given text. Several metrics of effort are used.
44             These metrics are described in detail in the L section.
45              
46             This module is useful for determining which keyboard layout is
47             more efficient, for making API/language design decisions, or to show your
48             boss how hard you're working.
49              
50             =head2 Function Quick Reference
51              
52             The following quick reference provides brief information about the
53             arguments that the functions can take. More detailed information is
54             given below.
55              
56             # effort() with a single argument
57             my $effort = effort(
58            
59             $text | \$text # the text to analyze
60             );
61            
62             # effort() with named arguments
63             my $effort = effort(
64            
65             text => $text | \$text, # the text to analyze
66             file => $filename | $filehandle, # analyze a file
67             layout => 'qwerty' # keyboard layout
68             | 'dvorak'
69             | 'aset',
70             | 'xpert',
71             | 'colemak',
72             unknowns => 0 | 1, # tally unknown chars?
73             initial => \%metrics, # set initial values
74             caps => 0 | 2 | 3 | ... # Caps Lock technique
75             );
76            
77             # layout()
78             my $l = layout; # get QWERTY layout
79             my $l = layout($layout_name); # get named layout
80            
81             # register_layout()
82             register_layout($name, \@layout); # register custom layout
83              
84             =head1 FUNCTIONS
85              
86             =head2 effort [$TEXT | \$TEXT]
87              
88             The argument should be a scalar or a reference to a scalar which contains
89             the text to be analyzed. If no parameter is provided, C<$_> is used
90             as the value of C<$TEXT>. Leading whitespace on each line of C<$TEXT>
91             is ignored since a decent text editor handles that for the typist.
92             Only characters found on a standard US-104 keyboard are tallied in
93             the metrics. That means that accented characters, unicode, etc. are
94             not included. If a character is unrecognized, it may be counted under
95             the 'unknowns' metric (see that documentation).
96              
97             =head2 effort %ARGUMENTS
98              
99             effort() may also be called with a list of named arguments. This allows
100             more flexibility in how the metrics are calculated. Below is a list of
101             acceptable arguments. In summary, calling effort like this
102              
103             effort($text);
104              
105             is identical to explicitly specifying all the defaults like this
106              
107             effort(
108             text => $text,
109             layout => 'qwerty',
110             unknowns => 0,
111             initial => {},
112             caps => 4,
113             );
114              
115              
116             =head3 text
117              
118             Specifies the text to be analyzed. The value should be either a scalar or
119             a reference to a scalar which contains the text. If neither this argument
120             nor B is specified, C<$_> is used as the text to analyze.
121              
122             =head3 file
123              
124             Specifies a file which contains the text to be analyzed. If the value
125             is a filehandle which is open for reading, the text will be read from that
126             file handle. The filehandle will remain open after C is finished
127             with it.
128              
129             If the value is a filename, the file will be opened and the text for analysis
130             read from the file. If neither this argument nor B is specified,
131             C<$_> is used as the text to analyze.
132              
133             =head3 layout
134              
135             Default: qwerty
136              
137             Specifies the keyboard layout to use when calculating metrics. Acceptable,
138             case-insensitive values for B are: qwerty, dvorak, aset, xpert,
139             colemak. If some other value is provided, the default value of 'qwerty' is
140             used.
141              
142             =head3 unknowns
143              
144             Default: 0
145              
146             Should a histogram of unrecognized characters be returned with the other
147             metrics? A true value indicates yes and a false value no. Tallying this
148             histogram takes a little bit more work in the inner loop and therefore
149             makes processing ever so slightly slower. It can be useful for seeing
150             how much of the text was not counted in the other metrics.
151              
152             See B in the L section for information on how this option
153             affects C's return value.
154              
155             =head3 initial
156              
157             Default: {}
158              
159             Sets the initial values for each of the metrics. This option is the way to
160             have C accumulate the results of multiple calls. By doing something
161             like
162              
163             $effort = effort($text_1);
164             $effort = effort(text=>$text_2, initial=>$effort);
165              
166             you get the same results as if you had done
167              
168             $effort = effort($text_1 . $text_2);
169              
170             except the former scales more gracefully. The value of B should
171             be a hashref with keys and values similar to the result of a previous
172             call to C. If the hashref does not contain a key-value pair
173             for a given metric, the initial value of that metric will be its normal
174             default value (generally 0).
175              
176             If the value of B is not a hashref, C proceeds as if
177             the B argument were not present at all. This behavior may
178             change in the future, so don't rely upon it.
179              
180             =head3 caps
181              
182             Default: 4
183              
184             Determines how strings of consecutive capital letters should be handled.
185             The default value of 4 means that four or more capital letters in a
186             row should be treated as though the user pressed "Caps Lock" at the
187             beginning, then typed the characters and then pressed "Caps Lock" again.
188             This behavior more accurately models what typical users do when typing
189             strings of capital letters. You may change the number of capital letters
190             that must be in a row in order to trigger this behavior by specifying
191             an integer greater than 1 as the value of the B argument. If you
192             specify, the value 1, the value 2 will be used instead.
193              
194             If the value of B is 0, capital letters are treated as though the
195             user pressed Shift for each one. If C is given, the default
196             value of B is used.
197              
198             When caps handling is enabled, "capital letter" means any character that
199             can be typed without the Shift key when Caps Lock is on. That includes
200             characters such as '.' and '/' and '-' etc. However, the string of
201             consecutive caps must start and end with a real capital letter. That way,
202             a string such as '-----T-----' won't be calculated using Caps Lock.
203              
204             =cut
205              
206             sub effort {
207             # establish the default options
208 36     36 1 65664 my %DEFAULTS = (
209             layout => 'qwerty',
210             unknowns => 0,
211             initial => {},
212             caps => 4,
213             );
214              
215             # establish our current options
216 36         67 my %opts;
217 36 100       112 if( @_ == 1 ) {
218 5         37 %opts = ( %DEFAULTS, text=>$_[0] );
219             } else {
220 31         155 %opts = ( %DEFAULTS, @_ );
221             }
222            
223 36 100 100     245 $opts{text} = $_ unless defined $opts{file} or defined $opts{text};
224              
225             # repair the caps argument
226 36 100       133 $opts{caps} = $DEFAULTS{caps} unless defined $opts{caps};
227 36 100       107 $opts{caps} = 2 if $opts{caps} == 1;
228              
229             # fill in the preliminary data structures as needed
230 36         99 $opts{layout} = lc($opts{layout});
231 36 100 100     228 %basis = &_basis( $opts{layout} )
232             unless $basis{LAYOUT} and $basis{LAYOUT} eq $opts{layout};
233              
234 36         61 my $fh; # the filehandle for reading the text
235             my $text; # or a reference to the text itself
236 36         118 my $close_fh = 0;
237 36 100       130 if( defined $opts{file} ) {
    100          
238 3 100       8 if( ref $opts{file} ) {
239 1         8 $fh = $opts{file};
240             } else {
241 2 100       318 open($fh, "<$opts{file}")
242             or croak "Couldn't open file $opts{file}";
243 1         2 $close_fh = 1;
244             }
245             } elsif( ref $opts{text} ) {
246 6         15 $text = $opts{text};
247             } else {
248 27         74 $text = \$opts{text}; # make $text a reference
249             }
250              
251             # get the first line of text
252 35         42 my $line;
253 35         45 my $line_rx = ".*(?:\n|\r|\r\n)?"; # match a line
254 35 100       74 if( $fh ) {
255 2         61 $line = <$fh>;
256             } else {
257 33         110 $$text =~ /^/g; # reset the regex in case we're given same arg twice
258 33         438 $$text =~ /($line_rx)/g;
259 33         96 $line = $1; # the pattern always matches (I think)
260             }
261              
262             # set the default initial values for the metrics
263 35         632 my %sum;
264 35         113 @sum{qw(characters presses distance)} = (0) x 3;
265 35 100       96 $sum{unknowns} = {} if $opts{unknowns};
266              
267             # munge the initial values based on the 'initial' option
268 35 100 100     770 if( ref($opts{initial}) eq 'HASH' and keys %{$opts{initial}} ) {
  34         157  
269 3         5 for (qw/characters presses distance/) {
270 9 100       25 $sum{$_} = $opts{initial}{$_} if defined $opts{initial}{$_};
271             }
272 3         5 for (qw/presses distance/) {
273 6 100       20 $sum{unknowns}{$_} = { %{ $opts{initial}{unknowns}{$_} } }
  2         7  
274             if defined $opts{initial}{unknowns}{$_};
275             }
276             }
277              
278 35         109 while( defined $line ) {
279 105 100       263 if( chomp $line ) {
280             # the newline counts as a character, a keypress and an ENTER
281 64         80 $sum{characters}++;
282 64         67 $sum{presses}++;
283 64         115 $sum{distance} += $basis{distance}{ENTER};
284             }
285 105         345 $line =~ s/^\s*//;
286 105         815 $line =~ s/\s*$//;
287              
288             # handle consecutive chunks typed with Caps Lock on
289 105 100       238 if( $opts{caps} ) {
290 100         144 my $c = $opts{caps}-2;
291 100         112 my $p = q{[A-Z]}; # pure caps
292 100         102 my $d = q{[[:space:]A-Z0-9,./;'\[\]\\=`-]}; # defiled caps
293 100 100       1126 if( my $caps = $line =~ s#($p $d {$c,} $p)#\L$1#gx ) {
294             # turn caps on and off for each chunk
295 14         25 $sum{presses} += 2*$caps;
296 14         36 $sum{distance} += 2*$caps*$basis{distance}{CAPS};
297             }
298             }
299              
300 105         722 foreach(split //, $line) {
301             # only count the character if we recognize it
302 3476 100       21776 $sum{characters}++ if exists $basis{presses}{$_};
303              
304 3476         3885 foreach my $metric (qw/presses distance/) {
305 6952 100       11735 if( defined $basis{$metric}{$_} ) {
    100          
306 6850         13500 $sum{$metric} += $basis{$metric}{$_};
307             } elsif( $opts{unknowns} ) {
308 20         59 $sum{unknowns}{$metric}{$_}++;
309             }
310             }
311             }
312              
313             # get the next line
314 105 100       486 if( $fh ) {
315 4         218 $line = <$fh>;
316             } else {
317 101 100       590 if( $$text =~ /($line_rx)/gc ) {
318 70         230 $line = $1;
319             } else {
320 31         94 undef $line;
321             }
322             }
323             }
324              
325 35 100       127 close $fh if $close_fh;
326              
327 35         76 $sum{energy} = (&J_per_mm*$sum{distance}) + (&J_per_click*$sum{presses});
328              
329 35         239 return \%sum;
330             }
331              
332             =head2 layout [$NAME]
333              
334             Returns an arrayref representing the requested layout or C if
335             the given name is unknown. If no layout name is provided, the QWERTY
336             layout is returned.
337              
338             See C below or the Text::TypingEffort source code for
339             examples of the contents of the arrayref.
340              
341             =cut
342              
343             sub layout {
344 22   100 22 1 1222 my $name = lc(shift) || 'qwerty';
345 22 100       434 return $layouts{$name} if exists $layouts{$name};
346 2         10 return undef;
347             }
348              
349             =head2 register_layout $NAME, \@LAYOUT
350              
351             Register a new layout, using the given name. The name is stored
352             without regard to case, so 'NAME' and 'name' are considered the same.
353             The layout itself should be an arrayref containing each key's character
354             and its shifted version. Running the code below displays a pseudo-code
355             snippet showing how the QWERTY keyboard layout is defined. Start in
356             the upper-left corner of a QWERTY keyboard and follow along through
357             the pseudo-code. You should get the idea. You can also find documented
358             examples in the source code.
359              
360             use Text::TypingEffort qw/layout/;
361             $l = layout;
362             print "register_layout('qwerty', [qw{\n";
363             while( ($lower, $upper) = splice(@$l, 0, 2) ) {
364             print "\t$lower $upper\n";
365             }
366             print "}]);\n";
367              
368             Typically, C is called just prior to C. For
369             example:
370              
371             my @layout = qw{
372             ...
373             };
374             register_layout('my custom layout', \@layout);
375             my $e = effort(
376             text => $text,
377             layout => 'my custom layout',
378             );
379              
380             =cut
381              
382             sub register_layout {
383 61     61 1 140 my $name = lc shift;
384 61         166 $layouts{$name} = shift;
385             }
386              
387             =head1 METRICS
388              
389             =head2 characters
390              
391             The number of recognized characters in the text. This is similar in
392             spirit to the Unix command C. Only those characters which are encoded
393             in the internal keyboard layout will be counted. That excludes accented
394             characters, Unicode characters and control characters but includes newlines.
395              
396             =head2 presses
397              
398             The number of keys pressed when typing the text. The value of this metric is
399             the value of the B metric plus the number of times the Shift key
400             was pressed.
401              
402             =head2 distance
403              
404             The distance, in millimeters, that the fingers travelled while typing
405             the text. This distance includes movement required for the Shift and
406             Enter keys, but does not include the vertical movement the finger makes
407             as the key descends during a press. Perhaps a better name for this
408             metric would be horizontal_distance, but that's too long ;-)
409              
410             The model for determining this metric is very simplistic. It assumes
411             that a finger moves from its home position to the destination key and
412             then returns to the home position before moving on to the next key.
413             Of course, this is not how people actually type, but the model should
414             result in an upper-bound for the amount of finger movement.
415              
416             =head2 energy
417              
418             The number of Joules of energy required to type the text. This metric is
419             the most inclusive in that it tries to accomodate the values of both the
420             B and the B metrics into a single metric. However,
421             this metric is also the least accurate at modeling the real world.
422             The calculations are roughly based upon the I
423             Activities> (or rather hearsay about it's contents since I don't have
424             a copy).
425              
426             The physical charactersistics of the keyboard are assumed to be roughly in
427             line with ISO 9241-4:1998, which specifies standards for such things.
428              
429             =head2 unknowns
430              
431             This metric is only included in the output if the B argument
432             to C was true.
433              
434             The value is a histogram of the unrecognized characters encountered during
435             processing. This includes any control characters, accented characters or
436             unicode characters. Generally, anything other than the letters, numbers
437             and punctuation found on a standard U.S. keyboard will be counted here.
438              
439             If all characters were recognized, the value will be an empty hashref.
440             If any characters were unknown, the value will be a hashref something
441             like this:
442              
443             unknowns => {
444             presses => {
445             'Å' => 2,
446             'Ö' => 3,
447             },
448             distance => {
449             'Å' => 2,
450             'Ö' => 3,
451             },
452             }
453              
454             The key indicates the metric for which information was missing. The value
455             is a hash indicating the character and the number of times that character
456             occurred. There will be no entries in the hash for the B
457             or B metrics as these are incidental to the other two.
458              
459             This metric is only added to the result if the B option was
460             specified and true.
461              
462             =head1 SEE ALSO
463              
464             Tactus Keyboard article on the mechanics and standards of
465             keyboard design - L
466              
467             =head1 CONTRIBUTING
468              
469             The source for Text::TypingEffort is maintained in a Git repository
470             located at L. To submit patches,
471             you can do something like this:
472              
473             $ git clone git://git.ndrix.com/Text-TypingEffort
474             $ cd Text-TypingEffort
475             # hack, commit, hack, commit
476             $ git format-patch -s origin
477             $ git send-email --to michael@ndrix.org *.patch
478              
479             See http://www.kernel.org/pub/software/scm/git/docs/everyday.html
480              
481             =head1 AUTHOR
482              
483             Michael Hendricks
484              
485             Thanks to Ricardo Signes for a patch for the C and
486             C subroutines.
487              
488             =head1 BUGS/TODO
489              
490             Please submit suggestions and report bugs to the CPAN Bug Tracker at
491             L
492              
493             =head1 COPYRIGHT AND LICENSE
494              
495             Copyright (C) 2005-2009 by Michael Hendricks
496              
497             Permission is hereby granted, free of charge, to any person obtaining
498             a copy of this software and associated documentation files (the
499             "Software"), to deal in the Software without restriction, including
500             without limitation the rights to use, copy, modify, merge, publish,
501             distribute, sublicense, and/or sell copies of the Software, and to
502             permit persons to whom the Software is furnished to do so, subject
503             to the following conditions:
504              
505             The above copyright notice and this permission notice shall be
506             included in all copies or substantial portions of the Software.
507              
508             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
509             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
510             MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
511             IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
512             ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
513             CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
514             WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
515              
516              
517             =cut
518              
519              
520              
521             ############### subroutines to help with the calculations ################
522              
523             sub _basis {
524 16     16   32 my ($desired) = @_;
525              
526 16         32 my %basis;
527 16         52 $basis{LAYOUT} = $desired;
528              
529             # get the keyboard characteristics
530 16         56 my @keyboard = &us_104;
531              
532             # get the layout
533 16 100       98 my @layout = @{ layout($desired) || layout };
  16         54  
534              
535             # get some keyboard characteristics
536 16         55 my($lshift,$rshift) = splice(@keyboard, 0, 2);
537              
538             # the space character is somewhat exceptional
539 16         80 $basis{distance}{' '} = 2*shift(@keyboard);
540 16         47 $basis{presses}{' '} = 1;
541              
542 16         48 $basis{distance}{ENTER} = 2*shift(@keyboard);
543 16         45 $basis{distance}{CAPS} = 2*shift(@keyboard);
544              
545             # populate $basis{presses} and $basis{distance}
546 16         73 while( my($shift, $d) = splice(@keyboard, 0, 2) ) {
547 752         985 my($lc, $uc) = splice(@layout, 0, 2);
548              
549 752         2667 $basis{presses}{$lc} = 1;
550 752         4435 $basis{presses}{$uc} = 2;
551              
552             # the *2 is because distances are initially one-way
553 752         1803 $basis{distance}{$lc} = 2*$d;
554 752 100       3303 $basis{distance}{$uc} = 2*($d + ($shift eq 'l' ? $lshift : $rshift));
555             }
556              
557 16         172 return %basis;
558             }
559              
560             # Calculate the number of Joules of energy needed to move the
561             # finger 1 millimeter as it reaches for a keyboard key.
562             #
563             # English and QWERTY are used in the values below because the caloric
564             # studies were done with a standard QWERTY keyboard and typing English text
565             sub J_per_mm {
566             return
567 35     35 0 212 502 # Joules/ hour (energy for 150lb man typing for 1 hour)
568             * (1/60) # hours / min
569             * (1/40) # min / word (typing speed)
570             * (1/4.3) # words / char (average English word length)
571             * (1/21.2);# chars / mm (average distance when typing QWERTY)
572             }
573              
574             # Calculate the number of Joules of energy needed to depress a single
575             # key on the keyboard.
576             #
577             # The energy required is the area of a triangle with sides equal
578             # to the key displacement in meters (.003) and the force required to
579             # depress the key in Newtons (.6) These values are taken from
580             # ISO 9241-4:1998(E) (indirectly since the actual source was a quote at
581             # http://www.tactuskeyboard.com/keymech.htm
582             sub J_per_click {
583 35     35 0 115 return (1/2)*(.003)*(.6);
584             }
585              
586              
587             ################ subroutines for keyboard specifications #################
588             sub us_104 {
589             return (
590             # distances the finger must move to reach the left Shift,
591             # right Shift, Space, Enter and Caps Lock, respectively
592             # (in millimeters)
593 16     16 0 511 qw{
594             15 30 0 35 15
595             },
596              
597             # define the `12345 row
598             # the first value is the shift key one must press when trying to
599             # "capitalize" the given key. Valid options are 'r' (right shift)
600             # and 'l' (left shift).
601             # the second value is the distance the finger must move from its
602             # home position to reach the given key. The distance is in millimeters.
603             qw{
604             r 45
605             r 35
606             r 35
607             r 35
608             r 35
609             r 30
610             r 40
611             l 35
612             l 35
613             l 35
614             l 30
615             l 30
616             l 35
617             l 45
618             },
619              
620             # define the QWERTY row
621             qw/
622             r 15
623             r 15
624             r 15
625             r 15
626             r 15
627             l 25
628             l 15
629             l 15
630             l 15
631             l 15
632             l 15
633             l 30
634             /,
635              
636             # define the home row
637             qw{
638             r 0
639             r 0
640             r 0
641             r 0
642             r 15
643             l 15
644             l 0
645             l 0
646             l 0
647             l 0
648             l 15
649             },
650              
651             # define the ZXCVB row
652             qw{
653             r 15
654             r 15
655             r 15
656             r 15
657             r 30
658             l 15
659             l 15
660             l 15
661             l 15
662             l 15
663             },
664             );
665              
666             }
667              
668             ################### subroutines for keyboard layouts ####################
669 12     12   92 { no warnings qw(qw); # stop warnings about the '#' and ',' characters
  12         23  
  12         6255  
670              
671             # the first value is the character generated by pressing the key
672             # without any modifier. The second value is the character generated
673             # when pressing the key along with the SHIFT key.
674              
675             register_layout('qwerty', [
676             # define the 12345 row
677             qw{
678             ` ~
679             1 !
680             2 @
681             3 #
682             4 $
683             5 %
684             6 ^
685             7 &
686             8 *
687             9 (
688             0 )
689             - _
690             = +
691             \ |
692             },
693              
694             # define the QWERTY row
695             qw/
696             q Q
697             w W
698             e E
699             r R
700             t T
701             y Y
702             u U
703             i I
704             o O
705             p P
706             [ {
707             ] }
708             /,
709              
710             # define the home row
711             qw{
712             a A
713             s S
714             d D
715             f F
716             g G
717             h H
718             j J
719             k K
720             l L
721             ; :
722             ' "
723             },
724              
725             # define the ZXCVB row
726             qw{
727             z Z
728             x X
729             c C
730             v V
731             b B
732             n N
733             m M
734             , <
735             . >
736             / ?
737             }
738             ]);
739              
740             register_layout('dvorak', [
741             # define the 12345 row
742             qw/
743             ` ~
744             1 !
745             2 @
746             3 #
747             4 $
748             5 %
749             6 ^
750             7 &
751             8 *
752             9 (
753             0 )
754             [ {
755             ] }
756             \\ |
757             /,
758             # define the ',.pYF row
759             qw{
760             ' "
761             , <
762             . >
763             p P
764             y Y
765             f F
766             g G
767             c C
768             r R
769             l L
770             / ?
771             = +
772             },
773             # define the home row
774             qw{
775             a A
776             o O
777             e E
778             u U
779             i I
780             d D
781             h H
782             t T
783             n N
784             s S
785             - _
786             },
787             # define the ;QJKX row
788             qw{
789             ; :
790             q Q
791             j J
792             k K
793             x X
794             b B
795             m M
796             w W
797             v V
798             z Z
799             },
800             ]);
801              
802             register_layout('aset', [
803             # define the 12345 row
804             qw{
805             ` ~
806             1 !
807             2 @
808             3 #
809             4 $
810             5 %
811             6 ^
812             7 &
813             8 *
814             9 (
815             0 )
816             - _
817             = +
818             \ |
819             },
820              
821             # define the QWERTY row
822             qw/
823             q Q
824             w W
825             d D
826             r R
827             f F
828             y Y
829             u U
830             k K
831             o O
832             p P
833             [ {
834             ] }
835             /,
836              
837             # define the home row
838             qw{
839             a A
840             s S
841             e E
842             t T
843             g G
844             h H
845             n N
846             i I
847             l L
848             ; :
849             ' "
850             },
851              
852             # define the ZXCVB row
853             qw{
854             z Z
855             x X
856             c C
857             v V
858             b B
859             j J
860             m M
861             , <
862             . >
863             / ?
864             }
865             ]);
866              
867             register_layout('xpert', [
868             # define the 12345 row
869             qw{
870             ` ~
871             1 !
872             2 @
873             3 #
874             4 $
875             5 %
876             6 ^
877             7 &
878             8 *
879             9 (
880             0 )
881             - _
882             = +
883             \ |
884             },
885              
886             # define the XPERT row
887             # the actual XPERT keyboard has a second 'e' key
888             # where I put the ';' but since it eliminates the
889             # semicolon, it has to go somewhere
890             qw/
891             x X
892             p P
893             ; :
894             r R
895             t T
896             y Y
897             u U
898             i I
899             o O
900             j J
901             [ {
902             ] }
903             /,
904              
905             # define the home row
906             qw{
907             q Q
908             s S
909             d D
910             f F
911             n N
912             h H
913             a A
914             e E
915             l L
916             k K
917             ' "
918             },
919              
920             # define the ZXCVB row
921             qw{
922             z Z
923             w W
924             c C
925             v V
926             b B
927             g G
928             m M
929             , <
930             . >
931             ? /
932             }
933             ]);
934              
935             register_layout('colemak', [
936             # define the 12345 row
937             qw{
938             ` ~
939             1 !
940             2 @
941             3 #
942             4 $
943             5 %
944             6 ^
945             7 &
946             8 *
947             9 (
948             0 )
949             - _
950             = +
951             \ |
952             },
953              
954             # define the QWFPG row
955             qw/
956             q Q
957             w W
958             f F
959             p P
960             g G
961             j J
962             l L
963             u U
964             y Y
965             ; :
966             [ {
967             ] }
968             /,
969              
970             # define the home row
971             qw{
972             a A
973             r R
974             s S
975             t T
976             d D
977             h H
978             n N
979             e E
980             i I
981             o O
982             ' "
983             },
984              
985             # define the ZXCVB row
986             qw{
987             z Z
988             x X
989             c C
990             v V
991             b B
992             k K
993             m M
994             , <
995             . >
996             / ?
997             }
998             ]);
999             }
1000              
1001             1;
1002             __END__