Re: Foreach and add_watch not working as I would expect



On 5/13/06, muppet <scott asofyet org> wrote:

On May 12, 2006, at 10:14 AM, Mike Martin wrote:

> I am having problems with using IO::Watch Gtk2::Helper etc under a
> foreach loop.
>
> For some reason it will only run the last element of the array. Here
> is example code.
[...]
> All that seems to happen is that if there are two elements of the
> array then two identical scrooll-windows open running the last $var.

- In watch2(), you're using IN as the file handle.  Remember that
file handles like this are global in perl.  Although you're grabbing
the fileno(IN) when installing the watch, you're still reading from
IN; if watch2() is run more than once, IN will always have the value
of the last file opened.  So, your watch handler will fire for the
old fileno, but you'll read from the new file handle.  That's why you
always want to use lexical file handles, e.g.

     sub watch2 {
         open (my $in, ...) or die ...;
         Glib::IO->add_watch (fileno ($in), ['in', 'hup'], sub {
             # Since $in is a lexical in the enclosing block, this
             # closure will trap the correct $in.
             sysread $in, ...;
         });
     }


thanks that worked

If using the closure to trap $in makes you uncomfortable, you can
pass $in as user data to the watch handler, like this:

         Glib::IO->add_watch (fileno($in), ['in', 'hup'],
\&watch_handler, $in);
     ...
     sub watch_handler {
         my ($fileno, $condition, $in) = @_;
         # $in is the user data passed to add_watch(), which in this
case is our file handle.
     }


- The first thing your watch handler does is check for $condition eq
'hup'.  However, $condition is a bitfield, which may contain more
than just 'hup'.  A better way to do this is

     sub watch_handler {
         my ($fileno, $condition) = @_;

         if ($condition >= 'in') {
             # handle input data...
         }
         # note: *not* mutually exclusive!
         if ($condition >= 'hup') {
             # got a hang up -- clean up and uninstall.
             return FALSE;
         }
         return TRUE;
     }



- In your watch handler, you're connecting to "insert-text".  That
means, that you'll connect a new signal handler to the buffer every
time a new chunk of data arrives on IN.  If it takes 50 reads to get
all the data from IN, you'll wind up with 50 signal handlers piled up
on $buffer.  This is not what you want.  Three alternatives:

    1.  Install the insert-text handler outside of the watch handler,
e.g., after creating the buffer.  This will leave the handler
installed even after you're finished with the read loop, which is
probably not what you want.
    2.  Scroll to mark directly after inserting.  This is the
simplest.  However, if the view actually updates in an idle, then the
scrolling will not quite work right.
    3.  Immediately after reading some data in the watch handler,
install a single-shot idle handler to scroll to the mark.  This is
like #2, but gives the view a chance to recalculate itself first.

- The same thing applies to the mark; since you create the mark in
the watch handler, you're piling up marks every time there's a chunk
of data.  With this one, the best solution is just to create the mark
immediately after creating the buffer.


--
Package contains eight 13-inch aliens in assorted colors.
  -- Catalog copy for space invaders wall decals.





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]