Re: Foreach and add_watch not working as I would expect




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, ...;
        });
    }

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]