Synchronize with trunk revision 59636 (just before Alex's CreateProcess revamp).
[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 #include "include/conio.h"
16 #include "include/settings.h"
17 #include "guisettings.h"
18
19 #define NDEBUG
20 #include <debug.h>
21
22
23 /* FUNCTIONS ******************************************************************/
24
25 VOID
26 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer)
27 {
28 /*
29 * This function supposes that the system clipboard was opened.
30 */
31
32 PCONSOLE Console = Buffer->Header.Console;
33
34 /*
35 * Pressing the Shift key while copying text, allows us to copy
36 * text without newline characters (inline-text copy mode).
37 */
38 BOOL InlineCopyMode = (GetKeyState(VK_SHIFT) & 0x8000);
39
40 HANDLE hData;
41 PCHAR_INFO ptr;
42 LPWSTR data, dstPos;
43 ULONG selWidth, selHeight;
44 ULONG xPos, yPos, size;
45
46 selWidth = Console->Selection.srSelection.Right - Console->Selection.srSelection.Left + 1;
47 selHeight = Console->Selection.srSelection.Bottom - Console->Selection.srSelection.Top + 1;
48 DPRINT("Selection is (%d|%d) to (%d|%d)\n",
49 Console->Selection.srSelection.Left,
50 Console->Selection.srSelection.Top,
51 Console->Selection.srSelection.Right,
52 Console->Selection.srSelection.Bottom);
53
54 /* Basic size for one line... */
55 size = selWidth;
56 /* ... and for the other lines, add newline characters if needed. */
57 if (selHeight > 0)
58 {
59 /*
60 * If we are not in inline-text copy mode, each selected line must
61 * finish with \r\n . Otherwise, the lines will be just concatenated.
62 */
63 size += (selWidth + (!InlineCopyMode ? 2 : 0)) * (selHeight - 1);
64 }
65 size += 1; /* Null-termination */
66 size *= sizeof(WCHAR);
67
68 /* Allocate memory, it will be passed to the system and may not be freed here */
69 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);
70 if (hData == NULL) return;
71
72 data = GlobalLock(hData);
73 if (data == NULL) return;
74
75 DPRINT("Copying %dx%d selection\n", selWidth, selHeight);
76 dstPos = data;
77
78 for (yPos = 0; yPos < selHeight; yPos++)
79 {
80 ptr = ConioCoordToPointer(Buffer,
81 Console->Selection.srSelection.Left,
82 yPos + Console->Selection.srSelection.Top);
83 /* Copy only the characters, leave attributes alone */
84 for (xPos = 0; xPos < selWidth; xPos++)
85 {
86 /*
87 * Sometimes, applications can put NULL chars into the screen-buffer
88 * (this behaviour is allowed). Detect this and replace by a space.
89 * FIXME - HACK: Improve the way we're doing that (i.e., put spaces
90 * instead of NULLs (or even, nothing) only if it exists a non-null
91 * char *after* those NULLs, before the end-of-line of the selection.
92 * Do the same concerning spaces -- i.e. trailing spaces --).
93 */
94 dstPos[xPos] = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' ');
95 }
96 dstPos += selWidth;
97
98 /* Add newline characters if we are not in inline-text copy mode */
99 if (!InlineCopyMode)
100 {
101 if (yPos != (selHeight - 1))
102 {
103 wcscat(data, L"\r\n");
104 dstPos += 2;
105 }
106 }
107 }
108
109 DPRINT("Setting data <%S> to clipboard\n", data);
110 GlobalUnlock(hData);
111
112 EmptyClipboard();
113 SetClipboardData(CF_UNICODETEXT, hData);
114 }
115
116 VOID
117 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer)
118 {
119 /*
120 * This function supposes that the system clipboard was opened.
121 */
122
123 PCONSOLE Console = Buffer->Header.Console;
124
125 HANDLE hData;
126 LPWSTR str;
127 WCHAR CurChar = 0;
128
129 SHORT VkKey; // MAKEWORD(low = vkey_code, high = shift_state);
130 INPUT_RECORD er;
131
132 hData = GetClipboardData(CF_UNICODETEXT);
133 if (hData == NULL) return;
134
135 str = GlobalLock(hData);
136 if (str == NULL) return;
137
138 DPRINT("Got data <%S> from clipboard\n", str);
139
140 er.EventType = KEY_EVENT;
141 er.Event.KeyEvent.wRepeatCount = 1;
142 while (*str)
143 {
144 /* \r or \n characters. Go to the line only if we get "\r\n" sequence. */
145 if (CurChar == L'\r' && *str == L'\n')
146 {
147 str++;
148 continue;
149 }
150 CurChar = *str++;
151
152 /* Get the key code (+ shift state) corresponding to the character */
153 VkKey = VkKeyScanW(CurChar);
154 if (VkKey == 0xFFFF)
155 {
156 DPRINT1("VkKeyScanW failed - Should simulate the key...\n");
157 continue;
158 }
159
160 /* Pressing some control keys */
161
162 /* Pressing the character key, with the control keys maintained pressed */
163 er.Event.KeyEvent.bKeyDown = TRUE;
164 er.Event.KeyEvent.wVirtualKeyCode = LOBYTE(VkKey);
165 er.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(LOBYTE(VkKey), MAPVK_VK_TO_CHAR);
166 er.Event.KeyEvent.uChar.UnicodeChar = CurChar;
167 er.Event.KeyEvent.dwControlKeyState = 0;
168 if (HIBYTE(VkKey) & 1)
169 er.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
170 if (HIBYTE(VkKey) & 2)
171 er.Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; // RIGHT_CTRL_PRESSED;
172 if (HIBYTE(VkKey) & 4)
173 er.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; // RIGHT_ALT_PRESSED;
174
175 ConioProcessInputEvent(Console, &er);
176
177 /* Up all the character and control keys */
178 er.Event.KeyEvent.bKeyDown = FALSE;
179 ConioProcessInputEvent(Console, &er);
180 }
181
182 GlobalUnlock(hData);
183 }
184
185 VOID
186 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
187 PGUI_CONSOLE_DATA GuiData,
188 HDC hDC,
189 PRECT rc)
190 {
191 PCONSOLE Console = Buffer->Header.Console;
192 // ASSERT(Console == GuiData->Console);
193
194 ULONG TopLine, BottomLine, LeftChar, RightChar;
195 ULONG Line, Char, Start;
196 PCHAR_INFO From;
197 PWCHAR To;
198 WORD LastAttribute, Attribute;
199 ULONG CursorX, CursorY, CursorHeight;
200 HBRUSH CursorBrush, OldBrush;
201 HFONT OldFont;
202
203 if (Buffer->Buffer == NULL) return;
204
205 TopLine = rc->top / GuiData->CharHeight + Buffer->ViewOrigin.Y;
206 BottomLine = (rc->bottom + (GuiData->CharHeight - 1)) / GuiData->CharHeight - 1 + Buffer->ViewOrigin.Y;
207 LeftChar = rc->left / GuiData->CharWidth + Buffer->ViewOrigin.X;
208 RightChar = (rc->right + (GuiData->CharWidth - 1)) / GuiData->CharWidth - 1 + Buffer->ViewOrigin.X;
209
210 LastAttribute = ConioCoordToPointer(Buffer, LeftChar, TopLine)->Attributes;
211
212 SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute)));
213 SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute)));
214
215 if (BottomLine >= Buffer->ScreenBufferSize.Y) BottomLine = Buffer->ScreenBufferSize.Y - 1;
216 if (RightChar >= Buffer->ScreenBufferSize.X) RightChar = Buffer->ScreenBufferSize.X - 1;
217
218 OldFont = SelectObject(hDC, GuiData->Font);
219
220 for (Line = TopLine; Line <= BottomLine; Line++)
221 {
222 WCHAR LineBuffer[80]; // Buffer containing a part or all the line to be displayed
223 From = ConioCoordToPointer(Buffer, LeftChar, Line); // Get the first code of the line
224 Start = LeftChar;
225 To = LineBuffer;
226
227 for (Char = LeftChar; Char <= RightChar; Char++)
228 {
229 /*
230 * We flush the buffer if the new attribute is different
231 * from the current one, or if the buffer is full.
232 */
233 if (From->Attributes != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR)))
234 {
235 TextOutW(hDC,
236 (Start - Buffer->ViewOrigin.X) * GuiData->CharWidth ,
237 (Line - Buffer->ViewOrigin.Y) * GuiData->CharHeight,
238 LineBuffer,
239 Char - Start);
240 Start = Char;
241 To = LineBuffer;
242 Attribute = From->Attributes;
243 if (Attribute != LastAttribute)
244 {
245 SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(Attribute)));
246 SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(Attribute)));
247 LastAttribute = Attribute;
248 }
249 }
250
251 *(To++) = (From++)->Char.UnicodeChar;
252 }
253
254 TextOutW(hDC,
255 (Start - Buffer->ViewOrigin.X) * GuiData->CharWidth ,
256 (Line - Buffer->ViewOrigin.Y) * GuiData->CharHeight,
257 LineBuffer,
258 RightChar - Start + 1);
259 }
260
261 /*
262 * Draw the caret
263 */
264 if (Buffer->CursorInfo.bVisible &&
265 Buffer->CursorBlinkOn &&
266 !Buffer->ForceCursorOff)
267 {
268 CursorX = Buffer->CursorPosition.X;
269 CursorY = Buffer->CursorPosition.Y;
270 if (LeftChar <= CursorX && CursorX <= RightChar &&
271 TopLine <= CursorY && CursorY <= BottomLine)
272 {
273 CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight);
274 Attribute = ConioCoordToPointer(Buffer, Buffer->CursorPosition.X, Buffer->CursorPosition.Y)->Attributes;
275
276 if (Attribute != DEFAULT_SCREEN_ATTRIB)
277 {
278 CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, Attribute));
279 }
280 else
281 {
282 CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, Buffer->ScreenDefaultAttrib));
283 }
284
285 OldBrush = SelectObject(hDC, CursorBrush);
286 PatBlt(hDC,
287 (CursorX - Buffer->ViewOrigin.X) * GuiData->CharWidth,
288 (CursorY - Buffer->ViewOrigin.Y) * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
289 GuiData->CharWidth,
290 CursorHeight,
291 PATCOPY);
292 SelectObject(hDC, OldBrush);
293 DeleteObject(CursorBrush);
294 }
295 }
296
297 SelectObject(hDC, OldFont);
298 }
299
300 /* EOF */