PreviousUpNext

15.4.1649  src/lib/x-kit/xclient/src/window/widget-cable.pkg

## widget-cable.pkg

# Compiled by:
#     src/lib/x-kit/xclient/xclient-internals.sublib



# A widget cable is a collection of
# three input streams and one output stream
# used by a widget to communicate with its parent.
#
# The three input streams are:
#     mouse mail
#     keyboard mail
#     other (e.g. expose events)
#
# The output stream is:
#     mail to parent.


stipulate
    include package   threadkit;                                                # threadkit                             is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
    package g2d =  geometry2d;                                                  # geometry2d                            is from   src/lib/std/2d/geometry2d.pkg
    package kb  =  keys_and_buttons;                                            # keys_and_buttons                      is from   src/lib/x-kit/xclient/src/wire/keys-and-buttons.pkg
    package ks  =  keysym;                                                      # keysym                                is from   src/lib/x-kit/xclient/src/window/keysym.pkg
    package xt  =  xtypes;                                                      # xtypes                                is from   src/lib/x-kit/xclient/src/wire/xtypes.pkg
    package ts  =  xserver_timestamp;                                           # xserver_timestamp                     is from   src/lib/x-kit/xclient/src/wire/xserver-timestamp.pkg
    #
    package s2t =  xevent_router_ximp;                                          # xevent_router_ximp                    is from   src/lib/x-kit/xclient/src/window/xevent-router-ximp.pkg
    package x2w =  windowsystem_to_xevent_router;                               # windowsystem_to_xevent_router         is from   src/lib/x-kit/xclient/src/window/windowsystem-to-xevent-router.pkg
    package sn  =  xsession_junk;                                               # xsession_junk                         is from   src/lib/x-kit/xclient/src/window/xsession-junk.pkg
#   package dt  =  draw_types;                                                  # draw_types                            is from   src/lib/x-kit/xclient/src/window/draw-types.pkg
    package hw  =  hash_window;                                                 # hash_window                           is from   src/lib/x-kit/xclient/src/window/hash-window.pkg
