PreviousUpNext

15.4.1622  src/lib/x-kit/xclient/src/window/draw.pkg

## draw.pkg
#
# Routines for drawing on windows and pixmaps.
#
# This is the library-internal version of this package;
# the client-level version is in:
#
#     src/lib/x-kit/xclient/xclient.pkg

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






###                 "Humanity has advanced, when it has advanced,
###                  not because it has been sober, responsible,
###                  and cautious, but because it has been playful,
###                  rebellious, and immature."
###
###                                          -- Tom Robbins


stipulate
    include package   threadkit;                        # threadkit                     is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
    package sn  =  xsession_junk;                       # xsession_junk                 is from   src/lib/x-kit/xclient/src/window/xsession-junk.pkg
    package g2d =  geometry2d;                          # geometry2d                    is from   src/lib/std/2d/geometry2d.pkg
    package fb  =  font_base;                           # font_base                     is from   src/lib/x-kit/xclient/src/window/font-base.pkg
    package di  =  xserver_ximp;                        # xserver_ximp                  is from   src/lib/x-kit/xclient/src/window/xserver-ximp.pkg
    package w2x =  windowsystem_to_xserver;             # windowsystem_to_xserver       is from   src/lib/x-kit/xclient/src/window/windowsystem-to-xserver.pkg
    package dt  =  draw_types;                          # draw_types                    is from   src/lib/x-kit/xclient/src/window/draw-types.pkg
    package pn  =  pen;                                 # pen                           is from   src/lib/x-kit/xclient/src/window/pen.pkg
