Tuesday, June 15, 2010

Re: show what I type in another window

#!/usr/bin/perl
use strict;
use warnings;

# friendlier error messages for modules not installed
sub nice_use {
my $module = shift;
eval "use $module";
$@ and die "This program requires the $module module\n";
$module->import(@_);
}
BEGIN { nice_use 'Time::HiRes', 'time' }
BEGIN { nice_use 'IPC::Run', 'run', 'start' }

# these are core modules
use File::Spec;
use Getopt::Long;
GetOptions(
'quit=s' => \(my $quit_key = 'F2'),
'window-id|id=s' => \(my $window_id),
'newline=s' => \(my $split_after = 1.3),
'backspace!' => \(my $literal_backspace = 1),
'x11-purist' => \(my $X11_purity = 0),
) or die <<USAGE;
Usage: $0 [options]
--quit=KEY Press KEY to quit [default: F2]
--window-id=ID / --id=ID Window ID [default: prompt via xwininfo]
--newline=DELAY Delay between starting new lines [default: 1.3]
--nobackspace Don't use literal backspaces [default: use]
--x11-purist Don't replace weird X11 keysyms [default: do]
USAGE

# Use xwininfo to find a window ID if none specified
if (!defined $window_id) {
print "Click to choose a window\n";
run [ 'xwininfo' ], '>', \my $wininfo;
$wininfo =~ /^(.*Window id.*)$/m
or die "Couldn't find window ID from: $wininfo\n";
$window_id = (split ' ', $1)[3];
}

# ignore KeyRelease for modifier keys
my %ignore = map {; $_ => 1 }
ISO_Level3_Shift =>
map { ($_."_L", $_."_R") }
qw/Super Control Alt Shift Meta/;

# Use 'xev' to monitor X events
my $events = '';
my $xev = start [ 'xev', '-id', $window_id ], '>', \$events;
my $done = 0;
my @q;

# prints a nicely formatted key for output
my $last_output = time;
sub format_key {
my $evt = shift;
my $now = time;

# Add a newline if there hasn't been a key output in a while
# Otherwise things just all run together
if ($split_after and $now - $last_output > $split_after) {
print "\n";
}
$last_output = $now;

my $key = '';

if (exists $$evt{string} and $$evt{string} =~ /[[:print:]]/) {
# Use the looked-up string if it's printable
$key .= $$evt{string};
} else {
# otherwise use the raw key
$key .= $$evt{key};
}

# Convert the modifier state into a nice representation
# E.g. 0xc becomes Ctrl+Alt+
my $mods = '';
$_ = hex for $$evt{state};
for ( [Ctrl=>4], [Alt=>8], [Shift=>1] ) {
my ($name, $mask) = @$_;
$$evt{state} & $mask and $mods .= "$name+";
}

# Wrap in 'key' indicators if there are modifiers
# or it's a named key
$key = "<$mods$key>" if $mods or 1 < length $key;

# Shift+W = 'W', for example...
$key =~ s{Shift\+(?=[^a-z]>)}{};

# '<:>' is just ':'
$key =~ s{<(.)>}{$1};

# If you make a lot of typos (I do), you probably want this
$key =~ s{<BackSpace>}{\b} if $literal_backspace;

# Prior? Next? What?
if (not $X11_purity) {
for ($key) {
s/<Prior>/<PageUp>/;
s/<Next>/<PageDown>/;
s/<Print>/<PrintScreen>/;
}
}

# Quit on specified key
$done++ if $$evt{key} =~ /^<?$quit_key>?$/;

print $key;
}

while (!$done) {
my $now = time;

# get input, but don't block on 'xev'
$xev->pump_nb;

# if there's input, process it
if (length $events) {

# 'xev' events are separated by blank lines
s/^\n+//, s/\n+$// for $events;
for (split /\n\n/, $events) {

# seen = actual time seen
my $evt = { seen => $now };

# xtime = time that X server reports
($$evt{xtime}) = /time (\d+)/;

# skip unless event = Press or Release
($$evt{event}) = /^(KeyPress|KeyRelease)/ or next;

# get the modifier state and keysym
(@$evt{qw/state key/}) = /^\s*state (0x[\da-f]+).*keysym \S+ (.*?)\)/m;

# Find the stringified key if present
/XLookupString gives ([1-9]\d*) bytes/ and
/XLookupString gives.*?"(.{$1})/sm and
$$evt{string} = $1;

push @q, $evt;
}

# clear the buffer
$events = '';
}

# test to see whether things that are on the queue should be printed
# Wait a short time to prevent auto-repeated keys from showing
my @print;
push @print, shift @q while @q and $now - $q[0]{seen} > 0.2;
if (@print) {

# deal with auto-repeated keys
my %count;
$count{$$_{xtime}}++ for @print;

format_key $_ for grep {
($$_{event} =~ /Key(Press|Release)/
#and exists($$_{string}) # printable,
and !$ignore{$$_{key}}, # non-ignored,
and $count{$$_{xtime}} < 2 # non-autorepeated,
) # TODO - expand for mouse events
} @print;
}

# delay so we're not busy-waiting on xev
select undef, undef, undef, 0.01;
}
On Mon, 14 Jun 2010, Javier Rojas wrote:

> Hi list,
>
> I'm going to make a presentation about vim, and I'd like to make a
> live demonstration of its functionality. Besides of typing and showing
> vim's marvels, I'd like the audience to see in some way what I'm
> typing.
>
> Do you know of any tool to do this? I Imagine such a tool as a
> multiplier of the keyboard input, that receives my keyboard hits,
> sends them to vim, and shows them (larger, with proper scrolling and
> timing) in a separate window.
>
> I think I can hack something like this with xdotool, and writing a
> custom program that drives both xdotool and vim, but maybe you can
> suggest an existing application for doing this.
>

The attached script[1] might do what you want. It was fun to write anyway.
It uses the output of 'xev' to grab key presses. So, it goes the
opposite direction from what you envisioned: start it in another window
and click on your Vim window, and it will print what you're typing.
(rather than typing in its window and sending the events to Vim)

It's in Perl. It requires two non-core modules:
Time::HiRes (often installed, IME)
IPC::Run (less-commonly installed, but oh-so-useful)

The trickiest part is the weirdness surrounding the way X11 implements
key repeats. For keys that can be repeated (holding it down produces
multiple events), the server immediately follows a KeyPress with a
KeyRelease[2]. So, even the simplest keystroke can end up as:

KeyRelease+KeyPress+KeyRelease
or
KeyPress+KeyRelease+KeyRelease

The method I used to filter these was to check the event time (The
auto-generated event has an identical timestamp.). Probably a bit
heuristic, and maybe server-dependent. YMMV.

Kind of disheartening how typo-prone I am. (Hence the default behavior
of the script to print <BackSpace>'s as literal backspaces.)

--
Best,
Ben

[1] Also web'ed, in case the list eats it:
http://benizi.com/vim/watch-keys.script.txt
[2] See, e.g. this bug:
https://bugs.launchpad.net/ubuntu/+source/xorg-server/+bug/403339

--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

No comments: