U0 RawPutChar(I64 ch)
{/*For RAW output during boot and in debugger.

See GrUpdateTextFG for
the normal screen text output routine.

See also GrUpdateScreen().
*/
    I64  i, row, col, x, y;
    U32 *framebuffer;
    U64  ch_bitmap;

    if (!(text.raw_flags & RAWF_SHOW_DOLLAR))
    {
        if (ch == '$')
        {
            if (text.raw_flags & RAWF_IN_DOLLAR)
            {
                text.raw_flags &= ~RAWF_IN_DOLLAR;
                if (!(text.raw_flags & RAWF_LAST_DOLLAR))
                {
                    text.raw_flags &= ~RAWF_LAST_DOLLAR;
                    return;
                }
            }
            else
            {
                text.raw_flags |= RAWF_IN_DOLLAR | RAWF_LAST_DOLLAR;
                return;
            }
        }
        text.raw_flags &= ~RAWF_LAST_DOLLAR;
        if (text.raw_flags & RAWF_IN_DOLLAR)
            return;
    }
    if (ch == '\t')
    {
        RawPutChar(CH_SPACE);
        while (text.raw_col & 3)
            RawPutChar(CH_SPACE);
    }
    else if (ch == CH_BACKSPACE)
    {
        text.raw_col--;
        RawPutChar(CH_SPACE);
        text.raw_col--;
    }
    else if (ch == '\n')
    {
        RawPutChar(CH_SPACE);
        while (text.raw_col % text.cols)
            RawPutChar(CH_SPACE);
    }
    else if (Bt(char_bmp_displayable, ch))
    {
        row = text.raw_col / text.cols % text.rows;
        col = text.raw_col % text.cols;

        if (text.raw_flags & RAWF_SCROLL && text.raw_col && !row && !col)
        {//Scroll screen down

            MemCopy(text.fb_alias,
                    text.fb_alias       + sys_vbe_mode.width * FONT_HEIGHT,
                    (text.screen_size   - sys_vbe_mode.width * FONT_HEIGHT) * sizeof(U32));

            MemSetU32(text.fb_alias + text.screen_size - sys_vbe_mode.width * FONT_HEIGHT, BLACK32, sys_vbe_mode.width * FONT_HEIGHT);
            text.raw_col -= text.cols ;
            row = text.rows - 1;
        }
        x = col * FONT_WIDTH;
        y = row * FONT_HEIGHT;
        ch_bitmap = text.font[ch & 0xFF];
        framebuffer = text.fb_alias + sys_vbe_mode.width * y + x;

        PUSHFD
        CLI
        for (i = 0; i < FONT_WIDTH * FONT_HEIGHT; i++)
        {
            if (ch_bitmap & 1)
                *framebuffer++ = WHITE32;
            else
                *framebuffer++ = BLACK32;
            if (i & (FONT_WIDTH - 1) == FONT_WIDTH - 1)
                framebuffer += sys_vbe_mode.width - FONT_WIDTH;
            ch_bitmap >>= 1;
        }
        POPFD
        text.raw_col++;
    }
}

U0 LFBFlush()
{//Flush winmgr screen cache, so updates whole screen.
    LBts(&sys_semas[SEMA_FLUSH_VBE_IMAGE], 0);
}

U0 WinDerivedValsUpdate(CTask *task)
{//Those things calculated from other variables.
    if (!task)
        task = Fs;
    //Assert: This is called with TASKf_TASK_LOCK set
    PUSHFD
    CLI
    task->win_width     = task->win_right  - task->win_left + 1;
    task->win_height    = task->win_bottom - task->win_top + 1;

    task->pix_left      = FONT_WIDTH * task->win_left;
    task->pix_right     = FONT_WIDTH * (task->win_right + 1) - 1;

    task->pix_width     = task->pix_right  - task->pix_left + 1;

    task->pix_top       = FONT_HEIGHT * task->win_top;
    task->pix_bottom    = FONT_HEIGHT * (task->win_bottom + 1) - 1;

    task->pix_height    = task->pix_bottom - task->pix_top + 1;
    POPFD
}

Bool WinInside(I64 x, I64 y, CTask *task=NULL, I64 border=0)
{//Is pixel (x,y) inside task's win? Border to FONT_WIDTH.
    if (!task)
        task = Fs;
    if (TaskValidate(task) && Bt(&task->display_flags, DISPLAYf_SHOW))
    {
        if (Bt(&task->display_flags, DISPLAYf_NO_BORDER))
            border = 0;
        if (task->pix_left - border <= x <= task->pix_right  + border &&
            task->pix_top  - border <= y <= task->pix_bottom + border)
            return TRUE;
    }

    return FALSE;
}