
"""Tk Select Manager

A basic framework for combining Tk event handling and synchronous
multiplexed I/O. """

from Tkinter import tkinter
import SelectManager
import socket
from errno import EAGAIN, EWOULDBLOCK, EALREADY

class TkSelectManager( SelectManager.AbstractManager ):

    """ TkSelectManager - a select manager to be used with Tk

    Please note that this class is only a thin wrapper around Tkinter's
    createfilehandler() and deletefilehandler() calls. Therefore, multiple
    instances of this class should not be used. (You can, though, use
    this class with several instances of ordinary SelectManagers.)
    Second, you shouldn't call TkSelectManager.loop(); instead, call
    your root widget's mainloop().

    Third, if you don't want to take the risk that big write()s could
    block, you should make the channels non-blocking. """

    count = 0

    def __init__( s, evq = None, max_timeout = None ):
        s.chans = []
        TkSelectManager.count = s.count + 1
        if s.count > 1:
            raise SelectManager.UnknownStateException \
                  ( "More than one TkSelectManager" )

    def __del__( s ):
        TkSelectManager.count = s.count - 1

    def tkcallback( s, file, mask ):
        def do_write( x, srv = s ):
            try:
                if x.can_handle_write(): x.handle_write_event( srv )
            except socket.error, why:
                if why[0] in ( EAGAIN, EWOULDBLOCK, EALREADY ): pass
                else: raise
        file.handle_read_event( s )
        map( do_write, s.chans )

    def add_channel( s, chan ):
        s.chans.append( chan )
        tkinter.createfilehandler( chan, tkinter.READABLE, s.tkcallback )

    def del_channel( s, chan ):
        if chan in s.chans:
            s.chans.remove( chan )
            tkinter.deletefilehandler( chan )
    
    def once( s ):

        """ Serve once

        This routine should _not_ be called. Instead, call root.mainloop() """

        raise SelectManager.UnknownStateException \
              ( "TkSelectManager.once() called" )

