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

See $LK,"GrUpdateTextFG",A="MN:GrUpdateTextFG"$ for
the normal screen text output routine.

See also $LK,"GrUpdateScreen",A="MN: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 & 7)
			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;
}
