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
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
12 /* INCLUDES *******************************************************************/
21 /* GLOBALS ********************************************************************/
23 #define IS_WHITESPACE(c) ((c) == L'\0' || (c) == L' ' || (c) == L'\t')
25 /* FUNCTIONS ******************************************************************/
28 PaletteRGBFromAttrib(PCONSRV_CONSOLE Console
, WORD Attribute
)
30 HPALETTE hPalette
= Console
->ActiveBuffer
->PaletteHandle
;
33 if (hPalette
== NULL
) return RGBFromAttrib(Console
, Attribute
);
35 GetPaletteEntries(hPalette
, Attribute
, 1, &pe
);
36 return PALETTERGB(pe
.peRed
, pe
.peGreen
, pe
.peBlue
);
40 CopyBlock(PTEXTMODE_SCREEN_BUFFER Buffer
,
41 PSMALL_RECT Selection
)
44 * Pressing the Shift key while copying text, allows us to copy
45 * text without newline characters (inline-text copy mode).
47 BOOL InlineCopyMode
= !!(GetKeyState(VK_SHIFT
) & KEY_PRESSED
);
52 ULONG selWidth
, selHeight
;
56 DPRINT("CopyBlock(%u, %u, %u, %u)\n",
57 Selection
->Left
, Selection
->Top
, Selection
->Right
, Selection
->Bottom
);
59 /* Prevent against empty blocks */
60 if ((Selection
== NULL
) || ConioIsRectEmpty(Selection
))
63 selWidth
= ConioRectWidth(Selection
);
64 selHeight
= ConioRectHeight(Selection
);
66 /* Basic size for one line... */
68 /* ... and for the other lines, add newline characters if needed. */
72 * If we are not in inline-text copy mode, each selected line must
73 * finish with \r\n . Otherwise, the lines will be just concatenated.
75 size
+= (selWidth
+ (!InlineCopyMode
? 2 : 0)) * (selHeight
- 1);
79 DPRINT1("This case must never happen, because selHeight is at least == 1\n");
82 size
++; /* Null-termination */
83 size
*= sizeof(WCHAR
);
85 /* Allocate some memory area to be given to the clipboard, so it will not be freed here */
86 hData
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, size
);
87 if (hData
== NULL
) return;
89 data
= GlobalLock(hData
);
96 DPRINT("Copying %dx%d selection\n", selWidth
, selHeight
);
99 for (yPos
= 0; yPos
< selHeight
; yPos
++)
101 ULONG length
= selWidth
;
103 ptr
= ConioCoordToPointer(Buffer
,
105 Selection
->Top
+ yPos
);
107 /* Trim whitespace from the right */
110 if (IS_WHITESPACE(ptr
[length
-1].Char
.UnicodeChar
))
116 /* Copy only the characters, leave attributes alone */
117 for (xPos
= 0; xPos
< length
; xPos
++)
120 * Sometimes, applications can put NULL chars into the screen-buffer
121 * (this behaviour is allowed). Detect this and replace by a space.
122 * For full-width characters: copy only the character specified
123 * in the leading-byte cell, skipping the trailing-byte cell.
125 if (!(ptr
[xPos
].Attributes
& COMMON_LVB_TRAILING_BYTE
))
127 *dstPos
++ = (ptr
[xPos
].Char
.UnicodeChar
? ptr
[xPos
].Char
.UnicodeChar
: L
' ');
131 /* Add newline characters if we are not in inline-text copy mode */
134 if (yPos
!= (selHeight
- 1))
136 wcscat(dstPos
, L
"\r\n");
142 DPRINT("Setting data <%S> to clipboard\n", data
);
146 SetClipboardData(CF_UNICODETEXT
, hData
);
150 CopyLines(PTEXTMODE_SCREEN_BUFFER Buffer
,
157 ULONG NumChars
, size
;
158 ULONG xPos
, yPos
, xBeg
, xEnd
;
160 DPRINT("CopyLines((%u, %u) ; (%u, %u))\n",
161 Begin
->X
, Begin
->Y
, End
->X
, End
->Y
);
163 /* Prevent against empty blocks... */
164 if (Begin
== NULL
|| End
== NULL
) return;
165 /* ... or malformed blocks */
166 if (Begin
->Y
> End
->Y
|| (Begin
->Y
== End
->Y
&& Begin
->X
> End
->X
)) return;
168 /* Compute the number of characters to copy */
169 if (End
->Y
== Begin
->Y
) // top == bottom
171 NumChars
= End
->X
- Begin
->X
+ 1;
173 else // if (End->Y > Begin->Y)
175 NumChars
= Buffer
->ScreenBufferSize
.X
- Begin
->X
;
177 if (End
->Y
>= Begin
->Y
+ 2)
179 NumChars
+= (End
->Y
- Begin
->Y
- 1) * Buffer
->ScreenBufferSize
.X
;
182 NumChars
+= End
->X
+ 1;
185 size
= (NumChars
+ 1) * sizeof(WCHAR
); /* Null-terminated */
187 /* Allocate some memory area to be given to the clipboard, so it will not be freed here */
188 hData
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, size
);
189 if (hData
== NULL
) return;
191 data
= GlobalLock(hData
);
198 DPRINT("Copying %d characters\n", NumChars
);
202 * We need to walk per-lines, and not just looping in the big screen-buffer
203 * array, because of the way things are stored inside it. The downside is
204 * that it makes the code more complicated.
206 for (yPos
= Begin
->Y
; (yPos
<= (ULONG
)End
->Y
) && (NumChars
> 0); yPos
++)
208 xBeg
= (yPos
== Begin
->Y
? Begin
->X
: 0);
209 xEnd
= (yPos
== End
->Y
? End
->X
: Buffer
->ScreenBufferSize
.X
- 1);
211 ptr
= ConioCoordToPointer(Buffer
, 0, yPos
);
213 /* Copy only the characters, leave attributes alone */
214 for (xPos
= xBeg
; (xPos
<= xEnd
) && (NumChars
-- > 0); xPos
++)
217 * Sometimes, applications can put NULL chars into the screen-buffer
218 * (this behaviour is allowed). Detect this and replace by a space.
219 * For full-width characters: copy only the character specified
220 * in the leading-byte cell, skipping the trailing-byte cell.
222 if (!(ptr
[xPos
].Attributes
& COMMON_LVB_TRAILING_BYTE
))
224 *dstPos
++ = (ptr
[xPos
].Char
.UnicodeChar
? ptr
[xPos
].Char
.UnicodeChar
: L
' ');
229 DPRINT("Setting data <%S> to clipboard\n", data
);
233 SetClipboardData(CF_UNICODETEXT
, hData
);
239 IN PCONSRV_CONSOLE Console
,
243 USHORT VkKey
; // MAKEWORD(low = vkey_code, high = shift_state);
247 /* Do nothing if we have nothing to paste */
248 if (!Buffer
|| (cchSize
<= 0))
251 er
.EventType
= KEY_EVENT
;
252 er
.Event
.KeyEvent
.wRepeatCount
= 1;
255 /* \r or \n characters. Go to the line only if we get "\r\n" sequence. */
256 if (CurChar
== L
'\r' && *Buffer
== L
'\n')
263 /* Get the key code (+ shift state) corresponding to the character */
264 VkKey
= VkKeyScanW(CurChar
);
267 DPRINT1("FIXME: TODO: VkKeyScanW failed - Should simulate the key!\n");
269 * We don't really need the scan/key code because we actually only
270 * use the UnicodeChar for output purposes. It may pose few problems
271 * later on but it's not of big importance. One trick would be to
272 * convert the character to OEM / multibyte and use MapVirtualKey()
273 * on each byte (simulating an Alt-0xxx OEM keyboard press).
277 /* Pressing some control keys */
279 /* Pressing the character key, with the control keys maintained pressed */
280 er
.Event
.KeyEvent
.bKeyDown
= TRUE
;
281 er
.Event
.KeyEvent
.wVirtualKeyCode
= LOBYTE(VkKey
);
282 er
.Event
.KeyEvent
.wVirtualScanCode
= MapVirtualKeyW(LOBYTE(VkKey
), MAPVK_VK_TO_VSC
);
283 er
.Event
.KeyEvent
.uChar
.UnicodeChar
= CurChar
;
284 er
.Event
.KeyEvent
.dwControlKeyState
= 0;
285 if (HIBYTE(VkKey
) & 1)
286 er
.Event
.KeyEvent
.dwControlKeyState
|= SHIFT_PRESSED
;
287 if (HIBYTE(VkKey
) & 2)
288 er
.Event
.KeyEvent
.dwControlKeyState
|= LEFT_CTRL_PRESSED
; // RIGHT_CTRL_PRESSED;
289 if (HIBYTE(VkKey
) & 4)
290 er
.Event
.KeyEvent
.dwControlKeyState
|= LEFT_ALT_PRESSED
; // RIGHT_ALT_PRESSED;
292 ConioProcessInputEvent(Console
, &er
);
294 /* Up all the character and control keys */
295 er
.Event
.KeyEvent
.bKeyDown
= FALSE
;
296 ConioProcessInputEvent(Console
, &er
);
301 GetSelectionBeginEnd(PCOORD Begin
, PCOORD End
,
302 PCOORD SelectionAnchor
,
303 PSMALL_RECT SmallRect
);
306 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
307 PGUI_CONSOLE_DATA GuiData
)
310 * This function supposes that the system clipboard was opened.
313 BOOL LineSelection
= GuiData
->LineSelection
;
315 DPRINT("Selection is (%d|%d) to (%d|%d) in %s mode\n",
316 GuiData
->Selection
.srSelection
.Left
,
317 GuiData
->Selection
.srSelection
.Top
,
318 GuiData
->Selection
.srSelection
.Right
,
319 GuiData
->Selection
.srSelection
.Bottom
,
320 (LineSelection
? "line" : "block"));
324 CopyBlock(Buffer
, &GuiData
->Selection
.srSelection
);
330 GetSelectionBeginEnd(&Begin
, &End
,
331 &GuiData
->Selection
.dwSelectionAnchor
,
332 &GuiData
->Selection
.srSelection
);
334 CopyLines(Buffer
, &Begin
, &End
);
339 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
340 PGUI_CONSOLE_DATA GuiData
)
343 * This function supposes that the system clipboard was opened.
346 PCONSRV_CONSOLE Console
= Buffer
->Header
.Console
;
351 hData
= GetClipboardData(CF_UNICODETEXT
);
352 if (hData
== NULL
) return;
354 pszText
= GlobalLock(hData
);
355 if (pszText
== NULL
) return;
357 DPRINT("Got data <%S> from clipboard\n", pszText
);
358 PasteText(Console
, pszText
, wcslen(pszText
));
365 PTEXTMODE_SCREEN_BUFFER Buffer
,
366 PGUI_CONSOLE_DATA GuiData
,
372 PCONSRV_CONSOLE Console
= Buffer
->Header
.Console
;
374 ULONG CursorX
, CursorY
, CursorHeight
;
375 HBRUSH CursorBrush
, OldBrush
;
378 if (Buffer
->CursorInfo
.bVisible
&&
379 Buffer
->CursorBlinkOn
&&
380 !Buffer
->ForceCursorOff
)
382 CursorX
= Buffer
->CursorPosition
.X
;
383 CursorY
= Buffer
->CursorPosition
.Y
;
384 if (LeftColumn
<= CursorX
&& CursorX
<= RightColumn
&&
385 TopLine
<= CursorY
&& CursorY
<= BottomLine
)
387 CursorHeight
= ConioEffectiveCursorSize(Console
, GuiData
->CharHeight
);
389 Attribute
= ConioCoordToPointer(Buffer
, Buffer
->CursorPosition
.X
, Buffer
->CursorPosition
.Y
)->Attributes
;
390 if (Attribute
== DEFAULT_SCREEN_ATTRIB
)
391 Attribute
= Buffer
->ScreenDefaultAttrib
;
393 CursorBrush
= CreateSolidBrush(PaletteRGBFromAttrib(Console
, TextAttribFromAttrib(Attribute
)));
394 OldBrush
= SelectObject(GuiData
->hMemDC
, CursorBrush
);
396 if (Attribute
& COMMON_LVB_LEADING_BYTE
)
398 /* The caret is on the leading byte */
399 PatBlt(GuiData
->hMemDC
,
400 CursorX
* GuiData
->CharWidth
,
401 CursorY
* GuiData
->CharHeight
+ (GuiData
->CharHeight
- CursorHeight
),
402 GuiData
->CharWidth
* 2,
406 else if (Attribute
& COMMON_LVB_TRAILING_BYTE
)
408 /* The caret is on the trailing byte */
409 PatBlt(GuiData
->hMemDC
,
410 (CursorX
- 1) * GuiData
->CharWidth
,
411 CursorY
* GuiData
->CharHeight
+ (GuiData
->CharHeight
- CursorHeight
),
412 GuiData
->CharWidth
* 2,
418 PatBlt(GuiData
->hMemDC
,
419 CursorX
* GuiData
->CharWidth
,
420 CursorY
* GuiData
->CharHeight
+ (GuiData
->CharHeight
- CursorHeight
),
426 SelectObject(GuiData
->hMemDC
, OldBrush
);
427 DeleteObject(CursorBrush
);
433 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
434 PGUI_CONSOLE_DATA GuiData
,
438 PCONSRV_CONSOLE Console
= Buffer
->Header
.Console
;
439 ULONG TopLine
, BottomLine
, LeftColumn
, RightColumn
;
440 ULONG Line
, Char
, Start
;
443 WORD LastAttribute
, Attribute
;
444 HFONT OldFont
, NewFont
;
447 // ASSERT(Console == GuiData->Console);
449 ConioInitLongRect(rcFramebuffer
, 0, 0, 0, 0);
451 if (Buffer
->Buffer
== NULL
)
454 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
457 ConioInitLongRect(rcFramebuffer
,
458 Buffer
->ViewOrigin
.Y
* GuiData
->CharHeight
+ rcView
->top
,
459 Buffer
->ViewOrigin
.X
* GuiData
->CharWidth
+ rcView
->left
,
460 Buffer
->ViewOrigin
.Y
* GuiData
->CharHeight
+ rcView
->bottom
,
461 Buffer
->ViewOrigin
.X
* GuiData
->CharWidth
+ rcView
->right
);
463 LeftColumn
= rcFramebuffer
->left
/ GuiData
->CharWidth
;
464 RightColumn
= rcFramebuffer
->right
/ GuiData
->CharWidth
;
465 if (RightColumn
>= (ULONG
)Buffer
->ScreenBufferSize
.X
)
466 RightColumn
= Buffer
->ScreenBufferSize
.X
- 1;
468 TopLine
= rcFramebuffer
->top
/ GuiData
->CharHeight
;
469 BottomLine
= rcFramebuffer
->bottom
/ GuiData
->CharHeight
;
470 if (BottomLine
>= (ULONG
)Buffer
->ScreenBufferSize
.Y
)
471 BottomLine
= Buffer
->ScreenBufferSize
.Y
- 1;
473 LastAttribute
= ConioCoordToPointer(Buffer
, LeftColumn
, TopLine
)->Attributes
;
475 SetTextColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, TextAttribFromAttrib(LastAttribute
)));
476 SetBkColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, BkgdAttribFromAttrib(LastAttribute
)));
478 /* We use the underscore flag as a underline flag */
479 IsUnderline
= !!(LastAttribute
& COMMON_LVB_UNDERSCORE
);
480 /* Select the new font */
481 NewFont
= GuiData
->Font
[IsUnderline
? FONT_BOLD
: FONT_NORMAL
];
482 OldFont
= SelectObject(GuiData
->hMemDC
, NewFont
);
486 for (Line
= TopLine
; Line
<= BottomLine
; Line
++)
488 for (Char
= LeftColumn
; Char
<= RightColumn
; Char
++)
490 From
= ConioCoordToPointer(Buffer
, Char
, Line
);
491 Attribute
= From
->Attributes
;
492 SetTextColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, TextAttribFromAttrib(Attribute
)));
493 SetBkColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, BkgdAttribFromAttrib(Attribute
)));
495 /* Change underline state if needed */
496 if (!!(Attribute
& COMMON_LVB_UNDERSCORE
) != IsUnderline
)
498 IsUnderline
= !!(Attribute
& COMMON_LVB_UNDERSCORE
);
500 /* Select the new font */
501 NewFont
= GuiData
->Font
[IsUnderline
? FONT_BOLD
: FONT_NORMAL
];
502 SelectObject(GuiData
->hMemDC
, NewFont
);
505 if (Attribute
& COMMON_LVB_TRAILING_BYTE
)
508 TextOutW(GuiData
->hMemDC
,
509 Char
* GuiData
->CharWidth
,
510 Line
* GuiData
->CharHeight
,
511 &From
->Char
.UnicodeChar
, 1);
517 for (Line
= TopLine
; Line
<= BottomLine
; Line
++)
519 WCHAR LineBuffer
[80]; // Buffer containing a part or all the line to be displayed
520 From
= ConioCoordToPointer(Buffer
, LeftColumn
, Line
); // Get the first code of the line
524 for (Char
= LeftColumn
; Char
<= RightColumn
; Char
++)
527 * We flush the buffer if the new attribute is different
528 * from the current one, or if the buffer is full.
530 if (From
->Attributes
!= LastAttribute
|| (Char
- Start
== sizeof(LineBuffer
) / sizeof(WCHAR
)))
532 TextOutW(GuiData
->hMemDC
,
533 Start
* GuiData
->CharWidth
,
534 Line
* GuiData
->CharHeight
,
539 Attribute
= From
->Attributes
;
540 if (Attribute
!= LastAttribute
)
542 LastAttribute
= Attribute
;
543 SetTextColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, TextAttribFromAttrib(LastAttribute
)));
544 SetBkColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, BkgdAttribFromAttrib(LastAttribute
)));
546 /* Change underline state if needed */
547 if (!!(LastAttribute
& COMMON_LVB_UNDERSCORE
) != IsUnderline
)
549 IsUnderline
= !!(LastAttribute
& COMMON_LVB_UNDERSCORE
);
550 /* Select the new font */
551 NewFont
= GuiData
->Font
[IsUnderline
? FONT_BOLD
: FONT_NORMAL
];
552 SelectObject(GuiData
->hMemDC
, NewFont
);
557 *(To
++) = (From
++)->Char
.UnicodeChar
;
560 TextOutW(GuiData
->hMemDC
,
561 Start
* GuiData
->CharWidth
,
562 Line
* GuiData
->CharHeight
,
564 RightColumn
- Start
+ 1);
568 /* Restore the old font */
569 SelectObject(GuiData
->hMemDC
, OldFont
);
572 GuiPaintCaret(Buffer
, GuiData
, TopLine
, BottomLine
, LeftColumn
, RightColumn
);
574 LeaveCriticalSection(&Console
->Lock
);