herein

    package draw {
        #
        exception BAD_DRAW_PARAMETER;

        stipulate

            # Extract from a drawable its
            #     draw_fn,
            #     xid
            #     depth
            #
            fun info_of_drawable (dt::DRAWABLE { draw_ops, root => dt::r::WINDOW w } )
                    =>
                    {   w ->   { window_id, per_depth_imps => { depth, ... }: sn::Per_Depth_Imps, ... }: sn::Window;
                        #
                        { draw_ops,  id => window_id,  depth };
                    };

                info_of_drawable (dt::DRAWABLE { draw_ops, root => dt::r::PIXMAP pm } )
                    =>
                    {   pm ->  { pixmap_id, per_depth_imps => { depth, ... }: sn::Per_Depth_Imps, ... }: sn::Rw_Pixmap;
                        #
                        { draw_ops,  id => pixmap_id,  depth };
                    };
            end;


            # Extract the xid and depth of a source drawable 
            #
            fun info_of_src (dt::FROM_WINDOW    ({ window_id, per_depth_imps => { depth, ... }: sn::Per_Depth_Imps, ... }: sn::Window ))
                    =>
                    (window_id, depth);

                info_of_src (dt::FROM_RW_PIXMAP ({ pixmap_id, per_depth_imps => { depth, ... }: sn::Per_Depth_Imps, ... }: sn::Rw_Pixmap))
                    =>
                    (pixmap_id, depth);

                info_of_src (dt::FROM_RO_PIXMAP (sn::RO_PIXMAP ({ pixmap_id, per_depth_imps => { depth, ... }: sn::Per_Depth_Imps, ... }: sn::Rw_Pixmap)))
                    =>
                    (pixmap_id, depth);
            end;


            # Build a drawing function from an encoding function.
            #
            # The functions have the basic type scheme
            #
            #   Drawable -> Pen -> Args -> Void
            #
            fun draw_fn f drawable pen
                =
                {   (info_of_drawable  drawable)
                        ->
                        { draw_ops, id, ... };
                        

                    \\ x =  draw_ops [ { to => id, pen, op => (f x) } ];
                };

            fun draw_fn2 f drawable pen
                =
                {   (info_of_drawable  drawable)
                        ->
                        { draw_ops, id, ... };

                    \\ x =   \\ x' =   draw_ops [ { to => id, pen, op => (f x x') } ];
                };

            fun check_list chkfn l
                =
                {   apply
                        (\\ x = {   chkfn x;
                                    ();
                                }
                        )
                        l;

                    l;
                };

            fun check_item chkfn
                =
                (\\ v = if (chkfn v)  v;
                        else          raise exception BAD_DRAW_PARAMETER;
                        fi
                );

            check_point =  check_item  g2d::valid_point;
            check_box   =  check_item  g2d::valid_box;
            check_line  =  check_item  g2d::valid_line;
            check_arc   =  check_item  g2d::valid_arc;

            check_pts   =  check_list  check_point;
            check_boxes =  check_list  check_box;
            check_lines =  check_list  check_line;
            check_arcs  =  check_list  check_arc;

        herein

            # Points:
            #
            draw_points     =  draw_fn  (\\ pts =  w2x::x::POLY_POINT (FALSE, check_pts pts));
            draw_point_path =  draw_fn  (\\ pts =  w2x::x::POLY_POINT (TRUE,  check_pts pts));
            draw_point      =  draw_fn  (\\ pt  =  w2x::x::POLY_POINT (FALSE, [check_point pt]));

            # Lines and segments:
            #
            draw_lines   =  draw_fn  (\\ pts   =  w2x::x::POLY_LINE (FALSE, check_pts pts));
            draw_path    =  draw_fn  (\\ pts   =  w2x::x::POLY_LINE (TRUE,  check_pts pts));
            draw_segs    =  draw_fn  (\\ lines =  w2x::x::POLY_SEG  (check_lines lines));
            draw_seg     =  draw_fn  (\\ seg   =  w2x::x::POLY_SEG  [check_line seg]);

            # Filled polygons:
            #
            fill_polygon =  draw_fn  (\\ { verts, shape } =  w2x::x::FILL_POLY (shape, FALSE, check_pts verts));
            fill_path    =  draw_fn  (\\ { path,  shape } =  w2x::x::FILL_POLY (shape, TRUE,  check_pts path ));

            # Rectangles:
            #
            draw_boxes  =  draw_fn  (\\ boxes =  w2x::x::POLY_BOX (check_boxes boxes));
            draw_box    =  draw_fn  (\\ box   =  w2x::x::POLY_BOX [check_box box]);

            fill_boxes  =  draw_fn  (\\ boxes =  w2x::x::POLY_FILL_BOX (check_boxes boxes));
            fill_box    =  draw_fn  (\\ box   =  w2x::x::POLY_FILL_BOX [check_box box]);

            # Arcs:
            #
            draw_arcs    =  draw_fn  (\\ arcs =  w2x::x::POLY_ARC      (check_arcs arcs));
            draw_arc     =  draw_fn  (\\ arc  =  w2x::x::POLY_ARC      [check_arc arc]);
            fill_arcs    =  draw_fn  (\\ arcs =  w2x::x::POLY_FILL_ARC (check_arcs arcs));
            fill_arc     =  draw_fn  (\\ arc  =  w2x::x::POLY_FILL_ARC [check_arc arc]);

            # Circles:
            #
            fun circle_to_arc { center => { col, row }, rad }
                =
                {   diam = rad + rad;

                    {
                        col => col-rad, row => row-rad,
                        wide => diam, high => diam,
                        angle1 => 0, angle2 => 64*360
                    };
                };

            draw_circle =  draw_fn  (\\ arg =  w2x::x::POLY_ARC      [circle_to_arc arg]);
            fill_circle =  draw_fn  (\\ arg =  w2x::x::POLY_FILL_ARC [circle_to_arc arg]);

            # Text drawing:
            #
            draw_transparent_string
                =
                draw_fn2 (
                    \\ ({ id, ... }: fb::Font)
                        =
                        \\ (pt, s)
                            =
                            w2x::x::POLY_TEXT8 (id, check_point pt, [w2x::t::TEXT (0, s)]
                    )
                );

            draw_opaque_string
                =
                draw_fn2
                    (\\ ({ id, ... }: fb::Font)
                        =
                        \\ (pt, s)
                            =
                            w2x::x::IMAGE_TEXT8 (id, check_point pt, s)
                    );

            # Polytext drawing:
            #
            #    "There are two styles of text drawing: opaque and transparent.
            #
            #    "Opaque text [...] is drawn by first filling in the bounding box
            #     with the background color and then drawing the text with the
            #     foreground color.  The function and fill-style of the pen are
            #     ignored, replaced in effect by OP_COPY and pn::FILL_STYLE_SOLID
            #
            #    "In transparent text [...] the pixels corresponding to bits set in
            #     a character's glyph are drawn using the foreground color in the
            #     context of the other relevant pen values, while the other pixels
            #     are unmodified.
            #
            #    "The [draw_transparent_text] function provides a user-level batching
            #     mechanism for drawing multiple strings of the same line with possible
            #     intervening font changes or horizontal shifts."
            #
            #         -- p22-3 http://mythryl.org/pub/exene/1993-lib.ps
            #            (Reppy + Gansner's 1993 eXene library manual.)
            package t {
                #
                Text      = TEXT         (fb::Font, List(Text_Item))
                also
                Text_Item = FONT         (fb::Font, List(Text_Item))
                          | STRING        String
                          | BLANK_PIXELS  Int                   # Skip this many pixels before next STRING.
                          ;
            };

            draw_transparent_text
                =
                draw_fn f
                where 
                    fun f (pt, t::TEXT ({ id=>font, ... }: fb::Font, items))
                        =
                        w2x::x::POLY_TEXT8
                          ( font,
                            check_point pt,
                            reverse (#2 (flat (font, 0, items, [])))
                          )
                        where
                            fun flat (_, d, [], l)
                                    =>
                                    (d, l);

                                flat (font, d, (t::STRING s) ! r, l)
                                    =>
                                    flat (font, 0, r, w2x::t::TEXT (d, s) ! l);

                                flat (font, d, (t::BLANK_PIXELS d') ! r, l)
                                    =>
                                    flat (font, d+d', r, l);

                                flat (_, d, [t::FONT ({ id=>font, ... }: fb::Font, items)], l)
                                    =>
                                    flat (font, d, items, (w2x::t::FONT font) ! l);

                                flat (font, d, (t::FONT ({ id=>font', ... }: fb::Font, items)) ! r, l)
                                    =>
                                    {   (flat (font', d, items, (w2x::t::FONT font') ! l))
                                            ->
                                            (d', l');

                                        flat (font, d', r, (w2x::t::FONT font) ! l');
                                    };
                            end;
                        end;
                end;

            # TODO: imageText (what does it mean?? *            XXX BUGGO FIXME


            #  BLT operations                           # "BLT" == "Block Transfer" -- dates back to Xerox Alto bitmapped display "bitblt" days.

            exception DEPTH_MISMATCH;
            exception BAD_PLANE;

            stipulate

                # * NOTE: we should probably check that 'from' and 'to' are on the same display *       XXX BUGGO FIXME

                fun copy_area_fn  msg_fn  (to, pen, to_pos, from, from_box)
                    =
                    {   (info_of_drawable  to)
                            ->
                            { id=>to_id,  draw_ops,  depth=>to_depth };
                            

                        (info_of_src  from)
                            ->
                            (from_id, from_depth);

                        (msg_fn (check_point to_pos, from_id, from_box))
                            ->
#                           (msg, result);
                            msg;

                        if (from_depth != to_depth)    raise exception DEPTH_MISMATCH;  fi;

                        draw_ops [ { to => to_id, pen, op => msg } ];

#                       result;
                    };

                fun copy_plane_fn  msg_fn  (to, pen, to_pos, from, from_box, plane)
                    =
                    {   (info_of_drawable  to) ->   { id=>to_id,  draw_ops, ... };

                        (info_of_src  from)    ->   (from_id, from_depth);

                        (msg_fn (check_point to_pos, from_id, from_box, plane))
                            ->
#                           (msg, result);
                            msg;

                        if (plane < 0  or from_depth <= plane)          raise exception BAD_PLANE;      fi;

                        draw_ops [ { to => to_id, pen, op => msg } ];

#                       result;
                    };

                copy_area = copy_area_fn
                                (\\ (to_pos, from_id, from_box)
                                    =
                                    {
#                                       oneshot =  make_oneshot_maildrop ();
                                        #
#                                       (w2x::x::COPY_AREA (to_pos, from_id, from_box, oneshot), oneshot);
                                        w2x::x::COPY_AREA (to_pos, from_id, from_box);
                                    }
                                );

                copy_plane = copy_plane_fn
                                (\\ (to_pos, from_id, from_box, plane)
                                    =
                                    {
#                                       oneshot =  make_oneshot_maildrop ();
                                        #
#                                       (w2x::x::COPY_PLANE (to_pos, from_id, from_box, plane, oneshot), oneshot);
                                        w2x::x::COPY_PLANE (to_pos, from_id, from_box, plane);
                                    }
                                );

                copy_pmarea  = copy_area_fn  (\\ arg = (w2x::x::COPY_PMAREA  arg));
                copy_pmplane = copy_plane_fn (\\ arg = (w2x::x::COPY_PMPLANE arg));

                                                                        # "They made us many promises,
                                                                        #  more than I can remember,
#               fun promise_event (to_drawimp, sync_1shot)              #  but they never kept but one;
#                   =                                                   #  they promised to take our land,
#                   {   sync_mailop                                     #  and they took it." -- Red Cloud
#                           =
#                           get_from_oneshot'  sync_1shot;

#                       dynamic_mailop {.
#                           #
#                           case (nonblocking_get_from_oneshot  sync_1shot)
#                               #
#                               THE boxes_fn
#                                   =>
#                                   always' ()  ==>  boxes_fn;

#                               NULL => {
# #                                         dt::flush_drawimp  to_drawimp;
#                                           #
#                                           sync_mailop   ==>   (\\ boxes_fn = boxes_fn ());
#                                       };
#                           esac;
#                       };
#                   };
            herein

                fun pixel_blt to pen { from as (dt::FROM_WINDOW _), from_box, to_pos }
                        =>
                        {
#                           to ->  dt::DRAWABLE { draw_ops, ... };

                            copy_area (to, pen, to_pos, from, from_box);
#                           sync_1shot = copy_area (to, pen, to_pos, from, from_box);

#                           dt::flush_drawimp  to_drawimp;

#                           (get_from_oneshot  sync_1shot) ();
                        };

                    pixel_blt  to  pen  { from, from_box, to_pos }
                        =>
                        {   copy_pmarea (to, pen, to_pos, from, from_box);
#                           [];
                        };
                end;

                fun pixel_blt_mailop to pen { from as (dt::FROM_WINDOW _), from_box, to_pos }
                        =>
                        {
#                           to ->  dt::DRAWABLE { draw_ops, ... };

                            copy_area (to, pen, to_pos, from, from_box);

#                           sync_v = copy_area (to, pen, to_pos, from, from_box);

#                           promise_event (to_drawimp, sync_v);
                        };

                    pixel_blt_mailop to pen { from, from_box, to_pos }
                        =>
                        {   copy_pmarea (to, pen, to_pos, from, from_box);

#                           always' [];
                        };
                end;

                fun plane_blt to pen { from as (dt::FROM_WINDOW _), from_box, to_pos, plane }
                        =>
                        {
#                           to ->  dt::DRAWABLE { draw_ops, ... };
                            #
                            copy_plane (to, pen, to_pos, from, from_box, plane);

#                           sync_1shot = copy_plane (to, pen, to_pos, from, from_box, plane);

#                           dt::flush_drawimp  to_drawimp;

#                           (get_from_oneshot  sync_1shot) ();
                        };

                    plane_blt to pen { from, from_box, to_pos, plane }
                        =>
                        {   copy_pmplane (to, pen, to_pos, from, from_box, plane);
#                           [];
                        };
                end;

                fun plane_blt_mailop to pen { from as (dt::FROM_WINDOW _), from_box, to_pos, plane }
                        =>
                        {
#                           to ->  dt::DRAWABLE { draw_ops, ... };

                            copy_plane (to, pen, to_pos, from, from_box, plane);

#                           sync_v = copy_plane (to, pen, to_pos, from, from_box, plane);

#                           promise_event (to_drawimp, sync_v);
                        };          

                    plane_blt_mailop to pen { from, from_box, to_pos, plane }
                        =>
                        {   copy_pmplane (to, pen, to_pos, from, from_box, plane);

#                           always' [];
                        };
                end;


                fun bitblt to pen { from, from_box, to_pos }
                    =
                    plane_blt to pen { from, from_box, to_pos, plane=>0 };


                fun bitblt_mailop to pen { from, from_box, to_pos }
                    =
                    plane_blt_mailop to pen { from, from_box, to_pos, plane=>0 };


                fun texture_blt to pen { from, to_pos }
                    =
                    {   (dt::size_of_ro_pixmap  from)
                            ->
                            { wide, high };

                        box = { col=>0, row=>0, wide, high };

                        plane_blt  to pen { from=>dt::FROM_RO_PIXMAP from, from_box=>box, to_pos, plane=>0 };

                        ();
                    };

                fun tile_blt to pen { from, to_pos }
                    =
                    {   (dt::size_of_ro_pixmap  from)
                            ->
                             { wide, high };

                        box = { col=>0, row=>0, wide, high };

                        pixel_blt  to pen { from=>dt::FROM_RO_PIXMAP from, from_box=>box, to_pos };

                        ();
                    };

                fun copy_blt drawable pen { to_pos, from_box }
                    =
                    {   from = case drawable
                                  #                     
                                  dt::DRAWABLE { root => dt::r::WINDOW w,  ... } =>  dt::FROM_WINDOW     w;
                                  dt::DRAWABLE { root => dt::r::PIXMAP pm, ... } =>  dt::FROM_RW_PIXMAP pm;
                              esac;


                        pixel_blt
                            drawable
                            pen
                            { from, to_pos, from_box };
                    };

                fun copy_blt_mailop  drawable  pen  { to_pos, from_box }
                    =
                    {   from = case drawable
                                  #                     
                                  dt::DRAWABLE { root => dt::r::WINDOW w,  ... } =>  dt::FROM_WINDOW     w;
                                  dt::DRAWABLE { root => dt::r::PIXMAP pm, ... } =>  dt::FROM_RW_PIXMAP pm;
                              esac;

                        pixel_blt_mailop
                            drawable
                            pen
                            { from, to_pos, from_box };
                    };

            end;                                                        # stipulate

            # Clear part of a destination drawable.
            # For windows, this fills in the background color;
            # for pixmaps, this fills in all 0's (which is actually
            # the default foreground pixel value).
            #
            stipulate

                clear_pen =   pn::make_pen  [  pn::p::FOREGROUND  rgb8::rgb8_color0  ];

            herein

                fun clear_box drawable
                    =
                    {   (info_of_drawable  drawable)
                            ->
                            { draw_ops,  id,  ... };

                        \\ box =    draw_ops [ { to  => id,
                                                 pen => clear_pen,
                                                 op  => (w2x::x::CLEAR_AREA (check_box box))
                                               }
                                             ];
                    };
            end;

            # Clear the whole area of a drawable:
            #
            fun clear_drawable  to
                =
                clear_box  to  ({ col=>0, row=>0, wide=>0, high=>0 } );


#           fun flush drawable
#               =
#               {   (info_of_drawable  drawable)
#                       ->
#                       { draw_ops, ... };
#
#                   dt::flush_drawimp  to_drawimp;
#               };

#           fun drawimp_thread_id_of  drawable
#               =
#               {   (info_of_drawable  drawable)
#                       ->
#                       { to_drawimp, ... };
#
#                   dt::drawimp_thread_id_of  to_drawimp;
#               };


        end;            # stipulate
    };                  # package draw 
end;                    # stipulate


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext