/*++ Copyright (c) 1985 - 1999, Microsoft Corporation Module Name: cursor.c Abstract: This file implements the NT console server cursor routines. Author: Therese Stowell (thereses) 5-Dec-1990 Revision History: --*/ #include "precomp.h" #pragma hdrstop //#define PROFILE_GDI #ifdef PROFILE_GDI LONG InvertCount; #define INVERT_CALL InvertCount++ #else #define INVERT_CALL #endif extern UINT guCaretBlinkTime; VOID InvertPixels( IN PSCREEN_INFORMATION ScreenInfo ) /*++ Routine Description: This routine inverts the cursor pixels, making it either visible or invisible. Arguments: ScreenInfo - pointer to screen info structure. Return Value: none. --*/ { #ifdef FE_SB if (CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)) { PCONVERSIONAREA_INFORMATION ConvAreaInfo; SMALL_RECT Region; SMALL_RECT CursorRegion; SMALL_RECT ClippedRegion; #ifdef DBG_KATTR // BeginKAttrCheck(ScreenInfo); #endif ConvAreaInfo = ScreenInfo->Console->ConsoleIme.ConvAreaRoot; CursorRegion.Left = CursorRegion.Right = ScreenInfo->BufferInfo.TextInfo.CursorPosition.X; CursorRegion.Top = CursorRegion.Bottom = ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y; while (ConvAreaInfo) { if ((ConvAreaInfo->ConversionAreaMode & (CA_HIDDEN+CA_HIDE_FOR_SCROLL))==0) { // // Do clipping region // Region.Left = ScreenInfo->Window.Left + ConvAreaInfo->CaInfo.rcViewCaWindow.Left + ConvAreaInfo->CaInfo.coordConView.X; Region.Right = Region.Left + (ConvAreaInfo->CaInfo.rcViewCaWindow.Right - ConvAreaInfo->CaInfo.rcViewCaWindow.Left); Region.Top = ScreenInfo->Window.Top + ConvAreaInfo->CaInfo.rcViewCaWindow.Top + ConvAreaInfo->CaInfo.coordConView.Y; Region.Bottom = Region.Top + (ConvAreaInfo->CaInfo.rcViewCaWindow.Bottom - ConvAreaInfo->CaInfo.rcViewCaWindow.Top); ClippedRegion.Left = max(Region.Left, ScreenInfo->Window.Left); ClippedRegion.Top = max(Region.Top, ScreenInfo->Window.Top); ClippedRegion.Right = min(Region.Right, ScreenInfo->Window.Right); ClippedRegion.Bottom = min(Region.Bottom, ScreenInfo->Window.Bottom); if (ClippedRegion.Right < ClippedRegion.Left || ClippedRegion.Bottom < ClippedRegion.Top) { ; } else { Region = ClippedRegion; ClippedRegion.Left = max(Region.Left, CursorRegion.Left); ClippedRegion.Top = max(Region.Top, CursorRegion.Top); ClippedRegion.Right = min(Region.Right, CursorRegion.Right); ClippedRegion.Bottom = min(Region.Bottom, CursorRegion.Bottom); if (ClippedRegion.Right < ClippedRegion.Left || ClippedRegion.Bottom < ClippedRegion.Top) { ; } else { return; } } } ConvAreaInfo = ConvAreaInfo->ConvAreaNext; } } #endif // FE_SB { ULONG CursorYSize; POLYPATBLT PolyData; #ifdef FE_SB SHORT RowIndex; PROW Row; COORD TargetPoint; int iTrailing = 0; int iDBCursor = 1; #endif INVERT_CALL; CursorYSize = ScreenInfo->BufferInfo.TextInfo.CursorYSize; if (ScreenInfo->BufferInfo.TextInfo.DoubleCursor) { if (ScreenInfo->BufferInfo.TextInfo.CursorSize > 50) CursorYSize = CursorYSize >> 1; else CursorYSize = CursorYSize << 1; } #ifdef FE_SB if (CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)) { TargetPoint = ScreenInfo->BufferInfo.TextInfo.CursorPosition; RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+TargetPoint.Y) % ScreenInfo->ScreenBufferSize.Y; Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex]; ASSERT(Row); if ((Row->CharRow.KAttrs[TargetPoint.X] & ATTR_TRAILING_BYTE) && ScreenInfo->BufferInfo.TextInfo.CursorDBEnable) { iTrailing = 1; iDBCursor = 2; } else if ((Row->CharRow.KAttrs[TargetPoint.X] & ATTR_LEADING_BYTE) && ScreenInfo->BufferInfo.TextInfo.CursorDBEnable) { iDBCursor = 2; } } PolyData.x = (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X - ScreenInfo->Window.Left - iTrailing) * SCR_FONTSIZE(ScreenInfo).X; PolyData.y = (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y - ScreenInfo->Window.Top) * SCR_FONTSIZE(ScreenInfo).Y + (CURSOR_Y_OFFSET_IN_PIXELS(SCR_FONTSIZE(ScreenInfo).Y,CursorYSize)); PolyData.cx = SCR_FONTSIZE(ScreenInfo).X * iDBCursor; PolyData.cy = CursorYSize; #else PolyData.x = (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X-ScreenInfo->Window.Left)*SCR_FONTSIZE(ScreenInfo).X; PolyData.y = (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y-ScreenInfo->Window.Top)*SCR_FONTSIZE(ScreenInfo).Y+(CURSOR_Y_OFFSET_IN_PIXELS(SCR_FONTSIZE(ScreenInfo).Y,CursorYSize)); PolyData.cx = SCR_FONTSIZE(ScreenInfo).X; PolyData.cy = CursorYSize; #endif PolyData.BrClr.hbr = GetStockObject(LTGRAY_BRUSH); PolyPatBlt(ScreenInfo->Console->hDC, PATINVERT, &PolyData, 1, PPB_BRUSH); GdiFlush(); } } VOID ConsoleShowCursor( IN PSCREEN_INFORMATION ScreenInfo ) /*++ Routine Description: This routine makes the cursor visible both in the data structures and on the screen. Arguments: ScreenInfo - pointer to screen info structure. Return Value: none. --*/ { if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) { ASSERT (ScreenInfo->BufferInfo.TextInfo.UpdatingScreen>0); if (--ScreenInfo->BufferInfo.TextInfo.UpdatingScreen == 0) { ScreenInfo->BufferInfo.TextInfo.CursorOn = FALSE; } } } VOID ConsoleHideCursor( IN PSCREEN_INFORMATION ScreenInfo ) /*++ Routine Description: This routine makes the cursor invisible both in the data structures and on the screen. Arguments: ScreenInfo - pointer to screen info structure. Return Value: none. --*/ { if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) { if (++ScreenInfo->BufferInfo.TextInfo.UpdatingScreen == 1) { if (ScreenInfo->BufferInfo.TextInfo.CursorVisible && ScreenInfo->BufferInfo.TextInfo.CursorOn && ScreenInfo->Console->CurrentScreenBuffer == ScreenInfo && !(ScreenInfo->Console->Flags & CONSOLE_IS_ICONIC)) { InvertPixels(ScreenInfo); ScreenInfo->BufferInfo.TextInfo.CursorOn = FALSE; } } } } NTSTATUS SetCursorInformation( IN PSCREEN_INFORMATION ScreenInfo, ULONG Size, BOOLEAN Visible ) /*++ Routine Description: This routine sets the cursor size and visibility both in the data structures and on the screen. Arguments: ScreenInfo - pointer to screen info structure. Size - cursor size Visible - cursor visibility Return Value: Status --*/ { if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) { ConsoleHideCursor(ScreenInfo); ScreenInfo->BufferInfo.TextInfo.CursorSize = Size; ScreenInfo->BufferInfo.TextInfo.CursorVisible = Visible; ScreenInfo->BufferInfo.TextInfo.CursorYSize = (WORD)CURSOR_SIZE_IN_PIXELS(SCR_FONTSIZE(ScreenInfo).Y,ScreenInfo->BufferInfo.TextInfo.CursorSize); #ifdef i386 if ((!(ScreenInfo->Console->Flags & CONSOLE_VDM_REGISTERED)) && ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) { SetCursorInformationHW(ScreenInfo,Size,Visible); } #endif ConsoleShowCursor(ScreenInfo); } return STATUS_SUCCESS; } NTSTATUS SetCursorMode( IN PSCREEN_INFORMATION ScreenInfo, BOOLEAN DoubleCursor ) /*++ Routine Description: This routine sets a flag saying whether the cursor should be displayed with it's default size or it should be modified to indicate the insert/overtype mode has changed. Arguments: ScreenInfo - pointer to screen info structure. DoubleCursor - should we indicated non-normal mode Return Value: Status --*/ { if ((ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) && (ScreenInfo->BufferInfo.TextInfo.DoubleCursor != DoubleCursor)) { ConsoleHideCursor(ScreenInfo); ScreenInfo->BufferInfo.TextInfo.DoubleCursor = DoubleCursor; #ifdef i386 if ((!(ScreenInfo->Console->Flags & CONSOLE_VDM_REGISTERED)) && ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) { SetCursorInformationHW(ScreenInfo, ScreenInfo->BufferInfo.TextInfo.CursorSize, ScreenInfo->BufferInfo.TextInfo.CursorVisible); } #endif ConsoleShowCursor(ScreenInfo); } return STATUS_SUCCESS; } VOID CursorTimerRoutine( IN PSCREEN_INFORMATION ScreenInfo ) /*++ Routine Description: This routine is called when the timer in the console with the focus goes off. It blinks the cursor. Arguments: ScreenInfo - pointer to screen info structure. Return Value: none. --*/ { if (!(ScreenInfo->Console->Flags & CONSOLE_HAS_FOCUS)) return; if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) { // // Update the cursor pos in USER so accessibility will work // if (ScreenInfo->BufferInfo.TextInfo.CursorMoved) { CONSOLE_CARET_INFO ConsoleCaretInfo; DWORD dwFlags = 0; ScreenInfo->BufferInfo.TextInfo.CursorMoved = FALSE; ConsoleCaretInfo.hwnd = ScreenInfo->Console->hWnd; ConsoleCaretInfo.rc.left = (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X - ScreenInfo->Window.Left) * SCR_FONTSIZE(ScreenInfo).X; ConsoleCaretInfo.rc.top = (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y - ScreenInfo->Window.Top) * SCR_FONTSIZE(ScreenInfo).Y; ConsoleCaretInfo.rc.right = ConsoleCaretInfo.rc.left + SCR_FONTSIZE(ScreenInfo).X; ConsoleCaretInfo.rc.bottom = ConsoleCaretInfo.rc.top + SCR_FONTSIZE(ScreenInfo).Y; NtUserConsoleControl(ConsoleSetCaretInfo, &ConsoleCaretInfo, sizeof(ConsoleCaretInfo)); if (ScreenInfo->BufferInfo.TextInfo.CursorVisible) { dwFlags |= CONSOLE_CARET_VISIBLE; } if (ScreenInfo->Console->Flags & CONSOLE_SELECTING) { dwFlags |= CONSOLE_CARET_SELECTION; } ConsoleNotifyWinEvent(ScreenInfo->Console, EVENT_CONSOLE_CARET, dwFlags, PACKCOORD(ScreenInfo->BufferInfo.TextInfo.CursorPosition)); } // if the DelayCursor flag has been set, wait one more tick before toggle. // This is used to guarantee the cursor is on for a finite period of time // after a move and off for a finite period of time after a WriteString if (ScreenInfo->BufferInfo.TextInfo.DelayCursor) { ScreenInfo->BufferInfo.TextInfo.DelayCursor = FALSE; return; } // // Don't blink the cursor for remote sessions. // if ((NtCurrentPeb()->SessionId != WTSGetActiveConsoleSessionId() || (guCaretBlinkTime == (UINT)-1)) && ScreenInfo->BufferInfo.TextInfo.CursorOn) { return; } if (ScreenInfo->BufferInfo.TextInfo.CursorVisible && !ScreenInfo->BufferInfo.TextInfo.UpdatingScreen) { InvertPixels(ScreenInfo); ScreenInfo->BufferInfo.TextInfo.CursorOn = !ScreenInfo->BufferInfo.TextInfo.CursorOn; } } } #ifdef i386 NTSTATUS SetCursorPositionHW( IN OUT PSCREEN_INFORMATION ScreenInfo, IN COORD Position ) /*++ Routine Description: This routine moves the cursor. Arguments: ScreenInfo - Pointer to screen buffer information. Position - Contains the new position of the cursor in screen buffer coordinates. Return Value: none. --*/ { #if defined(FE_SB) FSVIDEO_CURSOR_POSITION CursorPosition; SHORT RowIndex; PROW Row; COORD TargetPoint; if (ScreenInfo->ConvScreenInfo) return STATUS_SUCCESS; TargetPoint.X = Position.X; TargetPoint.Y = Position.Y; RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+TargetPoint.Y) % ScreenInfo->ScreenBufferSize.Y; Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex]; if (!CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)) CursorPosition.dwType = CHAR_TYPE_SBCS; else if (Row->CharRow.KAttrs[TargetPoint.X] & ATTR_TRAILING_BYTE) CursorPosition.dwType = CHAR_TYPE_TRAILING; else if (Row->CharRow.KAttrs[TargetPoint.X] & ATTR_LEADING_BYTE) CursorPosition.dwType = CHAR_TYPE_LEADING; else CursorPosition.dwType = CHAR_TYPE_SBCS; // set cursor position CursorPosition.Coord.Column = Position.X - ScreenInfo->Window.Left; CursorPosition.Coord.Row = Position.Y - ScreenInfo->Window.Top; #else VIDEO_CURSOR_POSITION CursorPosition; // set cursor position CursorPosition.Column = Position.X - ScreenInfo->Window.Left; CursorPosition.Row = Position.Y - ScreenInfo->Window.Top; #endif return GdiFullscreenControl(FullscreenControlSetCursorPosition, (PVOID)&CursorPosition, sizeof(CursorPosition), NULL, 0); } #endif NTSTATUS SetCursorPosition( IN OUT PSCREEN_INFORMATION ScreenInfo, IN COORD Position, IN BOOL TurnOn ) /*++ Routine Description: This routine sets the cursor position in the data structures and on the screen. Arguments: ScreenInfo - pointer to screen info structure. Position - new position of cursor TurnOn - true if cursor should be left on, false if should be left off Return Value: Status --*/ { // // Ensure that the cursor position is within the constraints of the screen // buffer. // if (Position.X >= ScreenInfo->ScreenBufferSize.X || Position.Y >= ScreenInfo->ScreenBufferSize.Y || Position.X < 0 || Position.Y < 0) { return STATUS_INVALID_PARAMETER; } ConsoleHideCursor(ScreenInfo); ScreenInfo->BufferInfo.TextInfo.CursorPosition = Position; #ifdef i386 if ((!(ScreenInfo->Console->Flags & CONSOLE_VDM_REGISTERED)) && ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) { SetCursorPositionHW(ScreenInfo,Position); } #endif ConsoleShowCursor(ScreenInfo); // if we have the focus, adjust the cursor state if (ScreenInfo->Console->Flags & CONSOLE_HAS_FOCUS) { if (TurnOn) { ScreenInfo->BufferInfo.TextInfo.DelayCursor = FALSE; CursorTimerRoutine(ScreenInfo); } else { ScreenInfo->BufferInfo.TextInfo.DelayCursor = TRUE; } ScreenInfo->BufferInfo.TextInfo.CursorMoved = TRUE; } return STATUS_SUCCESS; } #ifdef i386 NTSTATUS SetCursorInformationHW( PSCREEN_INFORMATION ScreenInfo, ULONG Size, BOOLEAN Visible ) { VIDEO_CURSOR_ATTRIBUTES CursorAttr; ULONG FontSizeY; if (ScreenInfo->BufferInfo.TextInfo.DoubleCursor) { if (Size > 50) Size = Size >> 1; else Size = Size << 1; } ASSERT (Size <= 100 && Size > 0); FontSizeY = CONSOLE_WINDOW_SIZE_Y(ScreenInfo) > 25 ? 8 : 16; CursorAttr.Height = (USHORT)CURSOR_PERCENTAGE_TO_TOP_SCAN_LINE(FontSizeY,Size); CursorAttr.Width = 31; CursorAttr.Enable = Visible; return GdiFullscreenControl(FullscreenControlSetCursorAttributes, (PVOID)&CursorAttr, sizeof(CursorAttr), NULL, 0); } #endif