PreviousUpNext

15.4.1468  src/lib/x-kit/xclient/pkg/window/draw-imp.pkg

## draw-imp.pkg

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



# TODO
#  - optimize the case where successive DOPs use the same pen.
#  - all window configuration operations (Resize, Move, Pop/Push, Create &
#    Delete) should go through the draw master. XXX BUGGO FIXME


stipulate
    include threadkit;                                  # threadkit                     is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
    package s2t =  xsocket_to_topwindow_router;         # xsocket_to_topwindow_router   is from   src/lib/x-kit/xclient/pkg/window/xsocket-to-topwindow-router.pkg
    package xg  =  xgeometry;                           # xgeometry                     is from   src/lib/std/2d/xgeometry.pkg
    package p2g =  pen_to_gcontext_imp;                 # pen_to_gcontext_imp           is from   src/lib/x-kit/xclient/pkg/window/pen-to-gcontext-imp.pkg
    package m1  =  oneshot_maildrop;                    # oneshot_maildrop              is from   src/lib/src/lib/thread-kit/src/core-thread-kit/oneshot-maildrop.pkg
    package pg  =  pen_guts;                            # pen_guts                      is from   src/lib/x-kit/xclient/pkg/window/pen-guts.pkg
    package v2w =  value_to_wire;                       # value_to_wire                 is from   src/lib/x-kit/xclient/pkg/wire/value-to-wire.pkg
    package vu8 =  vector_of_one_byte_unts;             # vector_of_one_byte_unts       is from   src/lib/std/src/vector-of-one-byte-unts.pkg
    package xok =  xsocket;                             # xsocket                       is from   src/lib/x-kit/xclient/pkg/wire/xsocket.pkg
    package xt  =  xtypes;                              # xtypes                        is from   src/lib/x-kit/xclient/pkg/wire/xtypes.pkg
    package xtr =  xlogger;                             # xlogger                       is from   src/lib/x-kit/xclient/pkg/stuff/xlogger.pkg
    #
    trace =  xtr::log_if  xtr::io_logging;              # Conditionally write strings to tracing.log or whatever.
herein


    package   draw_imp
    : (weak)  Draw_Imp                                  # Draw_Imp                      is from   src/lib/x-kit/xclient/pkg/window/draw-imp.api
    {
        package s {
            #
            Mapped_State                                # These are the messages we receive via our mappedstate_slot from xsession and topwindow-to-widget-router.
              = TOPWINDOW_IS_NOW_UNMAPPED
              | TOPWINDOW_IS_NOW_MAPPED
              | FIRST_EXPOSE
              ;
        };

        package t {
            #
            Poly_Text
              = TEXT  (Int, String)
              | FONT  xt::Font_Id
              ;
        };

        package o {
            Draw_Opcode
              = POLY_POINT     (Bool, List( xg::Point ))
              | POLY_LINE      (Bool, List( xg::Point ))
              | FILL_POLY      (xt::Shape, Bool, List( xg::Point ))
              | POLY_SEG       List( xg::Line )
              | POLY_BOX       List( xg::Box )
              | POLY_FILL_BOX  List( xg::Box )
              | POLY_ARC       List( xg::Arc  )
              | POLY_FILL_ARC  List( xg::Arc  )
              | COPY_AREA
                    ( xg::Point,
                      xt::Xid,
                      xg::Box,
                      Oneshot_Maildrop( Void -> List( xg::Box ) )
                    )
              | COPY_PLANE
                    ( xg::Point,
                      xt::Xid,
                      xg::Box,
                      Int,
                      Oneshot_Maildrop (Void -> List( xg::Box ) )
                    )
              | COPY_PMAREA    (xg::Point, xt::Xid, xg::Box)
              | COPY_PMPLANE   (xg::Point, xt::Xid, xg::Box, Int)
              | CLEAR_AREA      xg::Box
              | PUT_IMAGE  {
                    to_point: xg::Point,
                    size:     xg::Size,
                    depth:    Int,
                    lpad:     Int,
                    format:   xt::Image_Format,
                    data:     vu8::Vector
                  }
              | POLY_TEXT8   (xt::Font_Id, xg::Point, List( t::Poly_Text ))
              | IMAGE_TEXT8  (xt::Font_Id, xg::Point, String)
              ;
        };

        package i {
            #
            Destroy_Item
              = WINDOW  xt::Window_Id
              | PIXMAP  xt::Pixmap_Id
              ;
        };

        package d {
            Draw_Op
              = DRAW  {
                  to:    xt::Xid,
                  pen:   pg::Pen,
                  op:    o::Draw_Opcode
                }
              | LOCK_WINDOW_FOR_RUBBERBANDING  {
                  draw_slot:       Mailslot( Draw_Op ),         # Stream of drawing commands for the overlay.
                  release':        Mailop( Void )                       # Overlay release mailop.
                }
              | DESTROY    i::Destroy_Item
              | FLUSH      Oneshot_Maildrop( Void )
              | THREAD_ID  Oneshot_Maildrop( Int  )
              | BATCHING_ON
              | BATCHING_OFF
              ;
        };

        /* +DEBUG 
        fun dop_to_string (o::POLY_POINT    _) = "PolyPoint";
            dop_to_string (o::POLY_LINE     _) = "PolyLine";
            dop_to_string (o::POLY_SEG      _) = "PolySeg";
            dop_to_string (o::FILL_POLY     _) = "PolyFillPoly";
            dop_to_string (o::POLY_BOX      _) = "PolyRect";
            dop_to_string (o::POLY_FILL_BOX _) = "PolyFillRect";
            dop_to_string (o::POLY_ARC      _) = "PolyArc";
            dop_to_string (o::POLY_FILL_ARC _) = "PolyFillArc";
            dop_to_string (o::COPY_AREA     _) = "CopyArea";
            dop_to_string (o::COPY_PLANE    _) = "CopyPlane";
            dop_to_string (o::COPY_PMAREA   _) = "CopyPMArea";
            dop_to_string (o::COPY_PMPLANE  _) = "CopyPMPlane";
            dop_to_string (o::CLEAR_AREA    _) = "ClearArea";
            dop_to_string (o::PUT_IMAGE     _) = "PutImage";
            dop_to_string (o::POLY_TEXT8    _) = "PolyText8";
            dop_to_string (o::IMAGE_TEXT8   _) = "ImageText8";
        end;
         -DEBUG */


        stipulate

            # Maximum number of drawing commands
            # to buffer before flushing.
            #
            full_buffer_size = 16;

            my (|)  =  unt::bitwise_or;
            my (<<) =  unt::(<<);

            infix val | <<;

            # Officially Mythryl does not have pointer equality,
            # but we do it here anyway for speed.  Naughty! :-)
            #
            fun pen_eq
                ( a:  pg::Pen,
                  b:  pg::Pen
                )
                =
                {   ((unsafe::cast a): Int)
                    ==
                    ((unsafe::cast b): Int);
                };

            # Bitmasks for the various components of a pen.
            # These should track the slot numbers given in PenValues.

            pen_function        = (0u1 << 0u0);
            pen_plane_mask      = (0u1 << 0u1);

            pen_foreground      = (0u1 << 0u2);
            pen_background      = (0u1 << 0u3);

            pen_line_width      = (0u1 << 0u4);
            pen_line_style      = (0u1 << 0u5);

            pen_cap_style       = (0u1 << 0u6);
            pen_join_style      = (0u1 << 0u7);

            pen_fill_style      = (0u1 << 0u8);
            pen_fill_rule       = (0u1 << 0u9); 

            pen_tile            = (0u1 << 0u10);
            pen_stipple         = (0u1 << 0u11);

            pen_tile_stip_origin= (0u1 << 0u12);
            pen_subwindow_mode  = (0u1 << 0u13);

            pen_clip_origin     = (0u1 << 0u14);
            pen_clip_mask       = (0u1 << 0u15);

            pen_dash_offset     = (0u1 << 0u16);
            pen_dash_list       = (0u1 << 0u17);

            pen_arc_mode        = (0u1 << 0u18);
            pen_exposures       = 0u0; #  (0u1 << 0u19) 

            stipulate
                my standard_pen_components                      # The standard pen components used by most ops.
                      = pen_function
                      | pen_plane_mask
                      | pen_subwindow_mode
                      | pen_clip_origin
                      | pen_clip_mask
                      | pen_foreground
                      | pen_background
                      | pen_tile
                      | pen_stipple
                      | pen_tile_stip_origin
                      ;

                my standard_linedrawing_pen_components          # The pen components used by line-drawing operations.
                      =  standard_pen_components
                      | pen_line_width
                      | pen_line_style
                      | pen_cap_style
                      | pen_join_style
                      | pen_fill_style
                      | pen_dash_offset
                      | pen_dash_list
                      ;
            herein

                fun pen_vals_used (o::POLY_POINT    _)  =>  standard_pen_components;
                    pen_vals_used (o::POLY_LINE     _)  =>  standard_linedrawing_pen_components;
                    pen_vals_used (o::POLY_SEG      _)  =>  standard_linedrawing_pen_components;
                    pen_vals_used (o::FILL_POLY     _)  => (standard_pen_components|pen_fill_style);
                    pen_vals_used (o::POLY_BOX      _)  =>  standard_linedrawing_pen_components;
                    pen_vals_used (o::POLY_FILL_BOX _)  => (standard_pen_components|pen_fill_style);
                    pen_vals_used (o::POLY_ARC      _)  =>  standard_linedrawing_pen_components;
                    pen_vals_used (o::POLY_FILL_ARC _)  => (standard_pen_components|pen_fill_style);
                    pen_vals_used (o::COPY_AREA     _)  =>  standard_pen_components|pen_exposures;
                    pen_vals_used (o::COPY_PLANE    _)  =>  standard_pen_components|pen_exposures;
                    pen_vals_used (o::COPY_PMAREA   _)  =>  standard_pen_components;
                    pen_vals_used (o::COPY_PMPLANE  _)  =>  standard_pen_components;
                    pen_vals_used (o::CLEAR_AREA    _)  => 0u0;
                    pen_vals_used (o::PUT_IMAGE     _)  =>  standard_pen_components;
                    pen_vals_used (o::POLY_TEXT8    _)  => (standard_pen_components|pen_fill_style);
                    pen_vals_used (o::IMAGE_TEXT8   _)  =>  standard_pen_components;
                end;
            end;

#           stipulate

#               include value_to_wire;

#           herein

                fun send_draw_op (send_xrequest, send_xrequest_and_handle_exposures)
                    =
                    fn  (to, gc_id, _, o::POLY_POINT (rel, points))
                            =>
#                           send_xrequest (v2w::encode_poly_point { drawable=>to, gc_id, items=>points, relative=>rel } );      # Replaced by below code.
                            {
                                # Discovered there's a limit to the number
                                # of points that can be sent to the X server.
                                # It's less than 65535, but at least 65400.
                                # I figure this is close enough:              -- Hue White 2011-11-24
                                #
                                x_limit = 65400;

                                send_xrequests points
                                where
                                    fun send_xrequests points
                                        =
                                        if (list::length(points) <= x_limit)
                                            #
                                            send_xrequest (v2w::encode_poly_point { drawable=>to, gc_id, items=>points, relative=>rel } );
                                        else
                                            send_xrequest (v2w::encode_poly_point { drawable=>to, gc_id, items=>(list::take_n(points, x_limit)), relative=>rel } );
                                            send_xrequests (list::drop_n(points, x_limit));
                                        fi;
                                end;
                            };

                        (to, gc_id, _, o::POLY_LINE (rel, points))
                            =>
                            send_xrequest (v2w::encode_poly_line { drawable=>to, gc_id, items=>points, relative=>rel } );

                        (to, gc_id, _, o::POLY_SEG lines)
                            =>
                            send_xrequest (v2w::encode_poly_segment { drawable=>to, gc_id, items=>lines } );

                        (to, gc_id, _, o::FILL_POLY (shape, rel, points))
                            =>
#                           send_xrequest (v2w::encode_fill_poly { drawable=>to, gc_id, points, relative=>rel, shape } );
                            {
                                msg = v2w::encode_fill_poly { drawable=>to, gc_id, points, relative=>rel, shape };
trace  .{ sprintf "XYZZY draw_imp::send_draw_op/FILL_POLY doing send_xrequest, msg s=%s" (xok::bytes_to_hex msg); };
                                send_xrequest msg;
trace  .{ sprintf "XYZZY draw_imp::send_draw_op/FILL_POLY done  send_xrequest, msg s=%s" (xok::bytes_to_hex msg); };
                            };

                        (to, gc_id, _, o::POLY_BOX boxes)
                            =>
                            send_xrequest (v2w::encode_poly_box { drawable=>to, gc_id, items=>boxes } );

                        (to, gc_id, _, o::POLY_FILL_BOX boxes)
                            =>
                            send_xrequest (v2w::encode_poly_fill_box { drawable=>to, gc_id, items=>boxes } );

                        (to, gc_id, _, o::POLY_ARC arcs)
                            =>
                            send_xrequest (v2w::encode_poly_arc { drawable=>to, gc_id, items=>arcs } );

                        (to, gc_id, _, o::POLY_FILL_ARC arcs)
                            =>
                            send_xrequest (v2w::encode_poly_fill_arc { drawable=>to, gc_id, items=>arcs } );

                        (to, gc_id, _, o::COPY_AREA (pt, from, box, sync_v))
                            =>
                            {   my (p, size)
                                    =
                                    xg::box::upperleft_and_size  box;

                                send_xrequest_and_handle_exposures (v2w::encode_copy_area { gc_id, from, to, from_point=>p, size, to_point=>pt }, sync_v);
                            };

                        (to, gc_id, _, o::COPY_PLANE (pt, from, box, plane, sync_v))
                            =>
                            {   my (p, size)
                                    =
                                    xg::box::upperleft_and_size  box;

                                send_xrequest_and_handle_exposures (v2w::encode_copy_plane { gc_id, from, to, from_point=>p, size, to_point=>pt, plane }, sync_v);
                            };

                        (to, gc_id, _, o::COPY_PMAREA (pt, from, box))
                            =>
                            {   my (p, size)
                                    =
                                    xg::box::upperleft_and_size  box;

                                send_xrequest (v2w::encode_copy_area { gc_id, from, to, from_point=>p, size, to_point=>pt });
                            };

                        (to, gc_id, _, o::COPY_PMPLANE (pt, from, box, plane))
                            =>
                            {   my (p, size)
                                    =
                                    xg::box::upperleft_and_size  box;

                                send_xrequest (v2w::encode_copy_plane { gc_id, from, to, from_point=>p, size, to_point=>pt, plane });
                            };

                        (to, _, _, o::CLEAR_AREA box)
                            =>
                            send_xrequest (v2w::encode_clear_area { window_id=>to, box, exposures => FALSE } );

                        (to, gc_id, _, o::PUT_IMAGE im)
                            =>
                            send_xrequest
                                (v2w::encode_put_image
                                  { drawable => to,
                                    gc_id,
                                    depth  => im.depth,
                                    to     => im.to_point,
                                    size   => im.size,
                                    lpad   => im.lpad,
                                    format => im.format,
                                    data   => im.data
                                  }
                                );

                        (to, gc_id, cur_fid, o::POLY_TEXT8 (fid, point, txt_items))
                            =>
                            {   last_fid
                                    =
                                    f (fid, txt_items)
                                    where
                                        fun f (last_fid, [])               =>  last_fid;
                                            f (last_fid, (t::FONT id) ! r) =>  f (id, r);
                                            f (last_fid, _ ! r)            =>  f (last_fid, r);
                                        end;
                                    end;

                                txt_items
                                    =
                                    last_fid == cur_fid
                                    ?? txt_items
                                    :: txt_items @ [t::FONT cur_fid];

                                txt_items
                                    =
                                    fid == cur_fid
                                    ?? txt_items
                                    :: (t::FONT fid) ! txt_items;


                                fun split_delta (0, l)
                                        =>
                                        l;

                                    split_delta (i, l)
                                        =>
                                        if (i < -128)

                                             split_delta (i+128, -128 ! l);
                                        else
                                             i > 127
                                             ?? split_delta (i - 127, 127 ! l)
                                             :: i ! l;
                                        fi;
                                end;


                                # Split a string into legal
                                # lengths for a PolyText8 command 
                                #
                                fun split_text ""
                                        =>
                                        [];

                                    split_text s
                                        =>
                                        {   n = string::length s;

                                            fun split (i, l)
                                                =
                                                n - i  > 254
                                                ??  split (i+254,  substring (s, i, 254) ! l)
                                                ::  list::reverse (substring (s, i, n-i) ! l);

                                            n > 254  ??  split (0, [])
                                                     ::  [s];
                                        };
                                end;


                                fun split_item (t::FONT id)
                                        =>
                                        [xt::FONT_ITEM id];

                                    split_item (t::TEXT (delta, s))
                                        =>
                                        case (split_delta (delta, []), split_text s)
                                            #
                                            ([], []) =>   [];
                                            ([], sl) =>   (map (fn s = xt::TEXT_ITEM (0,  s)) sl);
                                            (dl, []) =>   (map (fn n = xt::TEXT_ITEM (n, "")) dl);

                                            ([d], s ! sr)
                                                =>
                                                (xt::TEXT_ITEM (d, s) ! (map (fn s = xt::TEXT_ITEM (0, s)) sr));

                                            (d ! dr, s ! sr)
                                                =>
                                                ((map (fn n = xt::TEXT_ITEM (n, "")) dr)
                                                 @ (xt::TEXT_ITEM (d, s) ! (map (fn s = xt::TEXT_ITEM (0, s)) sr)));
                                        esac;

                                end;

                                do_items
                                    =
                                    fold_right
                                        (fn (item, l) =  (split_item item) @ l)
                                        [];

                                send_xrequest
                                    (v2w::encode_poly_text8
                                        {
                                          drawable=>to,
                                          gc_id,
                                          point,
                                          items=>(do_items txt_items)
                                        }
                                    );
                            };

                        (to, gc_id, _, o::IMAGE_TEXT8(_, point, string))
                            =>
                            send_xrequest (v2w::encode_image_text8 { drawable=>to, gc_id, point, string } );
                    end;

#           end;                                # stipulate


            # Flush a list of drawing commands out to the sequencer.
            # This means aquiring actual X-server graphics contexts
            # for the operations from graphics_context_cache:
            #
            fun flush_buf (gc_cache, connection)
                =
                flush
                where 

                    Gc_Info
                      = NO_GC
                      | NO_FONT
                      | WITH_FONT xt::Font_Id
                      | SET_FONT  xt::Font_Id
                      ;

                    alloc_gc =  p2g::allocate_graphics_context  gc_cache;
                    free_gc  =  p2g::free_graphics_context      gc_cache;

                    alloc_gc_with_font    =   p2g::allocate_graphics_context_with_font     gc_cache;
                    alloc_gc_and_set_font =   p2g::allocate_graphics_context_and_set_font  gc_cache;
                    free_gc_and_font      =   p2g::free_graphics_context_and_font          gc_cache;

                    send_dop
                        =
                        send_draw_op
                          ( xok::send_xrequest                       connection,
                            xok::send_xrequest_and_handle_exposures  connection
                          );

                    # Our first argument is a list of X drawing operations
                    # to be performed.  For efficiency, we want to avoid
                    # switching graphics contexts needlessly, so we break our
                    # argument draw-op list into a sequence of sublists,
                    # each of which can be performed using a single gc.
                    # 
                    fun batch_drawops ([], results)
                            =>
                            results;                                                                    # No more input -- done. (Why don't we reverse it?)

                        batch_drawops
                            ( draw_ops as (first_op ! _),                                               # Input drawops list.
                              results                                                                   # Batch accumulator.
                            )
                            =>
                            {   fun gc_usage_of (o::CLEAR_AREA _)            =>   NO_GC;
                                    gc_usage_of (o::POLY_TEXT8  (fid, _, _)) =>   WITH_FONT fid;
                                    gc_usage_of (o::IMAGE_TEXT8 (fid, _, _)) =>   SET_FONT  fid;
                                    gc_usage_of op                           =>   NO_FONT;
                                end;


                                fun extend_mask (m, op)
                                    =
                                    m | (pen_vals_used op);


                                # We are given a list of X drawing operations to do.
                                # Our job is to find the maximal prefix of this list
                                # which can all use the same graphics context:
                                # 
                                fun find_max_prefix (arg as ([], _, _, _, _))
                                        =>
                                        arg;

                                    find_max_prefix (arg as ( { to, pen, op } ! rest, gc_usage, first_pen, used_mask, prefix))
                                        =>
                                        if (not (pen_eq (pen, first_pen)))
                                            #
                                            arg;
                                        else
                                            case (gc_usage, gc_usage_of op)
                                                #
                                                (_, NO_GC)
                                                    =>
                                                    find_max_prefix (rest, gc_usage, first_pen, used_mask,                                (to, op) ! prefix);

                                                (NO_GC, new_gc_usage)
                                                    =>
                                                    find_max_prefix (rest, new_gc_usage, first_pen, pen_vals_used op,                     (to, op) ! prefix);

                                                (_, NO_FONT)
                                                    =>
                                                    find_max_prefix (rest, gc_usage, first_pen, extend_mask (used_mask, op),              (to, op) ! prefix);

                                                (SET_FONT fid, WITH_FONT _)
                                                    =>
                                                    find_max_prefix (rest, SET_FONT fid, first_pen, extend_mask (used_mask, op),      (to, op) ! prefix);

                                                (_, WITH_FONT fid)
                                                    =>
                                                    find_max_prefix (rest, WITH_FONT fid, first_pen, extend_mask (used_mask, op),     (to, op) ! prefix);

                                                (SET_FONT fid1, SET_FONT fid2)
                                                    =>
                                                    if (fid1 == fid2)
                                                        #
                                                        find_max_prefix (rest, SET_FONT fid1, first_pen, extend_mask (used_mask, op), (to, op) ! prefix);
                                                    else
                                                        arg;
                                                    fi;

                                                (_, SET_FONT fid)
                                                    =>
                                                    find_max_prefix (rest, SET_FONT fid, first_pen, extend_mask (used_mask, op),      (to, op) ! prefix);
                                            esac;
                                        fi;
                                end;

                                my (remaining_draw_ops, gc_usage, pen, mask, max_prefix)
                                    =
                                    find_max_prefix (draw_ops, NO_GC, first_op.pen, 0u0, []);

                                batch_drawops (remaining_draw_ops, (gc_usage, pen, mask, max_prefix) ! results);
                           };
                    end;                                                # fun batch_drawops


                    fun send_draw_ops (gc, initial_fid)
                        =
                        draw
                        where 
                            fun draw []
                                    =>
                                    ();

                                draw ((to, op) ! r)
                                    =>
                                    {   send_dop (to, gc, initial_fid, op);
                                        draw r;
                                    };
                            end;

                        end;


                    xid0 =   xt::XID 0u0;


                    fun draw_batch (NO_GC, _, _, ops)
                            =>
                            send_draw_ops (xid0, xid0) ops;

                        draw_batch (NO_FONT, pen, mask, ops)
                            =>
                            {   gc =   alloc_gc { pen, used => mask };
                                #
                                send_draw_ops (gc, xid0) ops;
                                #
                                free_gc gc;
                            };

                        draw_batch (WITH_FONT fid, pen, mask, ops)
                            =>
                            {   my (gc, init_fid)
                                    =
                                    alloc_gc_with_font { pen, used => mask, fid };

                                #
                                send_draw_ops (gc, init_fid) ops;
                                #
                                free_gc_and_font gc;
                            };

                        draw_batch (SET_FONT fid, pen, mask, ops)
                            =>
                            {   gc =   alloc_gc_and_set_font { pen, used => mask, fid };
                                #
                                send_draw_ops (gc, fid) ops;
                                #
                                free_gc_and_font gc;
                            };
                    end;

                    draw_all_batches =  apply  draw_batch;

                    fun flush buf
                        =
                        {   draw_all_batches (batch_drawops (buf, []));
                            xok::flush connection;
                        };

                end;                    # fun flush_buf 


            # Insert a drawing command into the buffer,
            # checking for possible batching of operations.
            # BATCHING NOT IMPLEMENTED YET      XXX BUGGO FIXME
            #
            fun batch_cmd (commands_in_buffer, cmd, last, rest)
                =
                (commands_in_buffer+1, cmd ! last ! rest);


            fun destroy_window xsocket (i::WINDOW window_id)
                    =>
                    {   xok::send_xrequest xsocket (v2w::encode_destroy_window { window_id } );
                        xok::flush         xsocket;
                    };

                destroy_window xsocket (i::PIXMAP pm_id)
                    =>
                    {   xok::send_xrequest xsocket (v2w::encode_free_pixmap { pixmap => pm_id } );
                        xok::flush         xsocket;
                    };
            end;


            # Create an overlay buffer on the drawing command stream.
            #
            # This buffers operations aimed at locked windows and passes
            # the others onto the draw imp.  release' is enabled when
            # the overlay is released; this causes the buffer to flush
            # its buffered messages.
            #
            # A mailop is returned that signals flush-complete.
            #
            fun make_overlay_buffer (lock_imp, new_slot, old_slot', release')
                =
                {   flush_done_oneshot = make_oneshot_maildrop ();

                    fun release buf
                        =
                        {   list::apply (fn msg = give (new_slot, msg)) (list::reverse buf);
                            give (new_slot, d::FLUSH flush_done_oneshot);
                        };

                    fun loop buf
                        =
                        {   fun filter_msg (to, m)
                                =
                                if (s2t::window_is_locked (lock_imp, to))
                                    #
                                    loop (m ! buf);
                                else
                                    give  (new_slot, m);
                                    loop buf;
                                fi;

                            fun filter (m as d::DRAW { to, ... } )
                                    =>
                                    filter_msg (to, m);

                                filter (d::FLUSH flush_done_oneshot)
                                    =>
                                    {   give  (new_slot, d::FLUSH flush_done_oneshot);
                                        loop buf;
                                    };

                                filter (d::THREAD_ID thread_id_oneshot)
                                    =>
                                    {   give  (new_slot, d::THREAD_ID thread_id_oneshot);
                                        loop buf;
                                    };

                                filter (d::LOCK_WINDOW_FOR_RUBBERBANDING _)
                                    =>
                                    xgripe::impossible "[multiple overlays not supported]";

                                filter (m as (d::DESTROY (i::WINDOW wid)))
                                    =>
                                    filter_msg (wid, m);

                                filter _
                                    =>
                                    xgripe::impossible "[unsupported message in DrawMaster::make_ovrlay_buffer]";
                            end;

                            select [
                                old_slot' ==>  filter,
                                release'  ==>  .{ release buf; }
                            ];
                        };

                        xlogger::make_thread  "OverlayBuffer"  .{ loop []; };

                        get' flush_done_oneshot;
                };                                                       #  fun make_overlay_buffer 

        herein

            # We get called two places:
            #     src/lib/x-kit/xclient/pkg/window/xsession.pkg
            #     src/lib/x-kit/xclient/pkg/window/topwindow-to-widget-router.pkg
            #
            fun make_draw_imp (set_mappedstate', gc_cache, lock_imp, xsocket)
                =
                {   # Need to check state transitions to insure no deadlock *  XXX BUGGO FIXME

                    plea_slot    =   make_mailslot ();
                    plea'        =   take'  plea_slot;

                    flush        =   flush_buf (gc_cache, xsocket);

                    flush_delay' =   timeout_in' (time::from_milliseconds 40);

                    destroy      =   destroy_window xsocket;

                    # The draw_imp has two operating states,
                    # depending on whether its topwindow
                    # is mapped or unmapped, each represented
                    # by a loop function.

                    # Unmapped state is easy -- we just
                    # discard all DRAW commands:   :-)
                    #   
                    fun topwindow_is_unmapped_loop ()
                        =
                        {   fun set_mappedstate s::TOPWINDOW_IS_NOW_MAPPED
                                    =>
                                    topwindow_is_mapped_loop (0, []);

                                set_mappedstate s::TOPWINDOW_IS_NOW_UNMAPPED
                                    =>
                                    topwindow_is_unmapped_loop ();

                                set_mappedstate _
                                    =>
                                    (xgripe::impossible "[draw_mp (unmapped): bad config command]");
                            end;


                            fun do_plea (d::DESTROY id)
                                    =>
                                    {   destroy id;
                                        topwindow_is_unmapped_loop ();
                                    };

                                do_plea (d::LOCK_WINDOW_FOR_RUBBERBANDING { draw_slot, release' } )
                                    =>
                                    overlay (FALSE, draw_slot, release');

                                do_plea _
                                    =>
                                    topwindow_is_unmapped_loop ();
                            end;

                            # xlogger::trace (xlogger::dmTM, fn => ["drawimp: serverUnmapped\n"]); 
                            #
                            select [
                                plea'            ==>  do_plea,
                                set_mappedstate' ==>  set_mappedstate
                            ];
                         }

                    also
                    fun topwindow_is_mapped_loop (_, [])
                            =>
                            {   fun set_mappedstate s::TOPWINDOW_IS_NOW_UNMAPPED
                                        =>
                                        topwindow_is_unmapped_loop ();

                                    set_mappedstate s::TOPWINDOW_IS_NOW_MAPPED
                                        =>
                                        topwindow_is_mapped_loop (0, []);

                                    set_mappedstate _
                                        =>
                                        (xgripe::impossible "[drawimp (mapped): bad mapped-state command]");
                                end;


                                fun do_plea (d::DRAW m)
                                        =>
                                        topwindow_is_mapped_loop (1, [m]);

                                    do_plea (d::FLUSH flush_done_oneshot)       # Buffer is empty so FLUSH is a no-op.
                                        =>
                                        {
trace  .{ sprintf "XYZZY draw_imp::topwindow_is_mapped_loop: do_plea/FLUSH: buffer is empty, flush is a no-op"; };
                                            set (flush_done_oneshot, ());
                                            topwindow_is_mapped_loop (0, []);
                                        };

                                    do_plea (d::THREAD_ID thread_id_oneshot)
                                        =>
                                        {
                                            set (thread_id_oneshot, thread::get_current_thread's_id());
                                            topwindow_is_mapped_loop (0, []);
                                        };

                                    do_plea (d::LOCK_WINDOW_FOR_RUBBERBANDING { draw_slot, release' } )
                                        =>
                                        overlay (TRUE, draw_slot, release');

                                    do_plea (d::DESTROY id)
                                        =>
                                        {   destroy id;
                                            topwindow_is_mapped_loop (0, []);
                                        };

                                    do_plea _
                                        =>
                                        xgripe::impossible "DrawMaster: user batching not supported yet";
                                end;


                                #  xlogger::trace (xlogger::dmTM, fn => ["DrawMaster: serverMapped (empty)\n"]); 
                                #
                                select [
                                    plea'            ==>  do_plea,
                                    set_mappedstate' ==>  set_mappedstate
                                ];
                            };

                       topwindow_is_mapped_loop (commands_in_buffer, buf as (last_command ! rest))
                           =>
                           {   fun set_mappedstate s::TOPWINDOW_IS_NOW_UNMAPPED
                                        =>
                                        topwindow_is_unmapped_loop ();

                                   set_mappedstate s::TOPWINDOW_IS_NOW_MAPPED
                                        =>
                                        topwindow_is_mapped_loop (commands_in_buffer, buf);

                                   set_mappedstate _
                                        =>
                                        (xgripe::impossible "[drawimp (mapped): bad mapped-state command]");
                               end;


                               fun do_plea (d::DRAW m)
                                        =>
                                        topwindow_is_mapped_loop (batch_cmd (commands_in_buffer, m, last_command, rest));

                                   do_plea (d::FLUSH flush_done_oneshot)
                                        =>
                                        {
trace  .{ sprintf "XYZZY draw_imp::topwindow_is_mapped_loop: do_plea/FLUSH: buffer is NON-empty, doing flush"; };
                                            flush buf;
trace  .{ sprintf "XYZZY draw_imp::topwindow_is_mapped_loop: do_plea/FLUSH: buffer is NON-empty, done  flush"; };
                                            set (flush_done_oneshot, ());
                                            topwindow_is_mapped_loop (0, []);
                                        };

                                   do_plea (d::THREAD_ID  thread_id_oneshot)
                                        =>
                                        {
                                            set (thread_id_oneshot, thread::get_current_thread's_id());
                                            topwindow_is_mapped_loop (commands_in_buffer, buf);
                                        };

                                   do_plea (d::LOCK_WINDOW_FOR_RUBBERBANDING { draw_slot, release' } )
                                        =>
                                        {   flush buf;
                                            overlay (TRUE, draw_slot, release');
                                        };

                                   do_plea (d::DESTROY id)
                                        =>
                                        {   flush buf;
                                            destroy id;
                                            topwindow_is_mapped_loop (0, []);
                                        };

                                   do_plea _
                                        =>
                                        xgripe::impossible "draw_imp: user batching not supported yet";
                               end;


                               #  xlogger::trace (xlogger::dmTM, fn => ["DrawMaster: serverMapped (", makestring (length buf), ")\n"]); 

                               if (commands_in_buffer > full_buffer_size)
                                   #
                                   flush buf;
                                   topwindow_is_mapped_loop (0, []);
                               else
                                   select [
                                       flush_delay'     ==>   (fn _ = {  flush buf;  topwindow_is_mapped_loop (0, []);  }),
                                       plea'            ==>   do_plea,
                                       set_mappedstate' ==>   set_mappedstate
                                   ];
                               fi;
                          };
                    end 

                    also
                    fun overlay (is_mapped, new_slot, release')
                        =
                        {   flush_done'
                                =
                                make_overlay_buffer (lock_imp, new_slot, plea', release');

                            new_plea'
                                =
                                take'  new_slot;

                            fun topwindow_is_unmapped_overlay_loop ()
                                =
                                {   fun set_mappedstate s::TOPWINDOW_IS_NOW_MAPPED   => topwindow_is_mapped_overlay_loop (0, []);
                                        set_mappedstate s::TOPWINDOW_IS_NOW_UNMAPPED => topwindow_is_unmapped_overlay_loop();
                                        #
                                        set_mappedstate _ => xgripe::impossible "[drawimp (unmapped-overlay): bad mapped-state command]";
                                    end;

                                    fun do_plea (d::DESTROY id)
                                            =>
                                            {   destroy id;
                                                topwindow_is_unmapped_overlay_loop ();
                                            };

                                        do_plea _
                                            =>
                                            topwindow_is_unmapped_overlay_loop ();
                                    end;

                                    #  xlogger::trace (xlogger::dmTM, fn => ["draw_imp: overlayUnmapped\n"]); 

                                    select [
                                        flush_done'      ==>  (fn _ = FALSE),
                                        new_plea'        ==>  do_plea,
                                        set_mappedstate' ==>  set_mappedstate
                                    ];
                                }

                            also
                            fun topwindow_is_mapped_overlay_loop (_, [])
                                    =>
                                    {   fun set_mappedstate s::TOPWINDOW_IS_NOW_UNMAPPED => topwindow_is_unmapped_overlay_loop();
                                            set_mappedstate s::TOPWINDOW_IS_NOW_MAPPED   => topwindow_is_mapped_overlay_loop (0, []);
                                            set_mappedstate _                            => xgripe::impossible "[drawimp (mapped-overlay): bad config command]";
                                        end;

                                        fun do_plea (d::DRAW m)            =>                                                     topwindow_is_mapped_overlay_loop (1, [m]);
                                            do_plea (d::FLUSH     oneshot) => { set (oneshot, ());                                topwindow_is_mapped_overlay_loop (0, []); };                  # Buffer is empty so FLUSH is a no-op.
                                            do_plea (d::THREAD_ID oneshot) => { set (oneshot, thread::get_current_thread's_id()); topwindow_is_mapped_overlay_loop (0, []); };
                                            do_plea (d::DESTROY id)        => { destroy id;                                       topwindow_is_mapped_overlay_loop (0, []);};
                                            do_plea _                      => xgripe::impossible "[drawimp (mapped-overlay): bad command]";
                                        end;

                                        #  xlogger::trace (xlogger::dmTM, fn => ["draw_imp: overlayMapped (empty)\n"]); 

                                        select [
                                            flush_done'      ==>  (fn _ = TRUE),
                                            new_plea'        ==>  do_plea,
                                            set_mappedstate' ==>  set_mappedstate
                                        ];
                                    };

                                topwindow_is_mapped_overlay_loop (commands_in_buffer, buf as (last_draw_command ! rest))
                                    =>
                                    {   fun set_mappedstate s::TOPWINDOW_IS_NOW_UNMAPPED =>  topwindow_is_unmapped_overlay_loop();
                                            set_mappedstate s::TOPWINDOW_IS_NOW_MAPPED   =>  topwindow_is_mapped_overlay_loop (commands_in_buffer, buf);
                                            set_mappedstate _                            =>  xgripe::impossible "[drawimp (mapped): bad mapped-state command]";
                                        end;

                                        fun do_plea (d::DRAW draw_command)
                                               =>
                                               topwindow_is_mapped_overlay_loop (batch_cmd (commands_in_buffer, draw_command, last_draw_command, rest));

                                            do_plea (d::FLUSH flush_done_oneshot)
                                                =>
                                                {   flush buf;
                                                    set (flush_done_oneshot, ());
                                                    topwindow_is_mapped_overlay_loop (0, []);
                                                };

                                            do_plea (d::THREAD_ID thread_id_oneshot)
                                                =>
                                                {   set (thread_id_oneshot, thread::get_current_thread's_id());
                                                    topwindow_is_mapped_overlay_loop (commands_in_buffer, buf);
                                                };

                                            do_plea (d::DESTROY id)
                                                =>
                                                {   flush buf;
                                                    destroy id;
                                                    topwindow_is_mapped_overlay_loop (0, []);
                                                };

                                            do_plea _
                                                =>
                                                xgripe::impossible "draw_imp: user batching not supported yet";
                                        end;

                                        #  xlogger::trace (xlogger::dmTM, fn => ["draw_imp: overlayMapped (", makestring (length buf), ")\n"]); 

                                        select [
                                            flush_done'      ==>  (fn _ = {  flush buf;  TRUE;  }),
                                            flush_delay'     ==>  (fn _ = {  flush buf;  topwindow_is_mapped_overlay_loop (0, []);  }),
                                            new_plea'        ==>  do_plea,
                                            set_mappedstate' ==>  set_mappedstate
                                        ];
                                    };
                            end;

                            fun do_overlay ()
                                =
                                is_mapped  ??  topwindow_is_mapped_overlay_loop (0, [])
                                           ::  topwindow_is_unmapped_overlay_loop ();


                            do_overlay ()  ??  topwindow_is_mapped_loop (0, [])
                                           ::  topwindow_is_unmapped_loop ();

                        };                      # fun overlay 

                    fun start_draw_imp ()
                        =
                        {
trace  .{ sprintf "XYZZY start_draw_imp: thread_id d=%d" (thread::get_current_thread's_id ()); };
                            # Wait for FIRST_EXPOSE,
                            # then enter main loop:
                            #
                            case (do_mailop set_mappedstate')
                                #
                                s::FIRST_EXPOSE =>   topwindow_is_mapped_loop (0, []);
                                 _              =>   start_draw_imp ();
                            esac;
                        };

                    xlogger::make_thread  "draw_imp"  start_draw_imp;

                    fn msg =   give  (plea_slot, msg);

                };              # fun make_draw_imp
        end;                    # stipulate
    };                          # package draw_imp 
end;                            # stipulate



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext