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