I64 box_x_min, box_x_max, box_y_min, box_y_max;

class Arrow
{
    Arrow   *next, *last;
    F64      x, y, dx, dy;

} head;

     <1>/* Graphics Not Rendered in HTML */

Bool bow_drawn;
F64  bow_x, bow_y, bow_theta;

U0 DrawIt(CTask *task, CDC *dc)
{
    F64      theta, x, y, dx, dy, str_w, str_h, draw_len;
    Arrow   *tmpa;
    CD3I32   ctrl[5];

    dc->color = RED;
    GrBorder(dc, box_x_min, box_y_min, box_x_max, box_y_max);

    x = ClampI64(mouse.pos.x - task->pix_left - task->scroll_x, box_x_min, box_x_max);
    y = ClampI64(mouse.pos.y - task->pix_top  - task->scroll_y, box_y_min, box_y_max);
    dx = bow_x - x;
    dy = bow_y - y;

    if (bow_drawn && (dx | dy))
        bow_theta = Arg(dx, dy);
    else
    {
        bow_x = x;
        bow_y = y;
    }

    draw_len = Sqrt(dx * dx + dy * dy);
    str_w = draw_len / 3;
    str_h = Sqrt(60 * 60 - str_w * str_w);

    dc->color = BLACK;
    GrLine(dc,  x - str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta),
                y - str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta), x, y);
    GrLine(dc,  x + str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta), 
                y + str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta), x, y);

    MemSet(ctrl, 0, sizeof(ctrl));
    ctrl[0].x = x - str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta);
    ctrl[0].y = y - str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta);
    ctrl[1].x = x - 0.75 * str_h / 2 * Cos(bow_theta + pi / 2) + draw_len / 2 * Cos(bow_theta) + str_w * Cos(bow_theta);
    ctrl[1].y = y - 0.75 * str_h / 2 * Sin(bow_theta + pi / 2) + draw_len / 2 * Sin(bow_theta) + str_w * Sin(bow_theta);
    ctrl[2].x = x + draw_len / 2 * Cos(bow_theta) + str_w * Cos(bow_theta);
    ctrl[2].y = y + draw_len / 2 * Sin(bow_theta) + str_w * Sin(bow_theta);
    ctrl[3].x = x + 0.75 * str_h / 2 * Cos(bow_theta + pi / 2) + draw_len / 2 * Cos(bow_theta) + str_w * Cos(bow_theta);
    ctrl[3].y = y + 0.75 * str_h / 2 * Sin(bow_theta + pi / 2) + draw_len / 2 * Sin(bow_theta) + str_w * Sin(bow_theta);
    ctrl[4].x = x + str_h / 2 * Cos(bow_theta + pi / 2) + str_w * Cos(bow_theta);
    ctrl[4].y = y + str_h / 2 * Sin(bow_theta + pi / 2) + str_w * Sin(bow_theta);

    dc->color = BROWN;
    dc->thick = 2;
    Gr2BSpline3(dc, ctrl, 5);
    dc->thick = 1;

    if (bow_drawn)
        Sprite3ZB(dc, x, y, 0, <1>, bow_theta);

    tmpa = head.next;
    while (tmpa != &head)
    {
        theta = Arg(tmpa->dx, tmpa->dy);
        Sprite3ZB(dc, tmpa->x, tmpa->y, 0, <1>, theta);
        tmpa = tmpa->next;
    }
}

#define ANIMATE_SLEEP_MS                10

U0 AnimateTask(I64)
{
    I64      x, y;
    Arrow   *tmpa, *tmpa1;
    F64      dt, t0 = tS;

    while (TRUE)
    {
        dt = tS - t0;
        t0 = tS;

        x = ClampI64(mouse.pos.x - Fs->parent_task->pix_left - Fs->parent_task->scroll_x,
                     box_x_min, box_x_max) + Fs->parent_task->pix_left + Fs->parent_task->scroll_x;
        y = ClampI64(mouse.pos.y - Fs->parent_task->pix_top - Fs->parent_task->scroll_y, 
                     box_y_min, box_y_max) + Fs->parent_task->pix_top + Fs->parent_task->scroll_y;
        if (mouse.pos.x != x || mouse.pos.y != y)
            MouseSet(x, y);

        tmpa = head.next;
        while (tmpa != &head)
        {
            tmpa1 = tmpa->next;
            tmpa->x += tmpa->dx * dt;
            tmpa->y += tmpa->dy * dt;
            if (!(-Fs->parent_task->scroll_x <= tmpa->x < Fs->parent_task->pix_width  - Fs->parent_task->scroll_x) ||
                !(-Fs->parent_task->scroll_y <= tmpa->y < Fs->parent_task->pix_height - Fs->parent_task->scroll_y))
            {
                QueueRemove(tmpa);
                Free(tmpa);
            }
            tmpa = tmpa1;
        }
        Refresh;
    }
}

U0 Init()
{
    I64 w = Fs->pix_width, h = Fs->pix_height;

    QueueInit(&head);
    bow_drawn = FALSE;
    box_x_min = 7 * w / 16;
    box_y_min = 6 * h / 8;
    box_x_max = 9 * w / 16;
    box_y_max = 7 * h / 8;
    bow_theta = -pi / 2;
    bow_x = (box_x_min + box_x_max) / 2;
    bow_y = (box_y_min + box_y_max) / 2;
    MouseSet(bow_x + Fs->pix_left + Fs->scroll_x, bow_y + Fs->pix_top + Fs->scroll_y);
}

U0 CleanUp()
{
    QueueDel(&head, TRUE);
}

U0 Zing()
{
    I64      arg1, arg2;
    Arrow   *tmpa;

    MenuPush(   "File {"
                "  Abort(,CH_SHIFT_ESC);"
                "  Exit(,CH_ESC);"
                "}"
                "Play {"
                "  Restart(,'\n');"
                "}"
                );
    SettingsPush; //See SettingsPush
    AutoComplete;
    WinBorder;
    WinMax;
    DocCursor;
    DocClear;

    Init;
    Fs->animate_task = Spawn(&AnimateTask, NULL, "Animate",, Fs);
    Fs->draw_it      = &DrawIt;
    Fs->win_inhibit  = WIG_TASK_DEFAULT - WIF_SELF_FOCUS - WIF_SELF_GRAB_SCROLL;
    try
    {
        while (TRUE)
            switch (MessageGet(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN | 1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_MS_L_UP))
            {
                case MESSAGE_KEY_DOWN:
                    switch (arg1)
                    {
                        case '\n':
                            CleanUp;
                            Init;
                            break;

                        case CH_ESC:
                        case CH_SHIFT_ESC:
                            goto zi_done;
                    }
                    break;

                case MESSAGE_MS_L_DOWN:
                    bow_x = arg1;
                    bow_y = arg2;
                    bow_drawn = TRUE;
                    break;

                case MESSAGE_MS_L_UP:
                    if (arg1-bow_x || arg2-bow_y)
                    {
                        tmpa = MAlloc(sizeof(Arrow));
                        tmpa->dx = 10.0 * (bow_x - arg1);
                        tmpa->dy = 10.0 * (bow_y - arg2);
                        tmpa->x = arg1;
                        tmpa->y = arg2;
                        QueueInsert(tmpa, head.last);
                        Noise(50, 110, 114);
                    }
                    bow_drawn = FALSE;
                    break;
            }
zi_done:
        MessageGet(,, 1 << MESSAGE_KEY_UP);
    }
    catch
        PutExcept;
    SettingsPop;
    CleanUp;
    MenuPop;
}

Zing;