herein


    package widget_cable {
        #
        stipulate

            Motion_Transition
              =
              { window_point:   g2d::Point,                                             # Mouse position in window coords.
                screen_point:   g2d::Point,                                             # Mouse position in screen coords. XXX BUGGO FIXME shouldn't window and screen points be different types?
                timestamp:      ts::Xserver_Timestamp
              };

            Button_Up_Down
              =
              { mouse_button:   xt::Mousebutton,                                        # Button that is in transition.
                window_point:   g2d::Point,                                             # Mouse position in window coords.
                screen_point:   g2d::Point,                                             # Mouse position in screen coords.
                #                                                                       # NOTE: We may also want the modifier-key state.
                timestamp:      ts::Xserver_Timestamp
              };

            Button_Transition
              =
              { mouse_button:   xt::Mousebutton,                                        # Button that is in transition.
                window_point:   g2d::Point,                                             # Mouse position in window coords.
                screen_point:   g2d::Point,                                             # Mouse position in screen coords. 
                state:          xt::Mousebuttons_State,                                 # List of buttons that are pressed. 
                #                                                                       # NOTE: We may also want the modifier-key state.
                timestamp:      ts::Xserver_Timestamp
              };

        herein

            Mouse_Mail
              = MOUSE_FIRST_DOWN  Button_Up_Down
              | MOUSE_LAST_UP     Button_Up_Down
              #
              | MOUSE_DOWN        Button_Transition
              | MOUSE_UP          Button_Transition
              #
              | MOUSE_MOTION      Motion_Transition
              | MOUSE_ENTER       Motion_Transition
              | MOUSE_LEAVE       Motion_Transition
              #
              | MOUSE_CONFIG_SYNC
              ;
                                                                                        # These envelope-routed messages notify a
                                                                                        # target window of mouse events. An
                                                                                        # extended discussion may be found
                                                                                        # at the bottom of    src/lib/x-kit/widget/old/basic/widget.pkg    
                                                                                        #
                                                                                        # MOUSE_MOTION    
                                                                                        #     Notification of change in mouse position,
                                                                                        #         given in both window and screen coordinates.
                                                                                        #
                                                                                        # MOUSE_DOWN
                                                                                        # MOUSE_UP
                                                                                        # MOUSE_FIRST_DOWN
                                                                                        # MOUSE_LAST_UP
                                                                                        #     Notification of mouse button transitions.
                                                                                        #     including time, position, button changed,
                                                                                        #     and resulting state of all buttons.
                                                                                        #
                                                                                        # MOUSE_ENTER
                                                                                        # MOUSE_LEAVE
                                                                                        #     Notification of mouse entering/leaving window.
                                                                                        #
                                                                                        # MOUSE_CONFIG_SYNC
                                                                                        #     Generated by parent window for barrier
                                                                                        #         synchronization, together with a matching
                                                                                        #     KEY_CONFIG_SYNC on the mouse stream.
                                                                                        #
        end;

        Keyboard_Mail
          = KEY_PRESS    (ks::Keysym, xt::Modifier_Keys_State)
          | KEY_RELEASE  (ks::Keysym, xt::Modifier_Keys_State)
          | KEY_CONFIG_SYNC
          ;
                                                                                        # These envelope-routed messages notify a
                                                                                        # window of keyboard events that occur while
                                                                                        # the keyboard focus was in that window. An
                                                                                        # extended discussion may be found
                                                                                        # at the bottom of    src/lib/x-kit/widget/old/basic/widget.pkg    
                                                                                        #
                                                                                        # KEY_PRESS
                                                                                        # KEY_RELEASE
                                                                                        #     User press/release of a keyboard key.
                                                                                        #     The keysym gives the actual key;
                                                                                        #     the second argument gives the state
                                                                                        #     of control/shift/etc modifier keys.
                                                                                        #
                                                                                        # KEY_CONFIG_SYNC
                                                                                        #     A parent window synchronizing state on
                                                                                        #     all three channels generates this at
                                                                                        #     the same time as MOUSE_CONFIG_SYNC on
                                                                                        #     the mouse stream.
                                                                                        #

        Other_Mail
          = ETC_REDRAW      List( g2d::Box )
          | ETC_RESIZE            g2d::Box
          #
          | ETC_CHILD_BIRTH       sn::Window
          | ETC_CHILD_DEATH       sn::Window
          | ETC_OWN_DEATH
          ;
                                                                                        # Envelopes from our parent window,
                                                                                        # corresponding to X events.  An
                                                                                        # extended discussion may be found
                                                                                        # at the bottom of    src/lib/x-kit/widget/old/basic/widget.pkg    
                                                                                        #
                                                                                        # ETC_REDRAW
                                                                                        #     X Expose event: Need to redraw indicated parts
                                                                                        #     or else all of widget.  Wee 
                                                                                        #     which we need to redraw to restore the display.
                                                                                        #
                                                                                        # ETC_RESIZE
                                                                                        #     Notification of a change in the size of our window.
                                                                                        #
                                                                                        # ETC_CHILD_BIRTH
                                                                                        # ETC_CHILD_DEATH
                                                                                        #     Notification of status change in our childlist.
                                                                                        #     The system guarantees that ETC_CHILD_BIRTH will
                                                                                        #     be seen before any other control messages for
                                                                                        #     that window, and that there will be no control
                                                                                        #     messages for a child after ETC_CHILD_DEATH.  Also,
                                                                                        #     corresponding synchronization messages are passed
                                                                                        #     down the mouse and keyboard streams to allow a
                                                                                        #     barrier style synchronization on configuration
                                                                                        #     changes.  These messages are used in the widget
                                                                                        #     envelope routers to automatically reconfigure message
                                                                                        #     routine in compound widgets.
                                                                                        #
                                                                                        # ETC_OWN_DEATH
                                                                                        #     Our X server window no longer exists.
                                                                                        #

        Mail_To_Mom
          = REQ_RESIZE
          | REQ_DESTRUCTION
          ;
                                                                                        # Messages from child to parent are not in envelopes,
                                                                                        # since they only go one hop and consequently don't
                                                                                        # need the extended routing provided by envelopes.
                                                                                        #
                                                                                        # Note that incautious bidirectional parent<->child
                                                                                        # control communication can easily lead to deadlock!
                                                                                        #

        Envelope(X)                                                                     # An addressed message (with sequence number) 
            =
            ENVELOPE
              { route:    x2w::Envelope_Route,
                seqn:     Int,
                contents: X
              };

                                                                                        # NB: Envelope_Route is defined in src/lib/x-kit/xclient/src/window/xsocket-to-hostwindow-router-old.pkg
                                                                                        #     Probably both it and Envelope() should be defined in an envelope.pkg.   XXX BUGGO FIXME.

        Kidplug
            =
            KIDPLUG
              { from_mouse':    Mailop(  Envelope(     Mouse_Mail ) ),
                from_keyboard': Mailop(  Envelope(  Keyboard_Mail ) ),
                from_other':    Mailop(  Envelope(     Other_Mail ) ),
                #
                to_mom:         Mail_To_Mom -> Mailop( Void )
              };

                                                                                        # NB: 'sink' here should be understood
        Momplug                                                                         # in the electrical engineering sense
            =                                                                           # of current 'sources' and 'sinks'.
            MOMPLUG
              { mouse_sink:     Envelope(    Mouse_Mail ) -> Mailop( Void ),
                keyboard_sink:  Envelope( Keyboard_Mail ) -> Mailop( Void ),
                other_sink:     Envelope(    Other_Mail ) -> Mailop( Void ),
                #
                from_kid':      Mailop( Mail_To_Mom )
              };

        fun make_widget_cable ()                                                        # Void -> (Kid_End, Mom_End) 
            =
            {   from_mouse_slot    = make_mailslot ();
                from_keyboard_slot = make_mailslot ();
                from_mom_slot      = make_mailslot ();
                to_mom_slot        = make_mailslot ();

                fun out_event slot x
                    =
                    put_in_mailslot' (slot, x);

                { kidplug
                      =>
                      KIDPLUG
                        { from_mouse'    => take_from_mailslot'  from_mouse_slot,
                          from_keyboard' => take_from_mailslot'  from_keyboard_slot,
                          from_other'    => take_from_mailslot'  from_mom_slot,
                          #
                          to_mom         => out_event   to_mom_slot
                        },

                  momplug
                      =>
                      MOMPLUG
                        { mouse_sink     => out_event   from_mouse_slot,
                          keyboard_sink  => out_event   from_keyboard_slot,
                          other_sink     => out_event   from_mom_slot,
                          #
                          from_kid'      => take_from_mailslot' to_mom_slot
                        }
                };
            };

                                                                                        # Hop-by-hop envelope routing:
                                                                                        #
        Pass_To(X)
          = TO_SELF(X)                                                                  # Envelope has reached its target window/widget.
          | TO_CHILD  Envelope(X)                                                       # Envelope needs to be passed on down the widget hierarchy.
          ;

                                                                                        # Figure out next step in delivering
                                                                                        # an envelope -- either it is for us,
                                                                                        # or else it needs to be passed to
                                                                                        # one of our kids:
                                                                                        #
        fun route_envelope (ENVELOPE { route=>x2w::ENVELOPE_ROUTE_END _, contents, ... } )
                =>
                TO_SELF contents;

            route_envelope (ENVELOPE { route=>x2w::ENVELOPE_ROUTE(_, rest_of_route), seqn, contents } )
                =>
                TO_CHILD (ENVELOPE { route=>rest_of_route, seqn, contents } );
        end;


        stipulate

            fun next_window (ENVELOPE { route=>x2w::ENVELOPE_ROUTE_END dst, ... } ) =>   dst;
                next_window (ENVELOPE { route=>x2w::ENVELOPE_ROUTE (w, _),  ... } ) =>   w;
            end;

        herein

                                                                                        # Compare envelope to window and return
                                                                                        # TRUE iff envelope should be routed to
                                                                                        # that window for delivery:
                                                                                        #
            fun to_window (envelope, { window_id, ... }: sn::Window )
                =
                (next_window envelope) == window_id;

            exception NO_MATCH_WINDOW;

                                                                                        # Search a list of child windows
                                                                                        # and return the one matching the
                                                                                        # given envelope's delivery route.
                                                                                        #
                                                                                        # Raise NO_MATCH_WINDOW if there
                                                                                        # is no match. (Shouldn't happen.)
                                                                                        #
                                                                                        # This function does a linear sequential
                                                                                        # search which is usually fast enough;
                                                                                        # if a window has too many children for
                                                                                        # this to be sensible, use instead
                                                                                        #
                                                                                        #    next_stop_for_envelope_via_hashtable
                                                                                        #
            fun next_stop_for_envelope  windows  envelope
                =
                find windows
                where 
                    w = next_window envelope;

                    fun find (({ window_id, ... }: sn::Window, x) ! r)
                            =>
                            if (window_id == w)  x;
                            else                 find r;
                            fi;

                        find []
                            =>
                            raise exception NO_MATCH_WINDOW;
                    end;
                end;

                                                                                        # Faster version of above, used in
                                                                                        #
                                                                                        #     src/lib/x-kit/widget/old/basic/xevent-mail-router.pkg
                                                                                        #
            fun next_stop_for_envelope_via_hashtable  map
                =
                {   get =  hw::get_window_id  map;
                    #
                    \\ envelope =   get (next_window envelope);
                };

                                                                                        # Compare envelopes by sequence number.
                                                                                        #
                                                                                        # Since keyboard- and mouse-event envelopes
                                                                                        # get routed down separate streams, it is
                                                                                        # possible for them to be delivered out of
                                                                                        # order.  Most widgets do not care, but those
                                                                                        # which do can use this function to recover
                                                                                        # the original ordering.        
                                                                                        #
            fun envelope_before
                ( ENVELOPE { seqn=>a, ... },
                  ENVELOPE { seqn=>b, ... }
                )
                =
                (a < b);

            fun get_contents_of_envelope (ENVELOPE { contents, ... } )
                =
                contents;

        end;                                                                            # stipulate fun next_window ... 

        # Replace the given input stream with another:
        #
        fun replace_mouse     (KIDPLUG { from_keyboard', from_other',       to_mom, ... }, from_mouse'   ) =   KIDPLUG { from_mouse', from_keyboard', from_other', to_mom };
        fun replace_keyboard  (KIDPLUG { from_mouse',    from_other',       to_mom, ... }, from_keyboard') =   KIDPLUG { from_mouse', from_keyboard', from_other', to_mom };
        fun replace_other     (KIDPLUG { from_mouse',    from_keyboard',    to_mom, ... }, from_other'   ) =   KIDPLUG { from_mouse', from_keyboard', from_other', to_mom };

        exception MAILOP_ON_IGNORED_STREAM;

        # Create new kidplug that ignores the given stream.
        # Using (i.e. doing a mailop on) an ignored stream
        # will raise an exception, but ignoring a stream twice
        # will work.
        #
        stipulate

            fun ignore mailop
                =
                {
                    ignore_mailop
                        =
                        always' ()
                            ==>
                           {.  raise exception  MAILOP_ON_IGNORED_STREAM;  };

                    fun loop ()
                        =
                        for (;;) {
                            block_until_mailop_fires  mailop;
                        };

                    make_thread "widget_cable" {.

                        loop ()
                        except
                            _ = ();
                    };

                    ignore_mailop;
                };
        herein

            fun ignore_mouse              (KIDPLUG { from_mouse', from_keyboard', from_other', to_mom } ) =   KIDPLUG { from_mouse'=>ignore from_mouse', from_keyboard',                        from_other',                     to_mom };
            fun ignore_keyboard           (KIDPLUG { from_mouse', from_keyboard', from_other', to_mom } ) =   KIDPLUG { from_mouse',                     from_keyboard'=>ignore from_keyboard', from_other',                     to_mom };
            fun ignore_mouse_and_keyboard (KIDPLUG { from_mouse', from_keyboard', from_other', to_mom } ) =   KIDPLUG { from_mouse'=>ignore from_mouse', from_keyboard'=>ignore from_keyboard', from_other',                     to_mom };
            fun ignore_all                (KIDPLUG { from_mouse', from_keyboard', from_other', to_mom } ) =   KIDPLUG { from_mouse'=>ignore from_mouse', from_keyboard'=>ignore from_keyboard', from_other'=>ignore from_other', to_mom };

        end;

        # An input stream that never produces messages 
        #
        my null_stream:    Mailop( Envelope(X) )
            =
            threadkit::never';

        # Eat mouse mail while the given
        # mouse-button state predicate is satisfied.
        #
        # Note that the mouse stream may need
        # to be wrapped by "get_contents_of_envelope"
        #
        fun while_mouse_state
                predicate
                (init_state, m)
            =
            loop  init_state
            where 

                fun loop state
                    =
                    if (predicate state)
                        #                   
                        case (block_until_mailop_fires  m)
                            #
                            MOUSE_FIRST_DOWN { mouse_button, ... } =>   loop (kb::make_mousebutton_state [mouse_button]);
                            MOUSE_LAST_UP _                        =>   loop (xt::MOUSEBUTTON_STATE 0u0);
                            MOUSE_DOWN { state, ... }              =>   loop state;
                            MOUSE_UP { state, ... }                =>   loop state;
                            _                                      =>   loop state;
                        esac;
                   fi;

            end;
    };          # package widget_cable
end;            # stipulate


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext