1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Console Server DLL 4 * FILE: win32ss/user/winsrv/consrv/frontends/gui/text.c 5 * PURPOSE: GUI Terminal Front-End - Support for text-mode screen-buffers 6 * PROGRAMMERS: G� van Geldorp 7 * Johannes Anderwald 8 * Jeffrey Morlan 9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 10 */ 11 12 /* INCLUDES *******************************************************************/ 13 14 #include <consrv.h> 15 16 #define NDEBUG 17 #include <debug.h> 18 19 #include "guiterm.h" 20 21 /* GLOBALS ********************************************************************/ 22 23 #define IS_WHITESPACE(c) ((c) == L'\0' || (c) == L' ' || (c) == L'\t') 24 25 /* FUNCTIONS ******************************************************************/ 26 27 static COLORREF 28 PaletteRGBFromAttrib(PCONSRV_CONSOLE Console, WORD Attribute) 29 { 30 HPALETTE hPalette = Console->ActiveBuffer->PaletteHandle; 31 PALETTEENTRY pe; 32 33 if (hPalette == NULL) return RGBFromAttrib(Console, Attribute); 34 35 GetPaletteEntries(hPalette, Attribute, 1, &pe); 36 return PALETTERGB(pe.peRed, pe.peGreen, pe.peBlue); 37 } 38 39 static VOID 40 CopyBlock(PTEXTMODE_SCREEN_BUFFER Buffer, 41 PSMALL_RECT Selection) 42 { 43 /* 44 * Pressing the Shift key while copying text, allows us to copy 45 * text without newline characters (inline-text copy mode). 46 */ 47 BOOL InlineCopyMode = !!(GetKeyState(VK_SHIFT) & KEY_PRESSED); 48 49 HANDLE hData; 50 PCHAR_INFO ptr; 51 LPWSTR data, dstPos; 52 ULONG selWidth, selHeight; 53 ULONG xPos, yPos; 54 ULONG size; 55 56 DPRINT("CopyBlock(%u, %u, %u, %u)\n", 57 Selection->Left, Selection->Top, Selection->Right, Selection->Bottom); 58 59 /* Prevent against empty blocks */ 60 if (Selection == NULL) return; 61 if (Selection->Left > Selection->Right || Selection->Top > Selection->Bottom) 62 return; 63 64 selWidth = Selection->Right - Selection->Left + 1; 65 selHeight = Selection->Bottom - Selection->Top + 1; 66 67 /* Basic size for one line... */ 68 size = selWidth; 69 /* ... and for the other lines, add newline characters if needed. */ 70 if (selHeight > 0) 71 { 72 /* 73 * If we are not in inline-text copy mode, each selected line must 74 * finish with \r\n . Otherwise, the lines will be just concatenated. 75 */ 76 size += (selWidth + (!InlineCopyMode ? 2 : 0)) * (selHeight - 1); 77 } 78 else 79 { 80 DPRINT1("This case must never happen, because selHeight is at least == 1\n"); 81 } 82 83 size++; /* Null-termination */ 84 size *= sizeof(WCHAR); 85 86 /* Allocate some memory area to be given to the clipboard, so it will not be freed here */ 87 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size); 88 if (hData == NULL) return; 89 90 data = GlobalLock(hData); 91 if (data == NULL) 92 { 93 GlobalFree(hData); 94 return; 95 } 96 97 DPRINT("Copying %dx%d selection\n", selWidth, selHeight); 98 dstPos = data; 99 100 for (yPos = 0; yPos < selHeight; yPos++) 101 { 102 ULONG length = selWidth; 103 104 ptr = ConioCoordToPointer(Buffer, 105 Selection->Left, 106 Selection->Top + yPos); 107 108 /* Trim whitespace from the right */ 109 while (length > 0) 110 { 111 if (IS_WHITESPACE(ptr[length-1].Char.UnicodeChar)) 112 --length; 113 else 114 break; 115 } 116 117 /* Copy only the characters, leave attributes alone */ 118 for (xPos = 0; xPos < length; xPos++) 119 { 120 /* 121 * Sometimes, applications can put NULL chars into the screen-buffer 122 * (this behaviour is allowed). Detect this and replace by a space. 123 */ 124 *dstPos++ = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' '); 125 } 126 127 /* Add newline characters if we are not in inline-text copy mode */ 128 if (!InlineCopyMode) 129 { 130 if (yPos != (selHeight - 1)) 131 { 132 wcscat(dstPos, L"\r\n"); 133 dstPos += 2; 134 } 135 } 136 } 137 138 DPRINT("Setting data <%S> to clipboard\n", data); 139 GlobalUnlock(hData); 140 141 EmptyClipboard(); 142 SetClipboardData(CF_UNICODETEXT, hData); 143 } 144 145 static VOID 146 CopyLines(PTEXTMODE_SCREEN_BUFFER Buffer, 147 PCOORD Begin, 148 PCOORD End) 149 { 150 HANDLE hData; 151 PCHAR_INFO ptr; 152 LPWSTR data, dstPos; 153 ULONG NumChars, size; 154 ULONG xPos, yPos, xBeg, xEnd; 155 156 DPRINT("CopyLines((%u, %u) ; (%u, %u))\n", 157 Begin->X, Begin->Y, End->X, End->Y); 158 159 /* Prevent against empty blocks... */ 160 if (Begin == NULL || End == NULL) return; 161 /* ... or malformed blocks */ 162 if (Begin->Y > End->Y || (Begin->Y == End->Y && Begin->X > End->X)) return; 163 164 /* Compute the number of characters to copy */ 165 if (End->Y == Begin->Y) // top == bottom 166 { 167 NumChars = End->X - Begin->X + 1; 168 } 169 else // if (End->Y > Begin->Y) 170 { 171 NumChars = Buffer->ScreenBufferSize.X - Begin->X; 172 173 if (End->Y >= Begin->Y + 2) 174 { 175 NumChars += (End->Y - Begin->Y - 1) * Buffer->ScreenBufferSize.X; 176 } 177 178 NumChars += End->X + 1; 179 } 180 181 size = (NumChars + 1) * sizeof(WCHAR); /* Null-terminated */ 182 183 /* Allocate some memory area to be given to the clipboard, so it will not be freed here */ 184 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size); 185 if (hData == NULL) return; 186 187 data = GlobalLock(hData); 188 if (data == NULL) 189 { 190 GlobalFree(hData); 191 return; 192 } 193 194 DPRINT("Copying %d characters\n", NumChars); 195 dstPos = data; 196 197 /* 198 * We need to walk per-lines, and not just looping in the big screen-buffer 199 * array, because of the way things are stored inside it. The downside is 200 * that it makes the code more complicated. 201 */ 202 for (yPos = Begin->Y; (yPos <= (ULONG)End->Y) && (NumChars > 0); yPos++) 203 { 204 xBeg = (yPos == Begin->Y ? Begin->X : 0); 205 xEnd = (yPos == End->Y ? End->X : Buffer->ScreenBufferSize.X - 1); 206 207 ptr = ConioCoordToPointer(Buffer, 0, yPos); 208 209 /* Copy only the characters, leave attributes alone */ 210 for (xPos = xBeg; (xPos <= xEnd) && (NumChars-- > 0); xPos++) 211 { 212 /* 213 * Sometimes, applications can put NULL chars into the screen-buffer 214 * (this behaviour is allowed). Detect this and replace by a space. 215 */ 216 *dstPos++ = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' '); 217 } 218 } 219 220 DPRINT("Setting data <%S> to clipboard\n", data); 221 GlobalUnlock(hData); 222 223 EmptyClipboard(); 224 SetClipboardData(CF_UNICODETEXT, hData); 225 } 226 227 228 VOID 229 GetSelectionBeginEnd(PCOORD Begin, PCOORD End, 230 PCOORD SelectionAnchor, 231 PSMALL_RECT SmallRect); 232 233 VOID 234 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, 235 PGUI_CONSOLE_DATA GuiData) 236 { 237 /* 238 * This function supposes that the system clipboard was opened. 239 */ 240 241 BOOL LineSelection = GuiData->LineSelection; 242 243 DPRINT("Selection is (%d|%d) to (%d|%d) in %s mode\n", 244 GuiData->Selection.srSelection.Left, 245 GuiData->Selection.srSelection.Top, 246 GuiData->Selection.srSelection.Right, 247 GuiData->Selection.srSelection.Bottom, 248 (LineSelection ? "line" : "block")); 249 250 if (!LineSelection) 251 { 252 CopyBlock(Buffer, &GuiData->Selection.srSelection); 253 } 254 else 255 { 256 COORD Begin, End; 257 258 GetSelectionBeginEnd(&Begin, &End, 259 &GuiData->Selection.dwSelectionAnchor, 260 &GuiData->Selection.srSelection); 261 262 CopyLines(Buffer, &Begin, &End); 263 } 264 } 265 266 VOID 267 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, 268 PGUI_CONSOLE_DATA GuiData) 269 { 270 /* 271 * This function supposes that the system clipboard was opened. 272 */ 273 274 PCONSRV_CONSOLE Console = Buffer->Header.Console; 275 276 HANDLE hData; 277 LPWSTR str; 278 WCHAR CurChar = 0; 279 280 USHORT VkKey; // MAKEWORD(low = vkey_code, high = shift_state); 281 INPUT_RECORD er; 282 283 hData = GetClipboardData(CF_UNICODETEXT); 284 if (hData == NULL) return; 285 286 str = GlobalLock(hData); 287 if (str == NULL) return; 288 289 DPRINT("Got data <%S> from clipboard\n", str); 290 291 er.EventType = KEY_EVENT; 292 er.Event.KeyEvent.wRepeatCount = 1; 293 while (*str) 294 { 295 /* \r or \n characters. Go to the line only if we get "\r\n" sequence. */ 296 if (CurChar == L'\r' && *str == L'\n') 297 { 298 str++; 299 continue; 300 } 301 CurChar = *str++; 302 303 /* Get the key code (+ shift state) corresponding to the character */ 304 VkKey = VkKeyScanW(CurChar); 305 if (VkKey == 0xFFFF) 306 { 307 DPRINT1("FIXME: TODO: VkKeyScanW failed - Should simulate the key!\n"); 308 /* 309 * We don't really need the scan/key code because we actually only 310 * use the UnicodeChar for output purposes. It may pose few problems 311 * later on but it's not of big importance. One trick would be to 312 * convert the character to OEM / multibyte and use MapVirtualKey 313 * on each byte (simulating an Alt-0xxx OEM keyboard press). 314 */ 315 } 316 317 /* Pressing some control keys */ 318 319 /* Pressing the character key, with the control keys maintained pressed */ 320 er.Event.KeyEvent.bKeyDown = TRUE; 321 er.Event.KeyEvent.wVirtualKeyCode = LOBYTE(VkKey); 322 er.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(LOBYTE(VkKey), MAPVK_VK_TO_VSC); 323 er.Event.KeyEvent.uChar.UnicodeChar = CurChar; 324 er.Event.KeyEvent.dwControlKeyState = 0; 325 if (HIBYTE(VkKey) & 1) 326 er.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; 327 if (HIBYTE(VkKey) & 2) 328 er.Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; // RIGHT_CTRL_PRESSED; 329 if (HIBYTE(VkKey) & 4) 330 er.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; // RIGHT_ALT_PRESSED; 331 332 ConioProcessInputEvent(Console, &er); 333 334 /* Up all the character and control keys */ 335 er.Event.KeyEvent.bKeyDown = FALSE; 336 ConioProcessInputEvent(Console, &er); 337 } 338 339 GlobalUnlock(hData); 340 } 341 342 VOID 343 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, 344 PGUI_CONSOLE_DATA GuiData, 345 PRECT rcView, 346 PRECT rcFramebuffer) 347 { 348 PCONSRV_CONSOLE Console = Buffer->Header.Console; 349 // ASSERT(Console == GuiData->Console); 350 351 ULONG TopLine, BottomLine, LeftChar, RightChar; 352 ULONG Line, Char, Start; 353 PCHAR_INFO From; 354 PWCHAR To; 355 WORD LastAttribute, Attribute; 356 ULONG CursorX, CursorY, CursorHeight; 357 HBRUSH CursorBrush, OldBrush; 358 HFONT OldFont, NewFont; 359 BOOLEAN IsUnderline; 360 361 SetRectEmpty(rcFramebuffer); 362 363 if (Buffer->Buffer == NULL) return; 364 365 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return; 366 367 rcFramebuffer->left = Buffer->ViewOrigin.X * GuiData->CharWidth + rcView->left; 368 rcFramebuffer->top = Buffer->ViewOrigin.Y * GuiData->CharHeight + rcView->top; 369 rcFramebuffer->right = Buffer->ViewOrigin.X * GuiData->CharWidth + rcView->right; 370 rcFramebuffer->bottom = Buffer->ViewOrigin.Y * GuiData->CharHeight + rcView->bottom; 371 372 LeftChar = rcFramebuffer->left / GuiData->CharWidth; 373 TopLine = rcFramebuffer->top / GuiData->CharHeight; 374 RightChar = rcFramebuffer->right / GuiData->CharWidth; 375 BottomLine = rcFramebuffer->bottom / GuiData->CharHeight; 376 377 if (RightChar >= (ULONG)Buffer->ScreenBufferSize.X) RightChar = Buffer->ScreenBufferSize.X - 1; 378 if (BottomLine >= (ULONG)Buffer->ScreenBufferSize.Y) BottomLine = Buffer->ScreenBufferSize.Y - 1; 379 380 LastAttribute = ConioCoordToPointer(Buffer, LeftChar, TopLine)->Attributes; 381 382 SetTextColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute))); 383 SetBkColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute))); 384 385 /* We use the underscore flag as a underline flag */ 386 IsUnderline = !!(LastAttribute & COMMON_LVB_UNDERSCORE); 387 /* Select the new font */ 388 NewFont = GuiData->Font[IsUnderline ? FONT_BOLD : FONT_NORMAL]; 389 OldFont = SelectObject(GuiData->hMemDC, NewFont); 390 391 for (Line = TopLine; Line <= BottomLine; Line++) 392 { 393 WCHAR LineBuffer[80]; // Buffer containing a part or all the line to be displayed 394 From = ConioCoordToPointer(Buffer, LeftChar, Line); // Get the first code of the line 395 Start = LeftChar; 396 To = LineBuffer; 397 398 for (Char = LeftChar; Char <= RightChar; Char++) 399 { 400 /* 401 * We flush the buffer if the new attribute is different 402 * from the current one, or if the buffer is full. 403 */ 404 if (From->Attributes != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR))) 405 { 406 TextOutW(GuiData->hMemDC, 407 Start * GuiData->CharWidth, 408 Line * GuiData->CharHeight, 409 LineBuffer, 410 Char - Start); 411 Start = Char; 412 To = LineBuffer; 413 Attribute = From->Attributes; 414 if (Attribute != LastAttribute) 415 { 416 LastAttribute = Attribute; 417 SetTextColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute))); 418 SetBkColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute))); 419 420 /* Change underline state if needed */ 421 if (!!(LastAttribute & COMMON_LVB_UNDERSCORE) != IsUnderline) 422 { 423 IsUnderline = !!(LastAttribute & COMMON_LVB_UNDERSCORE); 424 /* Select the new font */ 425 NewFont = GuiData->Font[IsUnderline ? FONT_BOLD : FONT_NORMAL]; 426 /* OldFont = */ SelectObject(GuiData->hMemDC, NewFont); 427 } 428 } 429 } 430 431 *(To++) = (From++)->Char.UnicodeChar; 432 } 433 434 TextOutW(GuiData->hMemDC, 435 Start * GuiData->CharWidth, 436 Line * GuiData->CharHeight, 437 LineBuffer, 438 RightChar - Start + 1); 439 } 440 441 /* Restore the old font */ 442 SelectObject(GuiData->hMemDC, OldFont); 443 444 /* 445 * Draw the caret 446 */ 447 if (Buffer->CursorInfo.bVisible && 448 Buffer->CursorBlinkOn && 449 !Buffer->ForceCursorOff) 450 { 451 CursorX = Buffer->CursorPosition.X; 452 CursorY = Buffer->CursorPosition.Y; 453 if (LeftChar <= CursorX && CursorX <= RightChar && 454 TopLine <= CursorY && CursorY <= BottomLine) 455 { 456 CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight); 457 458 Attribute = ConioCoordToPointer(Buffer, Buffer->CursorPosition.X, Buffer->CursorPosition.Y)->Attributes; 459 if (Attribute == DEFAULT_SCREEN_ATTRIB) Attribute = Buffer->ScreenDefaultAttrib; 460 461 CursorBrush = CreateSolidBrush(PaletteRGBFromAttrib(Console, TextAttribFromAttrib(Attribute))); 462 OldBrush = SelectObject(GuiData->hMemDC, CursorBrush); 463 464 PatBlt(GuiData->hMemDC, 465 CursorX * GuiData->CharWidth, 466 CursorY * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight), 467 GuiData->CharWidth, 468 CursorHeight, 469 PATCOPY); 470 471 SelectObject(GuiData->hMemDC, OldBrush); 472 DeleteObject(CursorBrush); 473 } 474 } 475 476 LeaveCriticalSection(&Console->Lock); 477 } 478 479 /* EOF */ 480