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(PCONSOLE 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 ChangeFontAttributes(PGUI_CONSOLE_DATA GuiData
,
50 /* Initialize the LOGFONT structure */
51 RtlZeroMemory(&lf
, sizeof(lf
));
53 /* Retrieve the details of the current font */
54 if (GetObject(GuiData
->Font
, sizeof(lf
), &lf
) == 0)
55 return NULL
; // GuiData->Font;
57 /* Change the font attributes */
58 // lf.lfHeight = FontSize.Y;
59 // lf.lfWidth = FontSize.X;
60 lf
.lfWeight
= FontWeight
;
61 lf
.lfItalic
= bItalic
;
62 lf
.lfUnderline
= bUnderline
;
63 lf
.lfStrikeOut
= bStrikeOut
;
65 /* Build a new font */
66 NewFont
= CreateFontIndirect(&lf
);
68 return NULL
; // GuiData->Font;
70 // FIXME: Do we need to update GuiData->CharWidth and GuiData->CharHeight ??
72 /* Select it (return the old font) */
73 // return SelectObject(GuiData->hMemDC, NewFont);
78 CopyBlock(PTEXTMODE_SCREEN_BUFFER Buffer
,
79 PSMALL_RECT Selection
)
82 * Pressing the Shift key while copying text, allows us to copy
83 * text without newline characters (inline-text copy mode).
85 BOOL InlineCopyMode
= !!(GetKeyState(VK_SHIFT
) & 0x8000);
90 ULONG selWidth
, selHeight
;
94 DPRINT("CopyBlock(%u, %u, %u, %u)\n",
95 Selection
->Left
, Selection
->Top
, Selection
->Right
, Selection
->Bottom
);
97 /* Prevent against empty blocks */
98 if (Selection
== NULL
) return;
99 if (Selection
->Left
> Selection
->Right
|| Selection
->Top
> Selection
->Bottom
)
102 selWidth
= Selection
->Right
- Selection
->Left
+ 1;
103 selHeight
= Selection
->Bottom
- Selection
->Top
+ 1;
105 /* Basic size for one line... */
107 /* ... and for the other lines, add newline characters if needed. */
111 * If we are not in inline-text copy mode, each selected line must
112 * finish with \r\n . Otherwise, the lines will be just concatenated.
114 size
+= (selWidth
+ (!InlineCopyMode
? 2 : 0)) * (selHeight
- 1);
118 DPRINT1("This case must never happen, because selHeight is at least == 1\n");
121 size
+= 1; /* Null-termination */
122 size
*= sizeof(WCHAR
);
124 /* Allocate some memory area to be given to the clipboard, so it will not be freed here */
125 hData
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, size
);
126 if (hData
== NULL
) return;
128 data
= GlobalLock(hData
);
135 DPRINT("Copying %dx%d selection\n", selWidth
, selHeight
);
138 for (yPos
= 0; yPos
< selHeight
; yPos
++)
140 ULONG length
= selWidth
;
142 ptr
= ConioCoordToPointer(Buffer
,
144 Selection
->Top
+ yPos
);
146 /* Trim whitespace from the right */
149 if (IS_WHITESPACE(ptr
[length
-1].Char
.UnicodeChar
))
155 /* Copy only the characters, leave attributes alone */
156 for (xPos
= 0; xPos
< length
; xPos
++)
159 * Sometimes, applications can put NULL chars into the screen-buffer
160 * (this behaviour is allowed). Detect this and replace by a space.
162 *dstPos
++ = (ptr
[xPos
].Char
.UnicodeChar
? ptr
[xPos
].Char
.UnicodeChar
: L
' ');
165 /* Add newline characters if we are not in inline-text copy mode */
168 if (yPos
!= (selHeight
- 1))
170 wcscat(dstPos
, L
"\r\n");
176 DPRINT("Setting data <%S> to clipboard\n", data
);
180 SetClipboardData(CF_UNICODETEXT
, hData
);
184 CopyLines(PTEXTMODE_SCREEN_BUFFER Buffer
,
191 ULONG NumChars
, size
;
192 ULONG xPos
, yPos
, xBeg
, xEnd
;
194 DPRINT("CopyLines((%u, %u) ; (%u, %u))\n",
195 Begin
->X
, Begin
->Y
, End
->X
, End
->Y
);
197 /* Prevent against empty blocks... */
198 if (Begin
== NULL
|| End
== NULL
) return;
199 /* ... or malformed blocks */
200 if (Begin
->Y
> End
->Y
|| (Begin
->Y
== End
->Y
&& Begin
->X
> End
->X
)) return;
202 /* Compute the number of characters to copy */
203 if (End
->Y
== Begin
->Y
) // top == bottom
205 NumChars
= End
->X
- Begin
->X
+ 1;
207 else // if (End->Y > Begin->Y)
209 NumChars
= Buffer
->ScreenBufferSize
.X
- Begin
->X
;
211 if (End
->Y
>= Begin
->Y
+ 2)
213 NumChars
+= (End
->Y
- Begin
->Y
- 1) * Buffer
->ScreenBufferSize
.X
;
216 NumChars
+= End
->X
+ 1;
219 size
= (NumChars
+ 1) * sizeof(WCHAR
); /* Null-terminated */
221 /* Allocate some memory area to be given to the clipboard, so it will not be freed here */
222 hData
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, size
);
223 if (hData
== NULL
) return;
225 data
= GlobalLock(hData
);
232 DPRINT("Copying %d characters\n", NumChars
);
236 * We need to walk per-lines, and not just looping in the big screen-buffer
237 * array, because of the way things are stored inside it. The downside is
238 * that it makes the code more complicated.
240 for (yPos
= Begin
->Y
; (yPos
<= End
->Y
) && (NumChars
> 0); yPos
++)
242 xBeg
= (yPos
== Begin
->Y
? Begin
->X
: 0);
243 xEnd
= (yPos
== End
->Y
? End
->X
: Buffer
->ScreenBufferSize
.X
- 1);
245 ptr
= ConioCoordToPointer(Buffer
, 0, yPos
);
247 /* Copy only the characters, leave attributes alone */
248 for (xPos
= xBeg
; (xPos
<= xEnd
) && (NumChars
-- > 0); xPos
++)
251 * Sometimes, applications can put NULL chars into the screen-buffer
252 * (this behaviour is allowed). Detect this and replace by a space.
254 *dstPos
++ = (ptr
[xPos
].Char
.UnicodeChar
? ptr
[xPos
].Char
.UnicodeChar
: L
' ');
258 DPRINT("Setting data <%S> to clipboard\n", data
);
262 SetClipboardData(CF_UNICODETEXT
, hData
);
267 GetSelectionBeginEnd(PCOORD Begin
, PCOORD End
,
268 PCOORD SelectionAnchor
,
269 PSMALL_RECT SmallRect
);
272 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
273 PGUI_CONSOLE_DATA GuiData
)
276 * This function supposes that the system clipboard was opened.
279 BOOL LineSelection
= GuiData
->LineSelection
;
281 DPRINT("Selection is (%d|%d) to (%d|%d) in %s mode\n",
282 GuiData
->Selection
.srSelection
.Left
,
283 GuiData
->Selection
.srSelection
.Top
,
284 GuiData
->Selection
.srSelection
.Right
,
285 GuiData
->Selection
.srSelection
.Bottom
,
286 (LineSelection
? "line" : "block"));
290 CopyBlock(Buffer
, &GuiData
->Selection
.srSelection
);
296 GetSelectionBeginEnd(&Begin
, &End
,
297 &GuiData
->Selection
.dwSelectionAnchor
,
298 &GuiData
->Selection
.srSelection
);
300 CopyLines(Buffer
, &Begin
, &End
);
305 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
306 PGUI_CONSOLE_DATA GuiData
)
309 * This function supposes that the system clipboard was opened.
312 PCONSOLE Console
= Buffer
->Header
.Console
;
318 USHORT VkKey
; // MAKEWORD(low = vkey_code, high = shift_state);
321 hData
= GetClipboardData(CF_UNICODETEXT
);
322 if (hData
== NULL
) return;
324 str
= GlobalLock(hData
);
325 if (str
== NULL
) return;
327 DPRINT("Got data <%S> from clipboard\n", str
);
329 er
.EventType
= KEY_EVENT
;
330 er
.Event
.KeyEvent
.wRepeatCount
= 1;
333 /* \r or \n characters. Go to the line only if we get "\r\n" sequence. */
334 if (CurChar
== L
'\r' && *str
== L
'\n')
341 /* Get the key code (+ shift state) corresponding to the character */
342 VkKey
= VkKeyScanW(CurChar
);
345 DPRINT1("VkKeyScanW failed - Should simulate the key...\n");
349 /* Pressing some control keys */
351 /* Pressing the character key, with the control keys maintained pressed */
352 er
.Event
.KeyEvent
.bKeyDown
= TRUE
;
353 er
.Event
.KeyEvent
.wVirtualKeyCode
= LOBYTE(VkKey
);
354 er
.Event
.KeyEvent
.wVirtualScanCode
= MapVirtualKeyW(LOBYTE(VkKey
), MAPVK_VK_TO_CHAR
);
355 er
.Event
.KeyEvent
.uChar
.UnicodeChar
= CurChar
;
356 er
.Event
.KeyEvent
.dwControlKeyState
= 0;
357 if (HIBYTE(VkKey
) & 1)
358 er
.Event
.KeyEvent
.dwControlKeyState
|= SHIFT_PRESSED
;
359 if (HIBYTE(VkKey
) & 2)
360 er
.Event
.KeyEvent
.dwControlKeyState
|= LEFT_CTRL_PRESSED
; // RIGHT_CTRL_PRESSED;
361 if (HIBYTE(VkKey
) & 4)
362 er
.Event
.KeyEvent
.dwControlKeyState
|= LEFT_ALT_PRESSED
; // RIGHT_ALT_PRESSED;
364 ConioProcessInputEvent(Console
, &er
);
366 /* Up all the character and control keys */
367 er
.Event
.KeyEvent
.bKeyDown
= FALSE
;
368 ConioProcessInputEvent(Console
, &er
);
375 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
376 PGUI_CONSOLE_DATA GuiData
,
380 PCONSOLE Console
= Buffer
->Header
.Console
;
381 // ASSERT(Console == GuiData->Console);
383 ULONG TopLine
, BottomLine
, LeftChar
, RightChar
;
384 ULONG Line
, Char
, Start
;
387 WORD LastAttribute
, Attribute
;
388 ULONG CursorX
, CursorY
, CursorHeight
;
389 HBRUSH CursorBrush
, OldBrush
;
390 HFONT OldFont
, NewFont
;
391 BOOLEAN IsUnderscore
;
393 if (Buffer
->Buffer
== NULL
) return;
395 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_RUNNING
, TRUE
)) return;
397 rcFramebuffer
->left
= Buffer
->ViewOrigin
.X
* GuiData
->CharWidth
+ rcView
->left
;
398 rcFramebuffer
->top
= Buffer
->ViewOrigin
.Y
* GuiData
->CharHeight
+ rcView
->top
;
399 rcFramebuffer
->right
= Buffer
->ViewOrigin
.X
* GuiData
->CharWidth
+ rcView
->right
;
400 rcFramebuffer
->bottom
= Buffer
->ViewOrigin
.Y
* GuiData
->CharHeight
+ rcView
->bottom
;
402 LeftChar
= rcFramebuffer
->left
/ GuiData
->CharWidth
;
403 TopLine
= rcFramebuffer
->top
/ GuiData
->CharHeight
;
404 RightChar
= rcFramebuffer
->right
/ GuiData
->CharWidth
;
405 BottomLine
= rcFramebuffer
->bottom
/ GuiData
->CharHeight
;
407 if (RightChar
>= Buffer
->ScreenBufferSize
.X
) RightChar
= Buffer
->ScreenBufferSize
.X
- 1;
408 if (BottomLine
>= Buffer
->ScreenBufferSize
.Y
) BottomLine
= Buffer
->ScreenBufferSize
.Y
- 1;
410 LastAttribute
= ConioCoordToPointer(Buffer
, LeftChar
, TopLine
)->Attributes
;
412 SetTextColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, TextAttribFromAttrib(LastAttribute
)));
413 SetBkColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, BkgdAttribFromAttrib(LastAttribute
)));
415 // OldFont = ChangeFontAttributes(GuiData, /* {0}, */ GuiData->GuiInfo.FontWeight, FALSE, FALSE, FALSE);
416 IsUnderscore
= !!(LastAttribute
& COMMON_LVB_UNDERSCORE
);
417 NewFont
= ChangeFontAttributes(GuiData
, /* {0}, */ GuiData
->GuiInfo
.FontWeight
,
423 DPRINT1("ChangeFontAttributes failed, use the original font\n");
424 NewFont
= GuiData
->Font
;
426 OldFont
= SelectObject(GuiData
->hMemDC
, NewFont
);
428 for (Line
= TopLine
; Line
<= BottomLine
; Line
++)
430 WCHAR LineBuffer
[80]; // Buffer containing a part or all the line to be displayed
431 From
= ConioCoordToPointer(Buffer
, LeftChar
, Line
); // Get the first code of the line
435 for (Char
= LeftChar
; Char
<= RightChar
; Char
++)
438 * We flush the buffer if the new attribute is different
439 * from the current one, or if the buffer is full.
441 if (From
->Attributes
!= LastAttribute
|| (Char
- Start
== sizeof(LineBuffer
) / sizeof(WCHAR
)))
443 TextOutW(GuiData
->hMemDC
,
444 Start
* GuiData
->CharWidth
,
445 Line
* GuiData
->CharHeight
,
450 Attribute
= From
->Attributes
;
451 if (Attribute
!= LastAttribute
)
453 LastAttribute
= Attribute
;
454 SetTextColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, TextAttribFromAttrib(LastAttribute
)));
455 SetBkColor(GuiData
->hMemDC
, PaletteRGBFromAttrib(Console
, BkgdAttribFromAttrib(LastAttribute
)));
457 /* Change underscore state if needed */
458 if (!!(LastAttribute
& COMMON_LVB_UNDERSCORE
) != IsUnderscore
)
460 IsUnderscore
= !!(LastAttribute
& COMMON_LVB_UNDERSCORE
);
462 /* Delete the font we used up to now */
463 // SelectObject(GuiData->hMemDC, OldFont);
464 if (NewFont
!= GuiData
->Font
) DeleteObject(NewFont
);
466 NewFont
= ChangeFontAttributes(GuiData
, /* {0}, */ GuiData
->GuiInfo
.FontWeight
,
472 DPRINT1("ChangeFontAttributes failed, use the original font\n");
473 NewFont
= GuiData
->Font
;
476 /* OldFont = */ SelectObject(GuiData
->hMemDC
, NewFont
);
481 *(To
++) = (From
++)->Char
.UnicodeChar
;
484 TextOutW(GuiData
->hMemDC
,
485 Start
* GuiData
->CharWidth
,
486 Line
* GuiData
->CharHeight
,
488 RightChar
- Start
+ 1);
494 if (Buffer
->CursorInfo
.bVisible
&&
495 Buffer
->CursorBlinkOn
&&
496 !Buffer
->ForceCursorOff
)
498 CursorX
= Buffer
->CursorPosition
.X
;
499 CursorY
= Buffer
->CursorPosition
.Y
;
500 if (LeftChar
<= CursorX
&& CursorX
<= RightChar
&&
501 TopLine
<= CursorY
&& CursorY
<= BottomLine
)
503 CursorHeight
= ConioEffectiveCursorSize(Console
, GuiData
->CharHeight
);
505 Attribute
= ConioCoordToPointer(Buffer
, Buffer
->CursorPosition
.X
, Buffer
->CursorPosition
.Y
)->Attributes
;
506 if (Attribute
== DEFAULT_SCREEN_ATTRIB
) Attribute
= Buffer
->ScreenDefaultAttrib
;
508 CursorBrush
= CreateSolidBrush(PaletteRGBFromAttrib(Console
, TextAttribFromAttrib(Attribute
)));
509 OldBrush
= SelectObject(GuiData
->hMemDC
, CursorBrush
);
511 PatBlt(GuiData
->hMemDC
,
512 CursorX
* GuiData
->CharWidth
,
513 CursorY
* GuiData
->CharHeight
+ (GuiData
->CharHeight
- CursorHeight
),
518 SelectObject(GuiData
->hMemDC
, OldBrush
);
519 DeleteObject(CursorBrush
);
523 /* Restore the old font and delete the font we used up to now */
524 SelectObject(GuiData
->hMemDC
, OldFont
);
525 if (NewFont
!= GuiData
->Font
) DeleteObject(NewFont
);
527 LeaveCriticalSection(&Console
->Lock
);