U0 InputFilterTask()
{
        CJob *tmpc, *tmpc1;
        Bool  old_filter;
        I64   old_flags = RFlagsGet;

        Fs->win_inhibit = WIG_USER_TASK_DEFAULT;
        LBts(&Fs->task_flags, TASKf_INPUT_FILTER_TASK);
        old_filter = LBts(&Fs->last_input_filter_task->task_flags, TASKf_FILTER_INPUT);
        LBEqual(&Fs->task_flags, TASKf_FILTER_INPUT, old_filter);
        while (TRUE)
        {
                CLI
                JobsHandler(old_flags);
                tmpc1 = &Fs->server_ctrl.next_waiting;
                tmpc  = tmpc1->next;
                if (tmpc == tmpc1)
                        break;
                else
                {
                        if (tmpc->job_code == JOBT_TEXT_INPUT)
                        {
                                QueueRemove(tmpc);
                                RFlagsSet(old_flags);
                                try
                                        ExePrint("%s", tmpc->aux_str);
                                catch
                                        Fs->catch_except = TRUE;
                                JobDel(tmpc);
                        }
                        else
                                break;
                }
        }
        Fs->next_input_filter_task->last_input_filter_task = Fs->last_input_filter_task;
        Fs->last_input_filter_task->next_input_filter_task = Fs->next_input_filter_task;
        if (!old_filter)
                LBtr(&Fs->last_input_filter_task->task_flags, TASKf_FILTER_INPUT);
        RFlagsSet(old_flags);
}

I64 MessageScan(I64 *_arg1=NULL, I64 *_arg2=NULL, I64 mask=~1, CTask *task=NULL)
{/*Check for a message of type specified by a one in the mask.
Throw-out messages not in mask.
If no message fit mask, return NULL immediately.
Remove desired message, return message_code.
Note: This delivers messages from parent down to pop-up.
*/
        I64   res, old_flags;
        CJob *tmpc, *tmpc1;

        if (!task)
                task = Fs;
        old_flags = RFlagsGet;
        tmpc1 =&task->server_ctrl.next_waiting;
        while (TRUE)
        {
                CLI
                if (task == Fs)
                        JobsHandler(old_flags);
                tmpc = tmpc1->next;
                if (tmpc == tmpc1)
                        break;
                else
                {
                        if (tmpc->job_code == JOBT_MESSAGE)
                        {
                                QueueRemove(tmpc);
                                RFlagsSet(old_flags);
                                res = tmpc->message_code;
                                if (_arg1)
                                        *_arg1 = tmpc->aux1;
                                if (_arg2)
                                        *_arg2 = tmpc->aux2;
                                JobDel(tmpc);
                                if ((res != MESSAGE_KEY_DOWN || !(tmpc->aux2 & SCF_KEY_DESC) || !Bt(&task->win_inhibit, WIf_SELF_KEY_DESC)) &&
                                                Bt(&mask, res))
                                        goto sm_done;
                        }
                }
                RFlagsSet(old_flags);
        }
        res = MESSAGE_NULL;
        if (_arg1)
                *_arg1 = 0;
        if (_arg2)
                *_arg2 = 0;
        if (task->parent_task && task->parent_task->popup_task == task)
        {
                RFlagsSet(old_flags);
                return MessageScan(_arg1, _arg2, mask, task->parent_task);
        }
sm_done:
        RFlagsSet(old_flags);

        return res;
}

I64 FlushMessages(CTask *task=NULL)
{//Throw away all messages. Return count.
        I64 res = 0, arg1, arg2;

        while (MessageScan(&arg1, &arg2, ~1, task))
                res++;

        return res;
}

I64 MessageGet(I64 *_arg1=NULL, I64 *_arg2=NULL, I64 mask=~1, CTask *task=NULL)
{//Wait for a message of type specified by a one in the mask.
//Throw-out all messages not in mask.
        //Returns message_code. See ::/Demo/MessageLoop.CC.
        I64 res;

        if (!task)
                task = Fs;
        LBtr(&task->task_flags, TASKf_IDLE);
        while (!(res = MessageScan(_arg1, _arg2, mask, task)))
        {
                LBts(&task->task_flags, TASKf_IDLE);
                Yield;
        }
        LBtr(&task->task_flags, TASKf_IDLE);

        return res;
}

I64 CharScan()
{//Checks for MESSAGE_KEY_DOWN and returns 0 immediately if no key.
//Waits for MESSAGE_KEY_UP of non-zero ASCII key and returns ASCII if key.
        //MessageScan() throws away other message types.
        I64 arg1a, arg2a, arg1b, arg2b;

        if (!MessageScan(&arg1a, &arg2a, 1 << MESSAGE_KEY_DOWN) || !arg1a)
                return 0;
        else
                do MessageGet(&arg1b, &arg2b, 1 << MESSAGE_KEY_UP);
                while (!arg1b); //Be careful of SC_SHIFT and SC_CTRL, etc.

        return arg1a;
}

