/* * DoTheScreenSaver * * Check if scrensaver should be started and sends message to SAS window */ VOID FASTCALL DoTheScreenSaver(VOID) { LARGE_INTEGER TickCount; DWORD Test, TO; if (gspv.iScrSaverTimeout > 0) // Zero means Off. { KeQueryTickCount(&TickCount); Test = MsqCalculateMessageTime(&TickCount); Test = Test - LastInputTick; TO = 1000 * gspv.iScrSaverTimeout; if (Test > TO) { TRACE("Screensaver Message Start! Tick %lu Timeout %d \n", Test, gspv.iScrSaverTimeout); if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry... { if (!(ppiScrnSaver->W32PF_flags & W32PF_IDLESCREENSAVER)) { ppiScrnSaver->W32PF_flags |= W32PF_IDLESCREENSAVER; ERR("Screensaver is Idle\n"); } } else { PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue(); if (ForegroundQueue && ForegroundQueue->spwndActive) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure else UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0); } } } }
LRESULT FASTCALL DefWndHandleSysCommand(PWND pWnd, WPARAM wParam, LPARAM lParam) { LRESULT lResult = 0; BOOL Hook = FALSE; if (ISITHOOKED(WH_CBT) || (pWnd->head.rpdesk->pDeskInfo->fsHooks & HOOKID_TO_FLAG(WH_CBT))) { Hook = TRUE; lResult = co_HOOK_CallHooks(WH_CBT, HCBT_SYSCOMMAND, wParam, lParam); if (lResult) return lResult; } switch (wParam & 0xfff0) { case SC_SCREENSAVE: ERR("Screensaver Called!\n"); UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0); // always lParam 0 == not Secure break; default: // We do not support anything else here so we should return normal even when sending a hook. return 0; } return(Hook ? 1 : 0); // Don't call us again from user space. }
BOOL APIENTRY NtUserLockWorkStation(VOID) { BOOL ret; PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); UserEnterExclusive(); if (pti->rpdesk == IntGetActiveDesktop()) { ret = UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_LOCK_WORKSTATION, 0); } else { ret = FALSE; } UserLeave(); return ret; }
/* * @implemented */ DWORD_PTR APIENTRY NtUserCallOneParam( DWORD_PTR Param, DWORD Routine) { DECLARE_RETURN(DWORD_PTR); TRACE("Enter NtUserCallOneParam\n"); UserEnterExclusive(); switch(Routine) { case ONEPARAM_ROUTINE_POSTQUITMESSAGE: { PTHREADINFO pti; pti = PsGetCurrentThreadWin32Thread(); MsqPostQuitMessage(pti->MessageQueue, Param); RETURN(TRUE); } case ONEPARAM_ROUTINE_BEGINDEFERWNDPOS: { PSMWP psmwp; HDWP hDwp = NULL; INT count = (INT)Param; if (count < 0) { EngSetLastError(ERROR_INVALID_PARAMETER); RETURN(0); } /* Windows allows zero count, in which case it allocates context for 8 moves */ if (count == 0) count = 8; psmwp = (PSMWP) UserCreateObject( gHandleTable, NULL, (PHANDLE)&hDwp, otSMWP, sizeof(SMWP)); if (!psmwp) RETURN(0); psmwp->acvr = ExAllocatePoolWithTag(PagedPool, count * sizeof(CVR), USERTAG_SWP); if (!psmwp->acvr) { UserDeleteObject(hDwp, otSMWP); RETURN(0); } RtlZeroMemory(psmwp->acvr, count * sizeof(CVR)); psmwp->bHandle = TRUE; psmwp->ccvr = 0; // actualCount psmwp->ccvrAlloc = count; // suggestedCount RETURN((DWORD_PTR)hDwp); } case ONEPARAM_ROUTINE_SHOWCURSOR: RETURN( (DWORD_PTR)UserShowCursor((BOOL)Param) ); case ONEPARAM_ROUTINE_GETDESKTOPMAPPING: { PTHREADINFO ti; ti = GetW32ThreadInfo(); if (ti != NULL) { /* Try convert the pointer to a user mode pointer if the desktop is mapped into the process */ RETURN((DWORD_PTR)DesktopHeapAddressToUser((PVOID)Param)); } else { RETURN(0); } } case ONEPARAM_ROUTINE_WINDOWFROMDC: RETURN( (DWORD_PTR)IntWindowFromDC((HDC)Param)); case ONEPARAM_ROUTINE_SWAPMOUSEBUTTON: { DWORD_PTR Result; Result = gspv.bMouseBtnSwap; gspv.bMouseBtnSwap = Param ? TRUE : FALSE; gpsi->aiSysMet[SM_SWAPBUTTON] = gspv.bMouseBtnSwap; RETURN(Result); } case ONEPARAM_ROUTINE_SWITCHCARETSHOWING: RETURN( (DWORD_PTR)IntSwitchCaretShowing((PVOID)Param)); case ONEPARAM_ROUTINE_SETCARETBLINKTIME: RETURN( (DWORD_PTR)IntSetCaretBlinkTime((UINT)Param)); case ONEPARAM_ROUTINE_SETMESSAGEEXTRAINFO: RETURN( (DWORD_PTR)MsqSetMessageExtraInfo((LPARAM)Param)); case ONEPARAM_ROUTINE_CREATEEMPTYCUROBJECT: { PCURICON_OBJECT CurIcon; DWORD_PTR Result ; if (!(CurIcon = IntCreateCurIconHandle())) { EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); RETURN(0); } Result = (DWORD_PTR)CurIcon->Self; UserDereferenceObject(CurIcon); RETURN(Result); } case ONEPARAM_ROUTINE_GETCURSORPOSITION: { BOOL ret = TRUE; _SEH2_TRY { ProbeForWrite((POINT*)Param,sizeof(POINT),1); RtlCopyMemory((POINT*)Param,&gpsi->ptCursor,sizeof(POINT)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); ret = FALSE; } _SEH2_END; RETURN (ret); } case ONEPARAM_ROUTINE_ENABLEPROCWNDGHSTING: { BOOL Enable; PPROCESSINFO Process = PsGetCurrentProcessWin32Process(); if(Process != NULL) { Enable = (BOOL)(Param != 0); if(Enable) { Process->W32PF_flags &= ~W32PF_NOWINDOWGHOSTING; } else { Process->W32PF_flags |= W32PF_NOWINDOWGHOSTING; } RETURN( TRUE); } RETURN( FALSE); } case ONEPARAM_ROUTINE_GETINPUTEVENT: RETURN( (DWORD_PTR)IntMsqSetWakeMask(Param)); case ONEPARAM_ROUTINE_GETKEYBOARDTYPE: RETURN( UserGetKeyboardType(Param)); case ONEPARAM_ROUTINE_GETKEYBOARDLAYOUT: RETURN( (DWORD_PTR)UserGetKeyboardLayout(Param)); case ONEPARAM_ROUTINE_RELEASEDC: RETURN (UserReleaseDC(NULL, (HDC) Param, FALSE)); case ONEPARAM_ROUTINE_REALIZEPALETTE: RETURN (UserRealizePalette((HDC) Param)); case ONEPARAM_ROUTINE_GETQUEUESTATUS: { RETURN (IntGetQueueStatus((DWORD)Param)); } case ONEPARAM_ROUTINE_ENUMCLIPBOARDFORMATS: /* FIXME: Should use UserEnterShared */ RETURN(IntEnumClipboardFormats(Param)); case ONEPARAM_ROUTINE_CSRSS_GUICHECK: IntUserManualGuiCheck(Param); RETURN(TRUE); case ONEPARAM_ROUTINE_GETCURSORPOS: { BOOL Ret = TRUE; PPOINTL pptl; PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); if (pti->hdesk != InputDesktopHandle) RETURN(FALSE); _SEH2_TRY { pptl = (PPOINTL)Param; *pptl = gpsi->ptCursor; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Ret = FALSE; } _SEH2_END; RETURN(Ret); } case ONEPARAM_ROUTINE_SETPROCDEFLAYOUT: { PPROCESSINFO ppi; if (Param & LAYOUT_ORIENTATIONMASK) { ppi = PsGetCurrentProcessWin32Process(); ppi->dwLayout = Param; RETURN(TRUE); } EngSetLastError(ERROR_INVALID_PARAMETER); RETURN(FALSE); } case ONEPARAM_ROUTINE_GETPROCDEFLAYOUT: { BOOL Ret = TRUE; PPROCESSINFO ppi; PDWORD pdwLayout; if ( PsGetCurrentProcess() == CsrProcess) { EngSetLastError(ERROR_INVALID_ACCESS); RETURN(FALSE); } ppi = PsGetCurrentProcessWin32Process(); _SEH2_TRY { pdwLayout = (PDWORD)Param; *pdwLayout = ppi->dwLayout; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); Ret = FALSE; } _SEH2_END; RETURN(Ret); } case ONEPARAM_ROUTINE_REPLYMESSAGE: RETURN (co_MsqReplyMessage((LRESULT) Param)); case ONEPARAM_ROUTINE_MESSAGEBEEP: RETURN ( UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, Param) ); /* TODO: Implement sound sentry */ } ERR("Calling invalid routine number 0x%x in NtUserCallOneParam(), Param=0x%x\n", Routine, Param); EngSetLastError(ERROR_INVALID_PARAMETER); RETURN( 0); CLEANUP: TRACE("Leave NtUserCallOneParam, ret=%i\n",_ret_); UserLeave(); END_CLEANUP; }
// // Process win32k system timers. // VOID CALLBACK SystemTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { PDESKTOP pDesk; PWND pWnd = NULL; if (hwnd) { pWnd = UserGetWindowObject(hwnd); if (!pWnd) { ERR( "System Timer Proc has invalid window handle! 0x%x Id: %d\n", hwnd, idEvent); return; } } else { TRACE( "Windowless Timer Running!\n" ); return; } switch (idEvent) { /* Used in NtUserTrackMouseEvent. */ case ID_EVENT_SYSTIMER_MOUSEHOVER: { POINT Point; UINT Msg; WPARAM wParam; pDesk = pWnd->head.rpdesk; if ( pDesk->dwDTFlags & DF_TME_HOVER && pWnd == pDesk->spwndTrack ) { Point = gpsi->ptCursor; if ( RECTL_bPointInRect(&pDesk->rcMouseHover, Point.x, Point.y) ) { if (pDesk->htEx == HTCLIENT) // In a client area. { wParam = get_key_state(); Msg = WM_MOUSEHOVER; if (pWnd->ExStyle & WS_EX_LAYOUTRTL) { Point.x = pWnd->rcClient.right - Point.x - 1; } else Point.x -= pWnd->rcClient.left; Point.y -= pWnd->rcClient.top; } else { wParam = pDesk->htEx; // Need to support all HTXYZ hits. Msg = WM_NCMOUSEHOVER; } UserPostMessage(hwnd, Msg, wParam, MAKELPARAM(Point.x, Point.y)); pDesk->dwDTFlags &= ~DF_TME_HOVER; break; // Kill this timer. } } } return; // Not this window so just return. default: ERR( "System Timer Proc invalid id %d!\n", idEvent ); break; } IntKillTimer(pWnd, idEvent, TRUE); }
/* * co_UserProcessHotKeys * * Sends WM_HOTKEY message if given keys are hotkey */ BOOL NTAPI co_UserProcessHotKeys(WORD wVk, BOOL bIsDown) { UINT fModifiers; PHOT_KEY pHotKey; PWND pWnd; BOOL DoNotPostMsg = FALSE; BOOL IsModifier = FALSE; if (wVk == VK_SHIFT || wVk == VK_CONTROL || wVk == VK_MENU || wVk == VK_LWIN || wVk == VK_RWIN) { /* Remember that this was a modifier */ IsModifier = TRUE; } fModifiers = IntGetModifiers(gafAsyncKeyState); if (bIsDown) { if (IsModifier) { /* Modifier key down -- no hotkey trigger, but remember this */ gfsModOnlyCandidate = fModifiers; return FALSE; } else { /* Regular key down -- check for hotkey, and reset mod candidates */ pHotKey = IsHotKey(fModifiers, wVk); gfsModOnlyCandidate = 0; } } else { if (IsModifier) { /* Modifier key up -- modifier-only keys are triggered here */ pHotKey = IsHotKey(gfsModOnlyCandidate, 0); gfsModOnlyCandidate = 0; } else { /* Regular key up -- no hotkey, but reset mod-only candidates */ gfsModOnlyCandidate = 0; return FALSE; } } if (pHotKey) { TRACE("Hot key pressed (pWnd %p, id %d)\n", pHotKey->pWnd, pHotKey->id); /* FIXME: See comment about "UserDebuggerHotKey" on top of this file. */ if (pHotKey->id == IDHK_SHIFTF12 || pHotKey->id == IDHK_F12) { if (bIsDown) { ERR("Hot key pressed for Debug Activation! ShiftF12 = %d or F12 = %d\n",pHotKey->id == IDHK_SHIFTF12 , pHotKey->id == IDHK_F12); //DoNotPostMsg = co_ActivateDebugger(); // FIXME } return DoNotPostMsg; } /* WIN and F12 keys are not hardcoded here. See comments on top of this file. */ if (pHotKey->id == IDHK_WINKEY) { ASSERT(!bIsDown); pWnd = ValidateHwndNoErr(InputWindowStation->ShellWindow); if (pWnd) { TRACE("System Hot key Id %d Key %u\n", pHotKey->id, wVk ); UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0); co_IntShellHookNotify(HSHELL_TASKMAN, 0, 0); return FALSE; } } if (pHotKey->id == IDHK_SNAP_LEFT || pHotKey->id == IDHK_SNAP_RIGHT || pHotKey->id == IDHK_SNAP_UP || pHotKey->id == IDHK_SNAP_DOWN) { HWND topWnd = UserGetForegroundWindow(); if (topWnd) { UserPostMessage(topWnd, WM_KEYDOWN, wVk, 0); } return TRUE; } if (!pHotKey->pWnd) { TRACE("UPTM Hot key Id %d Key %u\n", pHotKey->id, wVk ); UserPostThreadMessage(pHotKey->pti, WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk)); //ptiLastInput = pHotKey->pti; return TRUE; /* Don't send any message */ } else { pWnd = pHotKey->pWnd; if (pWnd == PWND_BOTTOM) { if (gpqForeground == NULL) return FALSE; pWnd = gpqForeground->spwndFocus; } if (pWnd) { // pWnd->head.rpdesk->pDeskInfo->spwndShell needs testing. if (pWnd == ValidateHwndNoErr(InputWindowStation->ShellWindow) && pHotKey->id == SC_TASKLIST) { UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0); co_IntShellHookNotify(HSHELL_TASKMAN, 0, 0); } else { TRACE("UPM Hot key Id %d Key %u\n", pHotKey->id, wVk ); UserPostMessage(UserHMGetHandle(pWnd), WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk)); } //ptiLastInput = pWnd->head.pti; return TRUE; /* Don't send any message */ } } } return FALSE; }
NTSTATUS NTAPI UserDestroyThreadInfo(struct _ETHREAD *Thread) { PTHREADINFO *ppti; PSINGLE_LIST_ENTRY psle; PPROCESSINFO ppiCurrent; struct _EPROCESS *Process; PTHREADINFO ptiCurrent; Process = Thread->ThreadsProcess; /* Get the Win32 Thread */ ptiCurrent = PsGetThreadWin32Thread(Thread); ASSERT(ptiCurrent); TRACE_CH(UserThread,"Destroying pti 0x%p eThread 0x%p\n", ptiCurrent, Thread); ptiCurrent->TIF_flags |= TIF_INCLEANUP; ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags; ppiCurrent = ptiCurrent->ppi; ASSERT(ppiCurrent); IsRemoveAttachThread(ptiCurrent); ptiCurrent->TIF_flags |= TIF_DONTATTACHQUEUE; ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags; /* Decrement thread count and check if its 0 */ ppiCurrent->cThreads--; if(ptiCurrent->TIF_flags & TIF_GUITHREADINITIALIZED) { /* Do now some process cleanup that requires a valid win32 thread */ if(ptiCurrent->ppi->cThreads == 0) { /* Check if we have registered the user api hook */ if(ptiCurrent->ppi == ppiUahServer) { /* Unregister the api hook */ UserUnregisterUserApiHook(); } /* Notify logon application to restart shell if needed */ if(ptiCurrent->pDeskInfo) { if(ptiCurrent->pDeskInfo->ppiShellProcess == ppiCurrent) { DWORD ExitCode = PsGetProcessExitStatus(Process); TRACE_CH(UserProcess, "Shell process is exiting (%lu)\n", ExitCode); UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_SHELL_EXITED, ExitCode); ptiCurrent->pDeskInfo->ppiShellProcess = NULL; } } } DceFreeThreadDCE(ptiCurrent); HOOK_DestroyThreadHooks(Thread); EVENT_DestroyThreadEvents(Thread); DestroyTimersForThread(ptiCurrent); KeSetEvent(ptiCurrent->pEventQueueServer, IO_NO_INCREMENT, FALSE); UnregisterThreadHotKeys(ptiCurrent); /* if (IsListEmpty(&ptiCurrent->WindowListHead)) { ERR_CH(UserThread,"Thread Window List is Empty!\n"); } */ co_DestroyThreadWindows(Thread); if (ppiCurrent && ppiCurrent->ptiList == ptiCurrent && !ptiCurrent->ptiSibling && ppiCurrent->W32PF_flags & W32PF_CLASSESREGISTERED) { TRACE_CH(UserThread,"DestroyProcessClasses\n"); /* no process windows should exist at this point, or the function will assert! */ DestroyProcessClasses(ppiCurrent); ppiCurrent->W32PF_flags &= ~W32PF_CLASSESREGISTERED; } IntBlockInput(ptiCurrent, FALSE); IntCleanupThreadCallbacks(ptiCurrent); /* cleanup user object references stack */ psle = PopEntryList(&ptiCurrent->ReferencesList); while (psle) { PUSER_REFERENCE_ENTRY ref = CONTAINING_RECORD(psle, USER_REFERENCE_ENTRY, Entry); TRACE_CH(UserThread,"thread clean: remove reference obj 0x%p\n",ref->obj); UserDereferenceObject(ref->obj); psle = PopEntryList(&ptiCurrent->ReferencesList); } } /* Find the THREADINFO in the PROCESSINFO's list */ ppti = &ppiCurrent->ptiList; while (*ppti != NULL && *ppti != ptiCurrent) { ppti = &((*ppti)->ptiSibling); } /* we must have found it */ ASSERT(*ppti == ptiCurrent); /* Remove it from the list */ *ppti = ptiCurrent->ptiSibling; if (ptiCurrent->KeyboardLayout) UserDereferenceObject(ptiCurrent->KeyboardLayout); if (gptiForeground == ptiCurrent) { // IntNotifyWinEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, CHILDID_SELF, 0); // IntNotifyWinEvent(EVENT_SYSTEM_FOREGROUND, NULL, OBJID_WINDOW, CHILDID_SELF, 0); gptiForeground = NULL; } // Fixes CORE-6384 & CORE-7030. /* if (ptiLastInput == ptiCurrent) { if (!ppiCurrent->ptiList) ptiLastInput = gptiForeground; else ptiLastInput = ppiCurrent->ptiList; ERR_CH(UserThread,"DTI: ptiLastInput is Cleared!!\n"); } */ TRACE_CH(UserThread,"Freeing pti 0x%p\n", ptiCurrent); /* Free the THREADINFO */ IntDereferenceThreadInfo(ptiCurrent); return STATUS_SUCCESS; }
/* * co_UserProcessHotKeys * * Sends WM_HOTKEY message if given keys are hotkey */ BOOL NTAPI co_UserProcessHotKeys(WORD wVk, BOOL bIsDown) { UINT fModifiers; PHOT_KEY pHotKey; PWND pWnd; BOOL DoNotPostMsg = FALSE; if (wVk == VK_SHIFT || wVk == VK_CONTROL || wVk == VK_MENU || wVk == VK_LWIN || wVk == VK_RWIN) { /* Those keys are specified by modifiers */ wVk = 0; } fModifiers = IntGetModifiers(gafAsyncKeyState); /* Check if it is a hotkey */ pHotKey = IsHotKey(fModifiers, wVk); if (pHotKey) { TRACE("Hot key pressed (pWnd %p, id %d)\n", pHotKey->pWnd, pHotKey->id); /* FIXME: See comment about "UserDebuggerHotKey" on top of this file. */ if (pHotKey->id == IDHK_SHIFTF12 || pHotKey->id == IDHK_F12) { if (bIsDown) { ERR("Hot key pressed for Debug Activation! ShiftF12 = %d or F12 = %d\n",pHotKey->id == IDHK_SHIFTF12 , pHotKey->id == IDHK_F12); //DoNotPostMsg = co_ActivateDebugger(); // FIXME } return DoNotPostMsg; } /* Process hotkey if it is key up event */ if (!bIsDown) { /* WIN and F12 keys are not hardcoded here. See comments on top of this file. */ if (pHotKey->id == IDHK_WINKEY && bWinHotkeyActive) { pWnd = ValidateHwndNoErr(InputWindowStation->ShellWindow); if (pWnd) { TRACE("System Hot key Id %d Key %d\n",pHotKey->id, wVk ); UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0); co_IntShellHookNotify(HSHELL_TASKMAN, 0, 0); bWinHotkeyActive = FALSE; return FALSE; } } } else { /* The user pressed the win key */ if (pHotKey->id == IDHK_WINKEY) { bWinHotkeyActive = TRUE; return FALSE; } } if (bIsDown) { if (!pHotKey->pWnd) { TRACE("UPTM Hot key Id %d Key %d\n",pHotKey->id, wVk ); UserPostThreadMessage(pHotKey->pti, WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk)); //ptiLastInput = pHotKey->pti; return TRUE; /* Don't send any message */ } else { if (pHotKey->pWnd == PWND_BOTTOM) { if (gpqForeground != NULL) { pWnd = gpqForeground->spwndFocus; } else return FALSE; } else { pWnd = pHotKey->pWnd; } if (pWnd) { // pWnd->head.rpdesk->pDeskInfo->spwndShell needs testing. if (pWnd == ValidateHwndNoErr(InputWindowStation->ShellWindow) && pHotKey->id == SC_TASKLIST) { ERR("Sending to shell window w/o IDHK_WINKEY..\n"); UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0); } else { TRACE("UPM Hot key Id %d Key %d\n",pHotKey->id, wVk ); UserPostMessage(UserHMGetHandle(pWnd), WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk)); } //ptiLastInput = pWnd->head.pti; return TRUE; /* Don't send any message */ } } } } return FALSE; }