Sync with trunk r63174.
[reactos.git] / win32ss / user / winsrv / consrv / frontends / gui / text.c
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 /* FUNCTIONS ******************************************************************/
22
23 COLORREF PaletteRGBFromAttrib(PCONSOLE Console, WORD Attribute)
24 {
25 HPALETTE hPalette = Console->ActiveBuffer->PaletteHandle;
26 PALETTEENTRY pe;
27
28 if (hPalette == NULL) return RGBFromAttrib(Console, Attribute);
29
30 GetPaletteEntries(hPalette, Attribute, 1, &pe);
31 return PALETTERGB(pe.peRed, pe.peGreen, pe.peBlue);
32 }
33
34 VOID
35 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
36 PGUI_CONSOLE_DATA GuiData)
37 {
38 /*
39 * This function supposes that the system clipboard was opened.
40 */
41
42 /*
43 * Pressing the Shift key while copying text, allows us to copy
44 * text without newline characters (inline-text copy mode).
45 */
46 BOOL InlineCopyMode = (GetKeyState(VK_SHIFT) & 0x8000);
47
48 HANDLE hData;
49 PCHAR_INFO ptr;
50 LPWSTR data, dstPos;
51 ULONG selWidth, selHeight;
52 ULONG xPos, yPos, size;
53
54 selWidth = GuiData->Selection.srSelection.Right - GuiData->Selection.srSelection.Left + 1;
55 selHeight = GuiData->Selection.srSelection.Bottom - GuiData->Selection.srSelection.Top + 1;
56 DPRINT("Selection is (%d|%d) to (%d|%d)\n",
57 GuiData->Selection.srSelection.Left,
58 GuiData->Selection.srSelection.Top,
59 GuiData->Selection.srSelection.Right,
60 GuiData->Selection.srSelection.Bottom);
61
62 #ifdef IS_WHITESPACE
63 #undef IS_WHITESPACE
64 #endif
65 #define IS_WHITESPACE(c) ((c) == L'\0' || (c) == L' ' || (c) == L'\t')
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 size += 1; /* Null-termination */
79 size *= sizeof(WCHAR);
80
81 /* Allocate some memory area to be given to the clipboard, so it will not be freed here */
82 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);
83 if (hData == NULL) return;
84
85 data = GlobalLock(hData);
86 if (data == NULL)
87 {
88 GlobalFree(hData);
89 return;
90 }
91
92 DPRINT("Copying %dx%d selection\n", selWidth, selHeight);
93 dstPos = data;
94
95 for (yPos = 0; yPos < selHeight; yPos++)
96 {
97 ULONG length = selWidth;
98
99 ptr = ConioCoordToPointer(Buffer,
100 GuiData->Selection.srSelection.Left,
101 GuiData->Selection.srSelection.Top + yPos);
102
103 /* Trim whitespace from the right */
104 while (length > 0)
105 {
106 if (IS_WHITESPACE(ptr[length-1].Char.UnicodeChar))
107 --length;
108 else
109 break;
110 }
111
112 /* Copy only the characters, leave attributes alone */
113 for (xPos = 0; xPos < length; xPos++)
114 {
115 /*
116 * Sometimes, applications can put NULL chars into the screen-buffer
117 * (this behaviour is allowed). Detect this and replace by a space.
118 */
119 dstPos[xPos] = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' ');
120 }
121 dstPos += length;
122
123 /* Add newline characters if we are not in inline-text copy mode */
124 if (!InlineCopyMode)
125 {
126 if (yPos != (selHeight - 1))
127 {
128 wcscat(data, L"\r\n");
129 dstPos += 2;
130 }
131 }
132 }
133
134 DPRINT("Setting data <%S> to clipboard\n", data);
135 GlobalUnlock(hData);
136
137 EmptyClipboard();
138 SetClipboardData(CF_UNICODETEXT, hData);
139 }
140
141 VOID
142 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
143 PGUI_CONSOLE_DATA GuiData)
144 {
145 /*
146 * This function supposes that the system clipboard was opened.
147 */
148
149 PCONSOLE Console = Buffer->Header.Console;
150
151 HANDLE hData;
152 LPWSTR str;
153 WCHAR CurChar = 0;
154
155 USHORT VkKey; // MAKEWORD(low = vkey_code, high = shift_state);
156 INPUT_RECORD er;
157
158 hData = GetClipboardData(CF_UNICODETEXT);
159 if (hData == NULL) return;
160
161 str = GlobalLock(hData);
162 if (str == NULL) return;
163
164 DPRINT("Got data <%S> from clipboard\n", str);
165
166 er.EventType = KEY_EVENT;
167 er.Event.KeyEvent.wRepeatCount = 1;
168 while (*str)
169 {
170 /* \r or \n characters. Go to the line only if we get "\r\n" sequence. */
171 if (CurChar == L'\r' && *str == L'\n')
172 {
173 str++;
174 continue;
175 }
176 CurChar = *str++;
177
178 /* Get the key code (+ shift state) corresponding to the character */
179 VkKey = VkKeyScanW(CurChar);
180 if (VkKey == 0xFFFF)
181 {
182 DPRINT1("VkKeyScanW failed - Should simulate the key...\n");
183 continue;
184 }
185
186 /* Pressing some control keys */
187
188 /* Pressing the character key, with the control keys maintained pressed */
189 er.Event.KeyEvent.bKeyDown = TRUE;
190 er.Event.KeyEvent.wVirtualKeyCode = LOBYTE(VkKey);
191 er.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(LOBYTE(VkKey), MAPVK_VK_TO_CHAR);
192 er.Event.KeyEvent.uChar.UnicodeChar = CurChar;
193 er.Event.KeyEvent.dwControlKeyState = 0;
194 if (HIBYTE(VkKey) & 1)
195 er.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
196 if (HIBYTE(VkKey) & 2)
197 er.Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; // RIGHT_CTRL_PRESSED;
198 if (HIBYTE(VkKey) & 4)
199 er.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; // RIGHT_ALT_PRESSED;
200
201 ConioProcessInputEvent(Console, &er);
202
203 /* Up all the character and control keys */
204 er.Event.KeyEvent.bKeyDown = FALSE;
205 ConioProcessInputEvent(Console, &er);
206 }
207
208 GlobalUnlock(hData);
209 }
210
211 VOID
212 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
213 PGUI_CONSOLE_DATA GuiData,
214 PRECT rcView,
215 PRECT rcFramebuffer)
216 {
217 PCONSOLE Console = Buffer->Header.Console;
218 // ASSERT(Console == GuiData->Console);
219
220 ULONG TopLine, BottomLine, LeftChar, RightChar;
221 ULONG Line, Char, Start;
222 PCHAR_INFO From;
223 PWCHAR To;
224 WORD LastAttribute, Attribute;
225 ULONG CursorX, CursorY, CursorHeight;
226 HBRUSH CursorBrush, OldBrush;
227 HFONT OldFont;
228
229 if (Buffer->Buffer == NULL) return;
230
231 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
232
233 rcFramebuffer->left = Buffer->ViewOrigin.X * GuiData->CharWidth + rcView->left;
234 rcFramebuffer->top = Buffer->ViewOrigin.Y * GuiData->CharHeight + rcView->top;
235 rcFramebuffer->right = Buffer->ViewOrigin.X * GuiData->CharWidth + rcView->right;
236 rcFramebuffer->bottom = Buffer->ViewOrigin.Y * GuiData->CharHeight + rcView->bottom;
237
238 LeftChar = rcFramebuffer->left / GuiData->CharWidth;
239 TopLine = rcFramebuffer->top / GuiData->CharHeight;
240 RightChar = rcFramebuffer->right / GuiData->CharWidth;
241 BottomLine = rcFramebuffer->bottom / GuiData->CharHeight;
242
243 if (RightChar >= Buffer->ScreenBufferSize.X) RightChar = Buffer->ScreenBufferSize.X - 1;
244 if (BottomLine >= Buffer->ScreenBufferSize.Y) BottomLine = Buffer->ScreenBufferSize.Y - 1;
245
246 LastAttribute = ConioCoordToPointer(Buffer, LeftChar, TopLine)->Attributes;
247
248 SetTextColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute)));
249 SetBkColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute)));
250
251 OldFont = SelectObject(GuiData->hMemDC, GuiData->Font);
252
253 for (Line = TopLine; Line <= BottomLine; Line++)
254 {
255 WCHAR LineBuffer[80]; // Buffer containing a part or all the line to be displayed
256 From = ConioCoordToPointer(Buffer, LeftChar, Line); // Get the first code of the line
257 Start = LeftChar;
258 To = LineBuffer;
259
260 for (Char = LeftChar; Char <= RightChar; Char++)
261 {
262 /*
263 * We flush the buffer if the new attribute is different
264 * from the current one, or if the buffer is full.
265 */
266 if (From->Attributes != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR)))
267 {
268 TextOutW(GuiData->hMemDC,
269 Start * GuiData->CharWidth,
270 Line * GuiData->CharHeight,
271 LineBuffer,
272 Char - Start);
273 Start = Char;
274 To = LineBuffer;
275 Attribute = From->Attributes;
276 if (Attribute != LastAttribute)
277 {
278 SetTextColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, TextAttribFromAttrib(Attribute)));
279 SetBkColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, BkgdAttribFromAttrib(Attribute)));
280 LastAttribute = Attribute;
281 }
282 }
283
284 *(To++) = (From++)->Char.UnicodeChar;
285 }
286
287 TextOutW(GuiData->hMemDC,
288 Start * GuiData->CharWidth,
289 Line * GuiData->CharHeight,
290 LineBuffer,
291 RightChar - Start + 1);
292 }
293
294 /*
295 * Draw the caret
296 */
297 if (Buffer->CursorInfo.bVisible &&
298 Buffer->CursorBlinkOn &&
299 !Buffer->ForceCursorOff)
300 {
301 CursorX = Buffer->CursorPosition.X;
302 CursorY = Buffer->CursorPosition.Y;
303 if (LeftChar <= CursorX && CursorX <= RightChar &&
304 TopLine <= CursorY && CursorY <= BottomLine)
305 {
306 CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight);
307
308 Attribute = ConioCoordToPointer(Buffer, Buffer->CursorPosition.X, Buffer->CursorPosition.Y)->Attributes;
309 if (Attribute == DEFAULT_SCREEN_ATTRIB) Attribute = Buffer->ScreenDefaultAttrib;
310
311 CursorBrush = CreateSolidBrush(PaletteRGBFromAttrib(Console, TextAttribFromAttrib(Attribute)));
312 OldBrush = SelectObject(GuiData->hMemDC, CursorBrush);
313
314 PatBlt(GuiData->hMemDC,
315 CursorX * GuiData->CharWidth,
316 CursorY * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
317 GuiData->CharWidth,
318 CursorHeight,
319 PATCOPY);
320 SelectObject(GuiData->hMemDC, OldBrush);
321 DeleteObject(CursorBrush);
322 }
323 }
324
325 SelectObject(GuiData->hMemDC, OldFont);
326
327 LeaveCriticalSection(&Console->Lock);
328 }
329
330 /* EOF */