Bool KeyScan(I64 *_ch=NULL, I64 *_scan_code=NULL, Bool echo=FALSE)
{//Checks for MESSAGE_KEY_DOWN and returns FALSE immediately if no key.
//Sets ASCII and scan_code.
        //Removes key message and returns TRUE.
        //MessageScan() throws away other message types.
        I64 ch = 0, sc = 0;

        if (MessageScan(&ch, &sc, 1 << MESSAGE_KEY_DOWN))
        {
                if (_ch)
                        *_ch = ch;
                if (_scan_code)
                        *_scan_code = sc;
                if (echo)
                        PutKey(ch, sc);
                return TRUE;
        }
        else
        {
                if (_ch)
                        *_ch = 0;
                if (_scan_code)
                        *_scan_code = 0;
                return FALSE;
        }
}

I64 KeyGet(I64 *_scan_code=NULL, Bool echo=FALSE, Bool raw_cursor=FALSE)
{//Waits for MESSAGE_KEY_DOWN message and returns ASCII.
//Sets scan_code.
        //KeyScan() throws away other message types.
        I64  ch, sc;
        Bool cursor_on = FALSE;

        while (!KeyScan(&ch, &sc, FALSE))
        {
                if (IsRaw && raw_cursor)
                {
                        if (!cursor_on && ToI64(TSCGet * 5 / counts.time_stamp_freq) & 1)
                        {
                                '.';
                                cursor_on = TRUE;
                        }
                        else if (cursor_on && !(ToI64(TSCGet * 5 / counts.time_stamp_freq) & 1))
                        {
                                '' CH_BACKSPACE;
                                cursor_on = FALSE;
                        }
                }
                LBts(&Fs->task_flags, TASKf_IDLE);
                if (IsDebugMode)
                {
//We don't want interrupt-driven keyboard when in debugger
                        //because that could have side-effects or crash, so we poll
                        //keyboard when in debugger with interrupts off.
                        PUSHFD
                        CLI
                        KbdMouseHandler(TRUE, FALSE);
                        KbdMessagesQueue;
                        POPFD
                }
                else
                {
                        LBts(&Fs->task_flags, TASKf_AWAITING_MESSAGE);
                        Yield;
                }
                LBtr(&Fs->task_flags, TASKf_IDLE);
        }
        if (IsRaw && raw_cursor && cursor_on)
                '' CH_BACKSPACE;
        if (echo)
                PutKey(ch, sc);
        if (_scan_code)
                *_scan_code = sc;
        return ch;
}

I64 CharGet(I64 *_scan_code=NULL, Bool echo=TRUE, Bool raw_cursor=FALSE)
{//Waits for non-zero ASCII key.
//Sets scan_code.
        I64 ch1;

        do ch1 = KeyGet(_scan_code, FALSE, raw_cursor);
        while (!ch1);
        if (echo)
                "$PT$%c$FG$", ch1;

        return ch1;
}

U8 *StrGet(U8 *message=NULL, U8 *default=NULL, I64 flags=NONE)
{//Returns a MAlloc()ed prompted string. See Flags.
        U8 *st;

        if (message)
                "" message, default;
        st = (*fp_getstr2)(flags);
        if (!*st)
        {
                Free(st);
                if (default)
                        return StrNew(default);
                else
                        return StrNew("");
        }
        return st;
}

I64 StrNGet(U8 *buf, I64 size, Bool allow_ext=TRUE)
{//Prompt into fixed length string. Size must include terminator.
        U8 *st;
        I64 ch, i = 0;

        if (!size || !buf)
                return 0;
        if (allow_ext)
        {
                st = StrGet;
                if (StrLen(st) > size - 1)
                {
                        MemCopy(buf, st, size - 1);
                        buf[size - 1] = 0;
                }
                else
                        StrCopy(buf, st);
                i = StrLen(buf);
                Free(st);
        }
        else
        {
                while (TRUE)
                {
                        ch = CharGet(, FALSE, IsDebugMode);
                        if (ch == '\n')
                        {
                                '' ch;
                                break;
                        }
                        else if (ch == CH_BACKSPACE)
                        {
                                if (i > 0)
                                {
                                        i--;
                                        '' ch;
                                }
                        }
                        else
                        {
                                if (i < size - 1)
                                {
                                        buf[i++] = ch;
                                        '' ch;
                                }
                        }
                }
                buf[i] = 0;
        }
        return i;
}