3f8ec1f544d81992f98ee86d840b0054c22b2461
[reactos.git] / win32ss / user / consrv / frontends / gui / guiterm.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/frontends/gui/guiterm.c
5 * PURPOSE: GUI Terminal Front-End
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/console.h"
17 #include "include/settings.h"
18 #include "guiterm.h"
19 #include "guisettings.h"
20 #include "resource.h"
21
22 #include <windowsx.h>
23
24 #define NDEBUG
25 #include <debug.h>
26
27 /* GUI Console Window Class name */
28 #define GUI_CONSOLE_WINDOW_CLASS L"ConsoleWindowClass"
29
30 #ifndef WM_APP
31 #define WM_APP 0x8000
32 #endif
33 #define PM_CREATE_CONSOLE (WM_APP + 1)
34 #define PM_DESTROY_CONSOLE (WM_APP + 2)
35 #define PM_CONSOLE_BEEP (WM_APP + 3)
36 #define PM_CONSOLE_SET_TITLE (WM_APP + 4)
37
38
39 /* Not defined in any header file */
40 // extern VOID WINAPI PrivateCsrssManualGuiCheck(LONG Check);
41 // From win32ss/user/win32csr/dllmain.c
42 VOID
43 WINAPI
44 PrivateCsrssManualGuiCheck(LONG Check)
45 {
46 NtUserCallOneParam(Check, ONEPARAM_ROUTINE_CSRSS_GUICHECK);
47 }
48
49 /* GLOBALS ********************************************************************/
50
51 /**************************************************************\
52 \** Define the Console Leader Process for the console window **/
53 #define GWLP_CONSOLEWND_ALLOC (2 * sizeof(LONG_PTR))
54 #define GWLP_CONSOLE_LEADER_PID 0
55 #define GWLP_CONSOLE_LEADER_TID 4
56
57 #define SetConsoleWndConsoleLeaderCID(GuiData) \
58 do { \
59 PCONSOLE_PROCESS_DATA ProcessData; \
60 CLIENT_ID ConsoleLeaderCID; \
61 ProcessData = CONTAINING_RECORD((GuiData)->Console->ProcessList.Blink, \
62 CONSOLE_PROCESS_DATA, \
63 ConsoleLink); \
64 ConsoleLeaderCID = ProcessData->Process->ClientId; \
65 SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_PID, (LONG_PTR)(ConsoleLeaderCID.UniqueProcess)); \
66 SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_TID, (LONG_PTR)(ConsoleLeaderCID.UniqueThread )); \
67 } while (0)
68 /**************************************************************/
69
70 static BOOL ConsInitialized = FALSE;
71 static HICON ghDefaultIcon = NULL;
72 static HICON ghDefaultIconSm = NULL;
73 static HCURSOR ghDefaultCursor = NULL;
74 static HWND NotifyWnd = NULL;
75
76 typedef struct _GUICONSOLE_MENUITEM
77 {
78 UINT uID;
79 const struct _GUICONSOLE_MENUITEM *SubMenu;
80 WORD wCmdID;
81 } GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
82
83 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
84 {
85 { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK },
86 { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY },
87 { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE },
88 { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL },
89 { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL },
90 { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND },
91
92 { 0, NULL, 0 } /* End of list */
93 };
94
95 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
96 {
97 { IDS_EDIT, GuiConsoleEditMenuItems, 0 },
98 { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS },
99 { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES },
100
101 { 0, NULL, 0 } /* End of list */
102 };
103
104 /*
105 * Default 16-color palette for foreground and background
106 * (corresponding flags in comments).
107 */
108 const COLORREF s_Colors[16] =
109 {
110 RGB(0, 0, 0), // (Black)
111 RGB(0, 0, 128), // BLUE
112 RGB(0, 128, 0), // GREEN
113 RGB(0, 128, 128), // BLUE | GREEN
114 RGB(128, 0, 0), // RED
115 RGB(128, 0, 128), // BLUE | RED
116 RGB(128, 128, 0), // GREEN | RED
117 RGB(192, 192, 192), // BLUE | GREEN | RED
118
119 RGB(128, 128, 128), // (Grey) INTENSITY
120 RGB(0, 0, 255), // BLUE | INTENSITY
121 RGB(0, 255, 0), // GREEN | INTENSITY
122 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
123 RGB(255, 0, 0), // RED | INTENSITY
124 RGB(255, 0, 255), // BLUE | RED | INTENSITY
125 RGB(255, 255, 0), // GREEN | RED | INTENSITY
126 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
127 };
128
129 /* FUNCTIONS ******************************************************************/
130
131 static VOID
132 GuiConsoleAppendMenuItems(HMENU hMenu,
133 const GUICONSOLE_MENUITEM *Items)
134 {
135 UINT i = 0;
136 WCHAR szMenuString[255];
137 HMENU hSubMenu;
138
139 do
140 {
141 if (Items[i].uID != (UINT)-1)
142 {
143 if (LoadStringW(ConSrvDllInstance,
144 Items[i].uID,
145 szMenuString,
146 sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
147 {
148 if (Items[i].SubMenu != NULL)
149 {
150 hSubMenu = CreatePopupMenu();
151 if (hSubMenu != NULL)
152 {
153 GuiConsoleAppendMenuItems(hSubMenu,
154 Items[i].SubMenu);
155
156 if (!AppendMenuW(hMenu,
157 MF_STRING | MF_POPUP,
158 (UINT_PTR)hSubMenu,
159 szMenuString))
160 {
161 DestroyMenu(hSubMenu);
162 }
163 }
164 }
165 else
166 {
167 AppendMenuW(hMenu,
168 MF_STRING,
169 Items[i].wCmdID,
170 szMenuString);
171 }
172 }
173 }
174 else
175 {
176 AppendMenuW(hMenu,
177 MF_SEPARATOR,
178 0,
179 NULL);
180 }
181 i++;
182 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
183 }
184
185 static VOID
186 GuiConsoleCreateSysMenu(HWND hWnd)
187 {
188 HMENU hMenu;
189 hMenu = GetSystemMenu(hWnd, FALSE);
190 if (hMenu != NULL)
191 {
192 GuiConsoleAppendMenuItems(hMenu, GuiConsoleMainMenuItems);
193 DrawMenuBar(hWnd);
194 }
195 }
196
197
198 static VOID
199 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData);
200 static VOID
201 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData);
202 static VOID
203 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord);
204 static VOID WINAPI
205 GuiDrawRegion(PCONSOLE Console, SMALL_RECT* Region);
206 static VOID
207 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData);
208
209
210 static LRESULT
211 GuiConsoleHandleSysMenuCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
212 {
213 LRESULT Ret = TRUE;
214 PCONSOLE Console = GuiData->Console;
215
216 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
217 {
218 Ret = FALSE;
219 goto Quit;
220 }
221
222 switch (wParam)
223 {
224 case ID_SYSTEM_EDIT_MARK:
225 {
226 LPWSTR WindowTitle = NULL;
227 SIZE_T Length = 0;
228
229 Console->dwSelectionCursor.X = 0;
230 Console->dwSelectionCursor.Y = 0;
231 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
232 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS;
233 GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
234
235 Length = Console->Title.Length + sizeof(L"Mark - ")/sizeof(WCHAR) + 1;
236 WindowTitle = RtlAllocateHeap(ConSrvHeap, 0, Length * sizeof(WCHAR));
237 wcscpy(WindowTitle, L"Mark - ");
238 wcscat(WindowTitle, Console->Title.Buffer);
239 SetWindowText(GuiData->hWindow, WindowTitle);
240 RtlFreeHeap(ConSrvHeap, 0, WindowTitle);
241
242 break;
243 }
244
245 case ID_SYSTEM_EDIT_COPY:
246 GuiConsoleCopy(GuiData);
247 break;
248
249 case ID_SYSTEM_EDIT_PASTE:
250 GuiConsolePaste(GuiData);
251 break;
252
253 case ID_SYSTEM_EDIT_SELECTALL:
254 {
255 Console->Selection.dwSelectionAnchor.X = 0;
256 Console->Selection.dwSelectionAnchor.Y = 0;
257 Console->dwSelectionCursor.X = Console->ConsoleSize.X - 1;
258 Console->dwSelectionCursor.Y = Console->ConsoleSize.Y - 1;
259 GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
260 break;
261 }
262
263 case ID_SYSTEM_EDIT_SCROLL:
264 DPRINT1("Scrolling is not handled yet\n");
265 break;
266
267 case ID_SYSTEM_EDIT_FIND:
268 DPRINT1("Finding is not handled yet\n");
269 break;
270
271 case ID_SYSTEM_DEFAULTS:
272 GuiConsoleShowConsoleProperties(GuiData, TRUE);
273 break;
274
275 case ID_SYSTEM_PROPERTIES:
276 GuiConsoleShowConsoleProperties(GuiData, FALSE);
277 break;
278
279 default:
280 Ret = FALSE;
281 break;
282 }
283
284 LeaveCriticalSection(&Console->Lock);
285
286 Quit:
287 if (!Ret)
288 Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
289
290 return Ret;
291 }
292
293 static PGUI_CONSOLE_DATA
294 GuiGetGuiData(HWND hWnd)
295 {
296 /* This function ensures that the console pointer is not NULL */
297 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
298 return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
299 }
300
301 VOID
302 GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData)
303 {
304 /* Move the window if needed (not positioned by the system) */
305 if (!GuiData->GuiInfo.AutoPosition)
306 {
307 SetWindowPos(GuiData->hWindow,
308 NULL,
309 GuiData->GuiInfo.WindowOrigin.x,
310 GuiData->GuiInfo.WindowOrigin.y,
311 0,
312 0,
313 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
314 }
315 }
316
317 static VOID
318 GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData)
319 {
320 PCONSOLE Console = GuiData->Console;
321 SCROLLINFO sInfo;
322
323 DWORD Width = Console->ConsoleSize.X * GuiData->CharWidth +
324 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
325 DWORD Height = Console->ConsoleSize.Y * GuiData->CharHeight +
326 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
327
328 /* Set scrollbar sizes */
329 sInfo.cbSize = sizeof(SCROLLINFO);
330 sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
331 sInfo.nMin = 0;
332 if (Console->ActiveBuffer->ScreenBufferSize.Y > Console->ConsoleSize.Y)
333 {
334 sInfo.nMax = Console->ActiveBuffer->ScreenBufferSize.Y - 1;
335 sInfo.nPage = Console->ConsoleSize.Y;
336 sInfo.nPos = Console->ActiveBuffer->ShowY;
337 SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
338 Width += GetSystemMetrics(SM_CXVSCROLL);
339 ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
340 }
341 else
342 {
343 ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
344 }
345
346 if (Console->ActiveBuffer->ScreenBufferSize.X > Console->ConsoleSize.X)
347 {
348 sInfo.nMax = Console->ActiveBuffer->ScreenBufferSize.X - 1;
349 sInfo.nPage = Console->ConsoleSize.X;
350 sInfo.nPos = Console->ActiveBuffer->ShowX;
351 SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
352 Height += GetSystemMetrics(SM_CYHSCROLL);
353 ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
354 }
355 else
356 {
357 ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
358 }
359
360 /* Resize the window */
361 SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
362 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
363 }
364
365 static BOOL
366 GuiConsoleHandleNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
367 {
368 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
369 PCONSOLE Console;
370 HDC Dc;
371 HFONT OldFont;
372 TEXTMETRICW Metrics;
373 SIZE CharSize;
374
375 DPRINT("GuiConsoleHandleNcCreate\n");
376
377 if (NULL == GuiData)
378 {
379 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
380 return FALSE;
381 }
382
383 Console = GuiData->Console;
384
385 GuiData->hWindow = hWnd;
386
387 GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize),
388 0, // HIWORD(GuiData->GuiInfo.FontSize),
389 0,
390 TA_BASELINE,
391 GuiData->GuiInfo.FontWeight,
392 FALSE,
393 FALSE,
394 FALSE,
395 OEM_CHARSET,
396 OUT_DEFAULT_PRECIS,
397 CLIP_DEFAULT_PRECIS,
398 NONANTIALIASED_QUALITY,
399 FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */,
400 GuiData->GuiInfo.FaceName);
401
402 if (NULL == GuiData->Font)
403 {
404 DPRINT1("GuiConsoleNcCreate: CreateFont failed\n");
405 GuiData->hWindow = NULL;
406 SetEvent(GuiData->hGuiInitEvent);
407 return FALSE;
408 }
409 Dc = GetDC(GuiData->hWindow);
410 if (NULL == Dc)
411 {
412 DPRINT1("GuiConsoleNcCreate: GetDC failed\n");
413 DeleteObject(GuiData->Font);
414 GuiData->hWindow = NULL;
415 SetEvent(GuiData->hGuiInitEvent);
416 return FALSE;
417 }
418 OldFont = SelectObject(Dc, GuiData->Font);
419 if (NULL == OldFont)
420 {
421 DPRINT1("GuiConsoleNcCreate: SelectObject failed\n");
422 ReleaseDC(GuiData->hWindow, Dc);
423 DeleteObject(GuiData->Font);
424 GuiData->hWindow = NULL;
425 SetEvent(GuiData->hGuiInitEvent);
426 return FALSE;
427 }
428 if (!GetTextMetricsW(Dc, &Metrics))
429 {
430 DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n");
431 SelectObject(Dc, OldFont);
432 ReleaseDC(GuiData->hWindow, Dc);
433 DeleteObject(GuiData->Font);
434 GuiData->hWindow = NULL;
435 SetEvent(GuiData->hGuiInitEvent);
436 return FALSE;
437 }
438 GuiData->CharWidth = Metrics.tmMaxCharWidth;
439 GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
440
441 /* Measure real char width more precisely if possible. */
442 if (GetTextExtentPoint32W(Dc, L"R", 1, &CharSize))
443 GuiData->CharWidth = CharSize.cx;
444
445 SelectObject(Dc, OldFont);
446
447 ReleaseDC(GuiData->hWindow, Dc);
448
449 // FIXME: Keep these instructions here ? ///////////////////////////////////
450 Console->ActiveBuffer->CursorBlinkOn = TRUE;
451 Console->ActiveBuffer->ForceCursorOff = FALSE;
452 ////////////////////////////////////////////////////////////////////////////
453
454 SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData);
455
456 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
457 GuiConsoleCreateSysMenu(GuiData->hWindow);
458
459 DPRINT("GuiConsoleHandleNcCreate - setting start event\n");
460 SetEvent(GuiData->hGuiInitEvent);
461
462 return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create);
463 }
464
465 static VOID
466 SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect)
467 {
468 PCONSOLE Console = GuiData->Console;
469 PCONSOLE_SCREEN_BUFFER Buffer = Console->ActiveBuffer;
470
471 Rect->left = (SmallRect->Left - Buffer->ShowX) * GuiData->CharWidth;
472 Rect->top = (SmallRect->Top - Buffer->ShowY) * GuiData->CharHeight;
473 Rect->right = (SmallRect->Right + 1 - Buffer->ShowX) * GuiData->CharWidth;
474 Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ShowY) * GuiData->CharHeight;
475 }
476
477 static VOID
478 GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord)
479 {
480 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
481 RECT oldRect, newRect;
482
483 SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection);
484
485 if (coord != NULL)
486 {
487 SMALL_RECT rc;
488 /* exchange left/top with right/bottom if required */
489 rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X);
490 rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y);
491 rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X);
492 rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y);
493
494 SmallRectToRect(GuiData, &newRect, &rc);
495
496 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
497 {
498 if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0)
499 {
500 HRGN rgn1, rgn2;
501
502 /* calculate the region that needs to be updated */
503 if ((rgn1 = CreateRectRgnIndirect(&oldRect)))
504 {
505 if ((rgn2 = CreateRectRgnIndirect(&newRect)))
506 {
507 if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR)
508 {
509 InvalidateRgn(GuiData->hWindow, rgn1, FALSE);
510 }
511 DeleteObject(rgn2);
512 }
513 DeleteObject(rgn1);
514 }
515 }
516 }
517 else
518 {
519 InvalidateRect(GuiData->hWindow, &newRect, FALSE);
520 }
521 Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY;
522 Console->Selection.srSelection = rc;
523 Console->dwSelectionCursor = *coord;
524 ConioPause(Console, PAUSED_FROM_SELECTION);
525 }
526 else
527 {
528 /* clear the selection */
529 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
530 {
531 InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
532 }
533 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
534 ConioUnpause(Console, PAUSED_FROM_SELECTION);
535 }
536 }
537
538 static VOID
539 GuiConsolePaint(PCONSOLE Console,
540 PGUI_CONSOLE_DATA GuiData,
541 HDC hDC,
542 PRECT rc)
543 {
544 PCONSOLE_SCREEN_BUFFER Buff;
545 ULONG TopLine, BottomLine, LeftChar, RightChar;
546 ULONG Line, Char, Start;
547 PBYTE From;
548 PWCHAR To;
549 BYTE LastAttribute, Attribute;
550 ULONG CursorX, CursorY, CursorHeight;
551 HBRUSH CursorBrush, OldBrush;
552 HFONT OldFont;
553
554 Buff = Console->ActiveBuffer;
555
556 TopLine = rc->top / GuiData->CharHeight + Buff->ShowY;
557 BottomLine = (rc->bottom + (GuiData->CharHeight - 1)) / GuiData->CharHeight - 1 + Buff->ShowY;
558 LeftChar = rc->left / GuiData->CharWidth + Buff->ShowX;
559 RightChar = (rc->right + (GuiData->CharWidth - 1)) / GuiData->CharWidth - 1 + Buff->ShowX;
560 LastAttribute = ConioCoordToPointer(Buff, LeftChar, TopLine)[1];
561
562 SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute)));
563 SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute)));
564
565 if (BottomLine >= Buff->ScreenBufferSize.Y) BottomLine = Buff->ScreenBufferSize.Y - 1;
566 if (RightChar >= Buff->ScreenBufferSize.X) RightChar = Buff->ScreenBufferSize.X - 1;
567
568 OldFont = SelectObject(hDC, GuiData->Font);
569
570 for (Line = TopLine; Line <= BottomLine; Line++)
571 {
572 WCHAR LineBuffer[80];
573 From = ConioCoordToPointer(Buff, LeftChar, Line);
574 Start = LeftChar;
575 To = LineBuffer;
576
577 for (Char = LeftChar; Char <= RightChar; Char++)
578 {
579 if (*(From + 1) != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR)))
580 {
581 TextOutW(hDC,
582 (Start - Buff->ShowX) * GuiData->CharWidth,
583 (Line - Buff->ShowY) * GuiData->CharHeight,
584 LineBuffer,
585 Char - Start);
586 Start = Char;
587 To = LineBuffer;
588 Attribute = *(From + 1);
589 if (Attribute != LastAttribute)
590 {
591 SetTextColor(hDC, RGBFromAttrib(Console, TextAttribFromAttrib(Attribute)));
592 SetBkColor(hDC, RGBFromAttrib(Console, BkgdAttribFromAttrib(Attribute)));
593 LastAttribute = Attribute;
594 }
595 }
596
597 MultiByteToWideChar(Console->OutputCodePage,
598 0,
599 (PCHAR)From,
600 1,
601 To,
602 1);
603 To++;
604 From += 2;
605 }
606
607 TextOutW(hDC,
608 (Start - Buff->ShowX) * GuiData->CharWidth,
609 (Line - Buff->ShowY) * GuiData->CharHeight,
610 LineBuffer,
611 RightChar - Start + 1);
612 }
613
614 if (Buff->CursorInfo.bVisible && Buff->CursorBlinkOn &&
615 !Buff->ForceCursorOff)
616 {
617 CursorX = Buff->CursorPosition.X;
618 CursorY = Buff->CursorPosition.Y;
619 if (LeftChar <= CursorX && CursorX <= RightChar &&
620 TopLine <= CursorY && CursorY <= BottomLine)
621 {
622 CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight);
623 From = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y) + 1;
624
625 if (*From != DEFAULT_SCREEN_ATTRIB)
626 {
627 CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, *From));
628 }
629 else
630 {
631 CursorBrush = CreateSolidBrush(RGBFromAttrib(Console, Buff->ScreenDefaultAttrib));
632 }
633
634 OldBrush = SelectObject(hDC, CursorBrush);
635 PatBlt(hDC,
636 (CursorX - Buff->ShowX) * GuiData->CharWidth,
637 (CursorY - Buff->ShowY) * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
638 GuiData->CharWidth,
639 CursorHeight,
640 PATCOPY);
641 SelectObject(hDC, OldBrush);
642 DeleteObject(CursorBrush);
643 }
644 }
645
646 SelectObject(hDC, OldFont);
647 }
648
649 static VOID
650 GuiConsoleHandlePaint(PGUI_CONSOLE_DATA GuiData)
651 {
652 BOOL Success = TRUE;
653 PCONSOLE Console = GuiData->Console;
654 HDC hDC;
655 PAINTSTRUCT ps;
656
657 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
658 {
659 Success = FALSE;
660 goto Quit;
661 }
662
663 if (Console->ActiveBuffer == NULL ||
664 Console->ActiveBuffer->Buffer == NULL)
665 {
666 goto Quit;
667 }
668
669 hDC = BeginPaint(GuiData->hWindow, &ps);
670 if (hDC != NULL &&
671 ps.rcPaint.left < ps.rcPaint.right &&
672 ps.rcPaint.top < ps.rcPaint.bottom)
673 {
674 EnterCriticalSection(&GuiData->Lock);
675
676 GuiConsolePaint(Console,
677 GuiData,
678 hDC,
679 &ps.rcPaint);
680
681 if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
682 {
683 RECT rc;
684 SmallRectToRect(GuiData, &rc, &Console->Selection.srSelection);
685
686 /* invert the selection */
687 if (IntersectRect(&rc,
688 &ps.rcPaint,
689 &rc))
690 {
691 PatBlt(hDC,
692 rc.left,
693 rc.top,
694 rc.right - rc.left,
695 rc.bottom - rc.top,
696 DSTINVERT);
697 }
698 }
699
700 LeaveCriticalSection(&GuiData->Lock);
701 }
702 EndPaint(GuiData->hWindow, &ps);
703
704 Quit:
705 if (Success)
706 LeaveCriticalSection(&Console->Lock);
707 else
708 DefWindowProcW(GuiData->hWindow, WM_PAINT, 0, 0);
709
710 return;
711 }
712
713 static VOID
714 GuiConsoleHandleKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
715 {
716 PCONSOLE Console = GuiData->Console;
717 MSG Message;
718
719 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
720
721 if ( (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
722 ((Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0) &&
723 (Console->ActiveBuffer) )
724 {
725 BOOL Interpreted = FALSE;
726
727 /* Selection with keyboard */
728 if (msg == WM_KEYDOWN)
729 {
730 BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000);
731
732 switch (wParam)
733 {
734 case VK_LEFT:
735 {
736 Interpreted = TRUE;
737 if (Console->dwSelectionCursor.X > 0)
738 Console->dwSelectionCursor.X--;
739
740 break;
741 }
742
743 case VK_RIGHT:
744 {
745 Interpreted = TRUE;
746 if (Console->dwSelectionCursor.X < Console->ActiveBuffer->ScreenBufferSize.X - 1)
747 Console->dwSelectionCursor.X++;
748
749 break;
750 }
751
752 case VK_UP:
753 {
754 Interpreted = TRUE;
755 if (Console->dwSelectionCursor.Y > 0)
756 Console->dwSelectionCursor.Y--;
757
758 break;
759 }
760
761 case VK_DOWN:
762 {
763 Interpreted = TRUE;
764 if (Console->dwSelectionCursor.Y < Console->ActiveBuffer->ScreenBufferSize.Y - 1)
765 Console->dwSelectionCursor.Y++;
766
767 break;
768 }
769
770 case VK_HOME:
771 {
772 Interpreted = TRUE;
773 Console->dwSelectionCursor.X = 0;
774 Console->dwSelectionCursor.Y = 0;
775 break;
776 }
777
778 case VK_END:
779 {
780 Interpreted = TRUE;
781 Console->dwSelectionCursor.Y = Console->ActiveBuffer->ScreenBufferSize.Y - 1;
782 break;
783 }
784
785 case VK_PRIOR:
786 {
787 Interpreted = TRUE;
788 Console->dwSelectionCursor.Y -= Console->ConsoleSize.Y;
789 if (Console->dwSelectionCursor.Y < 0)
790 Console->dwSelectionCursor.Y = 0;
791
792 break;
793 }
794
795 case VK_NEXT:
796 {
797 Interpreted = TRUE;
798 Console->dwSelectionCursor.Y += Console->ConsoleSize.Y;
799 if (Console->dwSelectionCursor.Y >= Console->ActiveBuffer->ScreenBufferSize.Y)
800 Console->dwSelectionCursor.Y = Console->ActiveBuffer->ScreenBufferSize.Y - 1;
801
802 break;
803 }
804
805 default:
806 break;
807 }
808
809 if (Interpreted)
810 {
811 if (!MajPressed)
812 Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
813
814 GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
815 }
816 }
817 }
818 else
819 {
820 Message.hwnd = GuiData->hWindow;
821 Message.message = msg;
822 Message.wParam = wParam;
823 Message.lParam = lParam;
824
825 if (msg == WM_KEYDOWN)
826 {
827 /* If we are in selection mode (with mouse), clear the selection */
828 GuiConsoleUpdateSelection(Console, NULL);
829 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
830 }
831
832 ConioProcessKey(Console, &Message);
833 }
834
835 LeaveCriticalSection(&Console->Lock);
836 }
837
838 static VOID
839 GuiInvalidateCell(PCONSOLE Console, UINT x, UINT y)
840 {
841 SMALL_RECT CellRect = { x, y, x, y };
842 GuiDrawRegion(Console, &CellRect);
843 }
844
845 static VOID
846 GuiConsoleHandleTimer(PGUI_CONSOLE_DATA GuiData)
847 {
848 PCONSOLE Console = GuiData->Console;
849 PCONSOLE_SCREEN_BUFFER Buff;
850
851 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
852
853 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
854
855 Buff = Console->ActiveBuffer;
856 GuiInvalidateCell(Console, Buff->CursorPosition.X, Buff->CursorPosition.Y);
857 Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
858
859 if ((GuiData->OldCursor.x != Buff->CursorPosition.X) || (GuiData->OldCursor.y != Buff->CursorPosition.Y))
860 {
861 SCROLLINFO xScroll;
862 int OldScrollX = -1, OldScrollY = -1;
863 int NewScrollX = -1, NewScrollY = -1;
864
865 xScroll.cbSize = sizeof(SCROLLINFO);
866 xScroll.fMask = SIF_POS;
867 // Capture the original position of the scroll bars and save them.
868 if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll))OldScrollX = xScroll.nPos;
869 if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll))OldScrollY = xScroll.nPos;
870
871 // If we successfully got the info for the horizontal scrollbar
872 if (OldScrollX >= 0)
873 {
874 if ((Buff->CursorPosition.X < Buff->ShowX)||(Buff->CursorPosition.X >= (Buff->ShowX + Console->ConsoleSize.X)))
875 {
876 // Handle the horizontal scroll bar
877 if (Buff->CursorPosition.X >= Console->ConsoleSize.X) NewScrollX = Buff->CursorPosition.X - Console->ConsoleSize.X + 1;
878 else NewScrollX = 0;
879 }
880 else
881 {
882 NewScrollX = OldScrollX;
883 }
884 }
885 // If we successfully got the info for the vertical scrollbar
886 if (OldScrollY >= 0)
887 {
888 if ((Buff->CursorPosition.Y < Buff->ShowY) || (Buff->CursorPosition.Y >= (Buff->ShowY + Console->ConsoleSize.Y)))
889 {
890 // Handle the vertical scroll bar
891 if (Buff->CursorPosition.Y >= Console->ConsoleSize.Y) NewScrollY = Buff->CursorPosition.Y - Console->ConsoleSize.Y + 1;
892 else NewScrollY = 0;
893 }
894 else
895 {
896 NewScrollY = OldScrollY;
897 }
898 }
899
900 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
901 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
902 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
903 // and their associated scrollbar is left alone.
904 if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
905 {
906 Buff->ShowX = NewScrollX;
907 Buff->ShowY = NewScrollY;
908 ScrollWindowEx(GuiData->hWindow,
909 (OldScrollX - NewScrollX) * GuiData->CharWidth,
910 (OldScrollY - NewScrollY) * GuiData->CharHeight,
911 NULL,
912 NULL,
913 NULL,
914 NULL,
915 SW_INVALIDATE);
916 if (NewScrollX >= 0)
917 {
918 xScroll.nPos = NewScrollX;
919 SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE);
920 }
921 if (NewScrollY >= 0)
922 {
923 xScroll.nPos = NewScrollY;
924 SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE);
925 }
926 UpdateWindow(GuiData->hWindow);
927 GuiData->OldCursor.x = Buff->CursorPosition.X;
928 GuiData->OldCursor.y = Buff->CursorPosition.Y;
929 }
930 }
931
932 LeaveCriticalSection(&Console->Lock);
933 }
934
935 static VOID
936 GuiConsoleHandleClose(PGUI_CONSOLE_DATA GuiData)
937 {
938 PCONSOLE Console = GuiData->Console;
939 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
940
941 /*
942 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
943 * We shouldn't wait here, though, since the console lock is entered.
944 * A copy of the thread list probably needs to be made.
945 */
946 ConSrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT);
947
948 LeaveCriticalSection(&Console->Lock);
949 }
950
951 static LRESULT
952 GuiConsoleHandleNcDestroy(HWND hWnd)
953 {
954 // PGUI_CONSOLE_DATA GuiData;
955
956 KillTimer(hWnd, CONGUI_UPDATE_TIMER);
957 GetSystemMenu(hWnd, TRUE);
958
959 /* Free the GuiData registration */
960 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
961 // GuiData->hWindow = NULL;
962
963 // return 0;
964 return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
965 }
966
967 static COORD
968 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
969 {
970 PCONSOLE Console = GuiData->Console;
971 PCONSOLE_SCREEN_BUFFER Buffer = Console->ActiveBuffer;
972 COORD Coord;
973
974 Coord.X = Buffer->ShowX + ((short)LOWORD(lParam) / (int)GuiData->CharWidth);
975 Coord.Y = Buffer->ShowY + ((short)HIWORD(lParam) / (int)GuiData->CharHeight);
976
977 /* Clip coordinate to ensure it's inside buffer */
978 if (Coord.X < 0)
979 Coord.X = 0;
980 else if (Coord.X >= Buffer->ScreenBufferSize.X)
981 Coord.X = Buffer->ScreenBufferSize.X - 1;
982
983 if (Coord.Y < 0)
984 Coord.Y = 0;
985 else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
986 Coord.Y = Buffer->ScreenBufferSize.Y - 1;
987
988 return Coord;
989 }
990
991 static LRESULT
992 GuiConsoleHandleMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
993 {
994 BOOL Err = FALSE;
995 PCONSOLE Console = GuiData->Console;
996
997 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
998 {
999 Err = TRUE;
1000 goto Quit;
1001 }
1002
1003 if ( (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
1004 (Console->QuickEdit) )
1005 {
1006 switch (msg)
1007 {
1008 case WM_LBUTTONDOWN:
1009 {
1010 LPWSTR WindowTitle = NULL;
1011 SIZE_T Length = 0;
1012
1013 Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam);
1014 SetCapture(GuiData->hWindow);
1015 Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1016 GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
1017
1018 Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1;
1019 WindowTitle = RtlAllocateHeap(ConSrvHeap, 0, Length * sizeof(WCHAR));
1020 wcscpy(WindowTitle, L"Selection - ");
1021 wcscat(WindowTitle, Console->Title.Buffer);
1022 SetWindowText(GuiData->hWindow, WindowTitle);
1023 RtlFreeHeap(ConSrvHeap, 0, WindowTitle);
1024
1025 break;
1026 }
1027
1028 case WM_LBUTTONUP:
1029 {
1030 COORD c;
1031
1032 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1033
1034 c = PointToCoord(GuiData, lParam);
1035 Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1036 GuiConsoleUpdateSelection(Console, &c);
1037 ReleaseCapture();
1038
1039 break;
1040 }
1041
1042 case WM_RBUTTONDOWN:
1043 {
1044 if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1045 {
1046 GuiConsolePaste(GuiData);
1047 }
1048 else
1049 {
1050 GuiConsoleCopy(GuiData);
1051
1052 /* Clear the selection */
1053 GuiConsoleUpdateSelection(Console, NULL);
1054 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
1055 }
1056
1057 break;
1058 }
1059
1060 case WM_MOUSEMOVE:
1061 {
1062 COORD c;
1063
1064 if (!(wParam & MK_LBUTTON)) break;
1065 if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1066
1067 c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */
1068 GuiConsoleUpdateSelection(Console, &c);
1069
1070 break;
1071 }
1072
1073 default:
1074 Err = TRUE;
1075 break;
1076 }
1077 }
1078 else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT)
1079 {
1080 INPUT_RECORD er;
1081 WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1082 DWORD dwButtonState = 0;
1083 DWORD dwControlKeyState = 0;
1084 DWORD dwEventFlags = 0;
1085
1086 switch (msg)
1087 {
1088 case WM_LBUTTONDOWN:
1089 SetCapture(GuiData->hWindow);
1090 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1091 dwEventFlags = 0;
1092 break;
1093
1094 case WM_MBUTTONDOWN:
1095 SetCapture(GuiData->hWindow);
1096 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1097 dwEventFlags = 0;
1098 break;
1099
1100 case WM_RBUTTONDOWN:
1101 SetCapture(GuiData->hWindow);
1102 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1103 dwEventFlags = 0;
1104 break;
1105
1106 case WM_LBUTTONUP:
1107 ReleaseCapture();
1108 dwButtonState = 0;
1109 dwEventFlags = 0;
1110 break;
1111
1112 case WM_MBUTTONUP:
1113 ReleaseCapture();
1114 dwButtonState = 0;
1115 dwEventFlags = 0;
1116 break;
1117
1118 case WM_RBUTTONUP:
1119 ReleaseCapture();
1120 dwButtonState = 0;
1121 dwEventFlags = 0;
1122 break;
1123
1124 case WM_LBUTTONDBLCLK:
1125 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1126 dwEventFlags = DOUBLE_CLICK;
1127 break;
1128
1129 case WM_MBUTTONDBLCLK:
1130 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1131 dwEventFlags = DOUBLE_CLICK;
1132 break;
1133
1134 case WM_RBUTTONDBLCLK:
1135 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1136 dwEventFlags = DOUBLE_CLICK;
1137 break;
1138
1139 case WM_MOUSEMOVE:
1140 dwButtonState = 0;
1141 dwEventFlags = MOUSE_MOVED;
1142 break;
1143
1144 case WM_MOUSEWHEEL:
1145 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1146 dwEventFlags = MOUSE_WHEELED;
1147 break;
1148
1149 case WM_MOUSEHWHEEL:
1150 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1151 dwEventFlags = MOUSE_HWHEELED;
1152 break;
1153
1154 default:
1155 Err = TRUE;
1156 break;
1157 }
1158
1159 if (!Err)
1160 {
1161 if (wKeyState & MK_LBUTTON)
1162 dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1163 if (wKeyState & MK_MBUTTON)
1164 dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1165 if (wKeyState & MK_RBUTTON)
1166 dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1167
1168 if (GetKeyState(VK_RMENU) & 0x8000)
1169 dwControlKeyState |= RIGHT_ALT_PRESSED;
1170 if (GetKeyState(VK_LMENU) & 0x8000)
1171 dwControlKeyState |= LEFT_ALT_PRESSED;
1172 if (GetKeyState(VK_RCONTROL) & 0x8000)
1173 dwControlKeyState |= RIGHT_CTRL_PRESSED;
1174 if (GetKeyState(VK_LCONTROL) & 0x8000)
1175 dwControlKeyState |= LEFT_CTRL_PRESSED;
1176 if (GetKeyState(VK_SHIFT) & 0x8000)
1177 dwControlKeyState |= SHIFT_PRESSED;
1178 if (GetKeyState(VK_NUMLOCK) & 0x0001)
1179 dwControlKeyState |= NUMLOCK_ON;
1180 if (GetKeyState(VK_SCROLL) & 0x0001)
1181 dwControlKeyState |= SCROLLLOCK_ON;
1182 if (GetKeyState(VK_CAPITAL) & 0x0001)
1183 dwControlKeyState |= CAPSLOCK_ON;
1184 /* See WM_CHAR MSDN documentation for instance */
1185 if (lParam & 0x01000000)
1186 dwControlKeyState |= ENHANCED_KEY;
1187
1188 er.EventType = MOUSE_EVENT;
1189 er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam);
1190 er.Event.MouseEvent.dwButtonState = dwButtonState;
1191 er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
1192 er.Event.MouseEvent.dwEventFlags = dwEventFlags;
1193
1194 ConioProcessInputEvent(Console, &er);
1195 }
1196 }
1197 else
1198 {
1199 Err = TRUE;
1200 }
1201
1202 LeaveCriticalSection(&Console->Lock);
1203
1204 Quit:
1205 if (Err)
1206 return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1207 else
1208 return 0;
1209 }
1210
1211 static VOID
1212 GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData)
1213 {
1214 PCONSOLE Console = GuiData->Console;
1215
1216 if (OpenClipboard(GuiData->hWindow) == TRUE)
1217 {
1218 HANDLE hData;
1219 PBYTE ptr;
1220 LPSTR data, dstPos;
1221 ULONG selWidth, selHeight;
1222 ULONG xPos, yPos, size;
1223
1224 selWidth = Console->Selection.srSelection.Right - Console->Selection.srSelection.Left + 1;
1225 selHeight = Console->Selection.srSelection.Bottom - Console->Selection.srSelection.Top + 1;
1226 DPRINT("Selection is (%d|%d) to (%d|%d)\n",
1227 Console->Selection.srSelection.Left,
1228 Console->Selection.srSelection.Top,
1229 Console->Selection.srSelection.Right,
1230 Console->Selection.srSelection.Bottom);
1231
1232 /* Basic size for one line and termination */
1233 size = selWidth + 1;
1234 if (selHeight > 0)
1235 {
1236 /* Multiple line selections have to get \r\n appended */
1237 size += ((selWidth + 2) * (selHeight - 1));
1238 }
1239
1240 /* Allocate memory, it will be passed to the system and may not be freed here */
1241 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);
1242 if (hData == NULL)
1243 {
1244 CloseClipboard();
1245 return;
1246 }
1247 data = GlobalLock(hData);
1248 if (data == NULL)
1249 {
1250 CloseClipboard();
1251 return;
1252 }
1253
1254 DPRINT("Copying %dx%d selection\n", selWidth, selHeight);
1255 dstPos = data;
1256
1257 for (yPos = 0; yPos < selHeight; yPos++)
1258 {
1259 ptr = ConioCoordToPointer(Console->ActiveBuffer,
1260 Console->Selection.srSelection.Left,
1261 yPos + Console->Selection.srSelection.Top);
1262 /* Copy only the characters, leave attributes alone */
1263 for (xPos = 0; xPos < selWidth; xPos++)
1264 {
1265 dstPos[xPos] = ptr[xPos * 2];
1266 }
1267 dstPos += selWidth;
1268 if (yPos != (selHeight - 1))
1269 {
1270 strcat(data, "\r\n");
1271 dstPos += 2;
1272 }
1273 }
1274
1275 DPRINT("Setting data <%s> to clipboard\n", data);
1276 GlobalUnlock(hData);
1277
1278 EmptyClipboard();
1279 SetClipboardData(CF_TEXT, hData);
1280 CloseClipboard();
1281 }
1282 }
1283
1284 static VOID
1285 GuiConsolePaste(PGUI_CONSOLE_DATA GuiData)
1286 {
1287 PCONSOLE Console = GuiData->Console;
1288
1289 if (OpenClipboard(GuiData->hWindow) == TRUE)
1290 {
1291 HANDLE hData;
1292 LPSTR str;
1293 size_t len;
1294
1295 hData = GetClipboardData(CF_TEXT);
1296 if (hData == NULL)
1297 {
1298 CloseClipboard();
1299 return;
1300 }
1301
1302 str = GlobalLock(hData);
1303 if (str == NULL)
1304 {
1305 CloseClipboard();
1306 return;
1307 }
1308 DPRINT("Got data <%s> from clipboard\n", str);
1309 len = strlen(str);
1310
1311 // TODO: Push the text into the input buffer.
1312 ConioWriteConsole(Console, Console->ActiveBuffer, str, len, TRUE);
1313
1314 GlobalUnlock(hData);
1315 CloseClipboard();
1316 }
1317 }
1318
1319 static VOID
1320 GuiConsoleGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
1321 {
1322 PCONSOLE Console = GuiData->Console;
1323 DWORD windx, windy;
1324
1325 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1326
1327 windx = CONGUI_MIN_WIDTH * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1328 windy = CONGUI_MIN_HEIGHT * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1329
1330 minMaxInfo->ptMinTrackSize.x = windx;
1331 minMaxInfo->ptMinTrackSize.y = windy;
1332
1333 windx = (Console->ActiveBuffer->ScreenBufferSize.X) * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1334 windy = (Console->ActiveBuffer->ScreenBufferSize.Y) * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1335
1336 if (Console->ConsoleSize.X < Console->ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1337 if (Console->ConsoleSize.Y < Console->ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1338
1339 minMaxInfo->ptMaxTrackSize.x = windx;
1340 minMaxInfo->ptMaxTrackSize.y = windy;
1341
1342 LeaveCriticalSection(&Console->Lock);
1343 }
1344
1345 static VOID
1346 GuiConsoleResize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
1347 {
1348 PCONSOLE Console = GuiData->Console;
1349
1350 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
1351
1352 if ((GuiData->WindowSizeLock == FALSE) &&
1353 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
1354 {
1355 PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer;
1356 DWORD windx, windy, charx, chary;
1357
1358 GuiData->WindowSizeLock = TRUE;
1359
1360 windx = LOWORD(lParam);
1361 windy = HIWORD(lParam);
1362
1363 // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
1364 if (Console->ConsoleSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1365 if (Console->ConsoleSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1366
1367 charx = windx / GuiData->CharWidth;
1368 chary = windy / GuiData->CharHeight;
1369
1370 // Character alignment (round size up or down)
1371 if ((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx;
1372 if ((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary;
1373
1374 // Compensate for added scroll bars in new window
1375 if (charx < Buff->ScreenBufferSize.X)windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
1376 if (chary < Buff->ScreenBufferSize.Y)windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
1377
1378 charx = windx / GuiData->CharWidth;
1379 chary = windy / GuiData->CharHeight;
1380
1381 // Character alignment (round size up or down)
1382 if ((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx;
1383 if ((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary;
1384
1385 // Resize window
1386 if ((charx != Console->ConsoleSize.X) || (chary != Console->ConsoleSize.Y))
1387 {
1388 Console->ConsoleSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
1389 Console->ConsoleSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
1390 }
1391
1392 GuiConsoleResizeWindow(GuiData);
1393
1394 // Adjust the start of the visible area if we are attempting to show nonexistent areas
1395 if ((Buff->ScreenBufferSize.X - Buff->ShowX) < Console->ConsoleSize.X) Buff->ShowX = Buff->ScreenBufferSize.X - Console->ConsoleSize.X;
1396 if ((Buff->ScreenBufferSize.Y - Buff->ShowY) < Console->ConsoleSize.Y) Buff->ShowY = Buff->ScreenBufferSize.Y - Console->ConsoleSize.Y;
1397 InvalidateRect(GuiData->hWindow, NULL, TRUE);
1398
1399 GuiData->WindowSizeLock = FALSE;
1400 }
1401
1402 LeaveCriticalSection(&Console->Lock);
1403 }
1404
1405 /*
1406 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
1407
1408 VOID
1409 FASTCALL
1410 GuiConsoleHandleScrollbarMenu(VOID)
1411 {
1412 HMENU hMenu;
1413
1414 hMenu = CreatePopupMenu();
1415 if (hMenu == NULL)
1416 {
1417 DPRINT("CreatePopupMenu failed\n");
1418 return;
1419 }
1420
1421 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
1422 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1423 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
1424 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
1425 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1426 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
1427 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
1428 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
1429 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
1430 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
1431 }
1432 */
1433
1434 static LRESULT
1435 GuiConsoleHandleScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
1436 {
1437 PCONSOLE Console = GuiData->Console;
1438 PCONSOLE_SCREEN_BUFFER Buff;
1439 SCROLLINFO sInfo;
1440 int fnBar;
1441 int old_pos, Maximum;
1442 PUSHORT pShowXY;
1443
1444 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0;
1445
1446 Buff = Console->ActiveBuffer;
1447
1448 if (uMsg == WM_HSCROLL)
1449 {
1450 fnBar = SB_HORZ;
1451 Maximum = Buff->ScreenBufferSize.X - Console->ConsoleSize.X;
1452 pShowXY = &Buff->ShowX;
1453 }
1454 else
1455 {
1456 fnBar = SB_VERT;
1457 Maximum = Buff->ScreenBufferSize.Y - Console->ConsoleSize.Y;
1458 pShowXY = &Buff->ShowY;
1459 }
1460
1461 /* set scrollbar sizes */
1462 sInfo.cbSize = sizeof(SCROLLINFO);
1463 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1464
1465 if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit;
1466
1467 old_pos = sInfo.nPos;
1468
1469 switch (LOWORD(wParam))
1470 {
1471 case SB_LINELEFT:
1472 sInfo.nPos -= 1;
1473 break;
1474
1475 case SB_LINERIGHT:
1476 sInfo.nPos += 1;
1477 break;
1478
1479 case SB_PAGELEFT:
1480 sInfo.nPos -= sInfo.nPage;
1481 break;
1482
1483 case SB_PAGERIGHT:
1484 sInfo.nPos += sInfo.nPage;
1485 break;
1486
1487 case SB_THUMBTRACK:
1488 sInfo.nPos = sInfo.nTrackPos;
1489 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1490 break;
1491
1492 case SB_THUMBPOSITION:
1493 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1494 break;
1495
1496 case SB_TOP:
1497 sInfo.nPos = sInfo.nMin;
1498 break;
1499
1500 case SB_BOTTOM:
1501 sInfo.nPos = sInfo.nMax;
1502 break;
1503
1504 default:
1505 break;
1506 }
1507
1508 sInfo.nPos = max(sInfo.nPos, 0);
1509 sInfo.nPos = min(sInfo.nPos, Maximum);
1510
1511 if (old_pos != sInfo.nPos)
1512 {
1513 USHORT OldX = Buff->ShowX;
1514 USHORT OldY = Buff->ShowY;
1515 *pShowXY = sInfo.nPos;
1516
1517 ScrollWindowEx(GuiData->hWindow,
1518 (OldX - Buff->ShowX) * GuiData->CharWidth,
1519 (OldY - Buff->ShowY) * GuiData->CharHeight,
1520 NULL,
1521 NULL,
1522 NULL,
1523 NULL,
1524 SW_INVALIDATE);
1525
1526 sInfo.fMask = SIF_POS;
1527 SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
1528
1529 UpdateWindow(GuiData->hWindow);
1530 }
1531
1532 Quit:
1533 LeaveCriticalSection(&Console->Lock);
1534 return 0;
1535 }
1536
1537 static LRESULT CALLBACK
1538 GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1539 {
1540 LRESULT Result = 0;
1541 PGUI_CONSOLE_DATA GuiData = NULL;
1542 PCONSOLE Console = NULL;
1543
1544 /*
1545 * - If it's the first time we create a window for the terminal,
1546 * just initialize it and return.
1547 *
1548 * - If we are destroying the window, just do it and return.
1549 */
1550 if (msg == WM_NCCREATE)
1551 {
1552 return (LRESULT)GuiConsoleHandleNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
1553 }
1554 else if (msg == WM_NCDESTROY)
1555 {
1556 return GuiConsoleHandleNcDestroy(hWnd);
1557 }
1558
1559 /*
1560 * Now the terminal window is initialized.
1561 * Get the terminal data via the window's data.
1562 * If there is no data, just go away.
1563 */
1564 GuiData = GuiGetGuiData(hWnd);
1565 if (GuiData == NULL) return 0;
1566
1567 /*
1568 * Each helper function which needs the console
1569 * has to validate and lock it.
1570 */
1571
1572 /* We have a console, start message dispatching */
1573 switch (msg)
1574 {
1575 case WM_CLOSE:
1576 GuiConsoleHandleClose(GuiData);
1577 break;
1578
1579 case WM_PAINT:
1580 GuiConsoleHandlePaint(GuiData);
1581 break;
1582
1583 case WM_KEYDOWN:
1584 case WM_KEYUP:
1585 case WM_SYSKEYDOWN:
1586 case WM_SYSKEYUP:
1587 case WM_CHAR:
1588 {
1589 GuiConsoleHandleKey(GuiData, msg, wParam, lParam);
1590 break;
1591 }
1592
1593 case WM_TIMER:
1594 GuiConsoleHandleTimer(GuiData);
1595 break;
1596
1597 case WM_LBUTTONDOWN:
1598 case WM_MBUTTONDOWN:
1599 case WM_RBUTTONDOWN:
1600 case WM_LBUTTONUP:
1601 case WM_MBUTTONUP:
1602 case WM_RBUTTONUP:
1603 case WM_LBUTTONDBLCLK:
1604 case WM_MBUTTONDBLCLK:
1605 case WM_RBUTTONDBLCLK:
1606 case WM_MOUSEMOVE:
1607 case WM_MOUSEWHEEL:
1608 case WM_MOUSEHWHEEL:
1609 {
1610 Result = GuiConsoleHandleMouse(GuiData, msg, wParam, lParam);
1611 break;
1612 }
1613
1614 case WM_NCRBUTTONDOWN:
1615 {
1616 DPRINT("WM_NCRBUTTONDOWN\n");
1617 /*
1618 * HACK: !! Because, when we deal with WM_RBUTTON* and we do not
1619 * call after that DefWindowProc, on ReactOS, right-clicks on the
1620 * (non-client) application title-bar does not display the system
1621 * menu and does not trigger a WM_NCRBUTTONUP message too.
1622 * See: http://git.reactos.org/?p=reactos.git;a=blob;f=reactos/win32ss/user/user32/windows/defwnd.c;hb=HEAD#l1103
1623 * and line 1135 too.
1624 */
1625 if (DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam) == HTCAPTION)
1626 return DefWindowProcW(hWnd, WM_CONTEXTMENU, wParam, lParam);
1627 else
1628 goto Default;
1629 }
1630 // case WM_NCRBUTTONUP:
1631 // DPRINT1("WM_NCRBUTTONUP\n");
1632 // goto Default;
1633
1634 case WM_CONTEXTMENU:
1635 {
1636 if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
1637 {
1638 HMENU hMenu = CreatePopupMenu();
1639 if (hMenu != NULL)
1640 {
1641 GuiConsoleAppendMenuItems(hMenu, GuiConsoleEditMenuItems);
1642 TrackPopupMenuEx(hMenu,
1643 TPM_RIGHTBUTTON,
1644 GET_X_LPARAM(lParam),
1645 GET_Y_LPARAM(lParam),
1646 hWnd,
1647 NULL);
1648 DestroyMenu(hMenu);
1649 }
1650 break;
1651 }
1652 else
1653 {
1654 goto Default;
1655 }
1656 }
1657
1658 case WM_COMMAND:
1659 case WM_SYSCOMMAND:
1660 {
1661 Result = GuiConsoleHandleSysMenuCommand(GuiData, wParam, lParam);
1662 break;
1663 }
1664
1665 case WM_HSCROLL:
1666 case WM_VSCROLL:
1667 {
1668 Result = GuiConsoleHandleScroll(GuiData, msg, wParam);
1669 break;
1670 }
1671
1672 case WM_SETFOCUS:
1673 case WM_KILLFOCUS:
1674 {
1675 Console = GuiData->Console; // Not NULL because checked in GuiGetGuiData.
1676 if (ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1677 {
1678 INPUT_RECORD er;
1679 er.EventType = FOCUS_EVENT;
1680 er.Event.FocusEvent.bSetFocus = (msg == WM_SETFOCUS);
1681 ConioProcessInputEvent(Console, &er);
1682
1683 LeaveCriticalSection(&Console->Lock);
1684 }
1685 break;
1686 }
1687
1688 case WM_GETMINMAXINFO:
1689 GuiConsoleGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
1690 break;
1691
1692 case WM_SIZE:
1693 GuiConsoleResize(GuiData, wParam, lParam);
1694 break;
1695
1696 case PM_APPLY_CONSOLE_INFO:
1697 {
1698 Console = GuiData->Console; // Not NULL because checked in GuiGetGuiData.
1699 if (ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
1700 {
1701 GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam);
1702 LeaveCriticalSection(&Console->Lock);
1703 }
1704 break;
1705 }
1706
1707 case PM_CONSOLE_BEEP:
1708 DPRINT1("Beep !!\n");
1709 Beep(800, 200);
1710 break;
1711
1712 // case PM_CONSOLE_SET_TITLE:
1713 // SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
1714 // break;
1715
1716 default: Default:
1717 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
1718 break;
1719 }
1720
1721 return Result;
1722 }
1723
1724
1725
1726 /******************************************************************************
1727 * GUI Terminal Initialization *
1728 ******************************************************************************/
1729
1730 static LRESULT CALLBACK
1731 GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1732 {
1733 HWND NewWindow;
1734 LONG WindowCount;
1735 MSG Msg;
1736
1737 switch (msg)
1738 {
1739 case WM_CREATE:
1740 {
1741 SetWindowLongW(hWnd, GWL_USERDATA, 0);
1742 return 0;
1743 }
1744
1745 case PM_CREATE_CONSOLE:
1746 {
1747 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam;
1748 PCONSOLE Console = GuiData->Console;
1749
1750 NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
1751 GUI_CONSOLE_WINDOW_CLASS,
1752 Console->Title.Buffer,
1753 WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
1754 CW_USEDEFAULT,
1755 CW_USEDEFAULT,
1756 CW_USEDEFAULT,
1757 CW_USEDEFAULT,
1758 NULL,
1759 NULL,
1760 ConSrvDllInstance,
1761 (PVOID)GuiData);
1762 if (NULL != NewWindow)
1763 {
1764 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
1765 WindowCount++;
1766 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
1767
1768 DPRINT("Set icons via PM_CREATE_CONSOLE\n");
1769 if (GuiData->hIcon == NULL)
1770 {
1771 DPRINT("Not really /o\\...\n");
1772 GuiData->hIcon = ghDefaultIcon;
1773 GuiData->hIconSm = ghDefaultIconSm;
1774 }
1775 else if (GuiData->hIcon != ghDefaultIcon)
1776 {
1777 DPRINT("Yes \\o/\n");
1778 SendMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
1779 SendMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
1780 }
1781
1782 /* Move and resize the window to the user's values */
1783 /* CAN WE DEADLOCK ?? */
1784 GuiConsoleMoveWindow(GuiData);
1785 GuiData->WindowSizeLock = TRUE;
1786 GuiConsoleResizeWindow(GuiData);
1787 GuiData->WindowSizeLock = FALSE;
1788
1789 // ShowWindow(NewWindow, (int)wParam);
1790 ShowWindowAsync(NewWindow, (int)wParam);
1791 DPRINT("Window showed\n");
1792 }
1793
1794 return (LRESULT)NewWindow;
1795 }
1796
1797 case PM_DESTROY_CONSOLE:
1798 {
1799 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam;
1800
1801 /*
1802 * Window creation is done using a PostMessage(), so it's possible
1803 * that the window that we want to destroy doesn't exist yet.
1804 * So first empty the message queue.
1805 */
1806 /*
1807 while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
1808 {
1809 TranslateMessage(&Msg);
1810 DispatchMessageW(&Msg);
1811 }*/
1812 while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) ;
1813
1814 if (GuiData->hWindow != NULL) /* && DestroyWindow(GuiData->hWindow) */
1815 {
1816 DestroyWindow(GuiData->hWindow);
1817
1818 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
1819 WindowCount--;
1820 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
1821 if (0 == WindowCount)
1822 {
1823 NotifyWnd = NULL;
1824 DestroyWindow(hWnd);
1825 DPRINT("CONSRV: Going to quit the Gui Thread!!\n");
1826 PostQuitMessage(0);
1827 }
1828 }
1829
1830 return 0;
1831 }
1832
1833 default:
1834 return DefWindowProcW(hWnd, msg, wParam, lParam);
1835 }
1836 }
1837
1838 static DWORD WINAPI
1839 GuiConsoleGuiThread(PVOID Data)
1840 {
1841 MSG msg;
1842 PHANDLE GraphicsStartupEvent = (PHANDLE)Data;
1843
1844 /*
1845 * This thread dispatches all the console notifications to the notify window.
1846 * It is common for all the console windows.
1847 */
1848
1849 PrivateCsrssManualGuiCheck(+1);
1850
1851 NotifyWnd = CreateWindowW(L"ConSrvCreateNotify",
1852 L"",
1853 WS_OVERLAPPEDWINDOW,
1854 CW_USEDEFAULT,
1855 CW_USEDEFAULT,
1856 CW_USEDEFAULT,
1857 CW_USEDEFAULT,
1858 NULL,
1859 NULL,
1860 ConSrvDllInstance,
1861 NULL);
1862 if (NULL == NotifyWnd)
1863 {
1864 PrivateCsrssManualGuiCheck(-1);
1865 SetEvent(*GraphicsStartupEvent);
1866 return 1;
1867 }
1868
1869 SetEvent(*GraphicsStartupEvent);
1870
1871 while (GetMessageW(&msg, NULL, 0, 0))
1872 {
1873 TranslateMessage(&msg);
1874 DispatchMessageW(&msg);
1875 }
1876
1877 DPRINT("CONSRV: Quit the Gui Thread!!\n");
1878 PrivateCsrssManualGuiCheck(-1);
1879
1880 return 1;
1881 }
1882
1883 static BOOL
1884 GuiInit(VOID)
1885 {
1886 WNDCLASSEXW wc;
1887 ATOM ConsoleClassAtom;
1888
1889 /* Exit if we were already initialized */
1890 // if (ConsInitialized) return TRUE;
1891
1892 /*
1893 * Initialize and register the different window classes, if needed.
1894 */
1895 if (!ConsInitialized)
1896 {
1897 /* Initialize the notification window class */
1898 wc.cbSize = sizeof(WNDCLASSEXW);
1899 wc.lpszClassName = L"ConSrvCreateNotify";
1900 wc.lpfnWndProc = GuiConsoleNotifyWndProc;
1901 wc.style = 0;
1902 wc.hInstance = ConSrvDllInstance;
1903 wc.hIcon = NULL;
1904 wc.hIconSm = NULL;
1905 wc.hCursor = NULL;
1906 wc.hbrBackground = NULL;
1907 wc.lpszMenuName = NULL;
1908 wc.cbClsExtra = 0;
1909 wc.cbWndExtra = 0;
1910 if (RegisterClassExW(&wc) == 0)
1911 {
1912 DPRINT1("Failed to register GUI notify wndproc\n");
1913 return FALSE;
1914 }
1915
1916 /* Initialize the console window class */
1917 ghDefaultIcon = LoadImageW(ConSrvDllInstance,
1918 MAKEINTRESOURCEW(IDI_TERMINAL),
1919 IMAGE_ICON,
1920 GetSystemMetrics(SM_CXICON),
1921 GetSystemMetrics(SM_CYICON),
1922 LR_SHARED);
1923 ghDefaultIconSm = LoadImageW(ConSrvDllInstance,
1924 MAKEINTRESOURCEW(IDI_TERMINAL),
1925 IMAGE_ICON,
1926 GetSystemMetrics(SM_CXSMICON),
1927 GetSystemMetrics(SM_CYSMICON),
1928 LR_SHARED);
1929 ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
1930 wc.cbSize = sizeof(WNDCLASSEXW);
1931 wc.lpszClassName = GUI_CONSOLE_WINDOW_CLASS;
1932 wc.lpfnWndProc = GuiConsoleWndProc;
1933 wc.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */;
1934 wc.hInstance = ConSrvDllInstance;
1935 wc.hIcon = ghDefaultIcon;
1936 wc.hIconSm = ghDefaultIconSm;
1937 wc.hCursor = ghDefaultCursor;
1938 wc.hbrBackground = CreateSolidBrush(RGB(0,0,0)); // FIXME: Use defaults from registry.
1939 wc.lpszMenuName = NULL;
1940 wc.cbClsExtra = 0;
1941 wc.cbWndExtra = GWLP_CONSOLEWND_ALLOC;
1942
1943 ConsoleClassAtom = RegisterClassExW(&wc);
1944 if (ConsoleClassAtom == 0)
1945 {
1946 DPRINT1("Failed to register GUI console wndproc\n");
1947 return FALSE;
1948 }
1949 else
1950 {
1951 NtUserConsoleControl(GuiConsoleWndClassAtom, &ConsoleClassAtom, sizeof(ATOM));
1952 }
1953
1954 ConsInitialized = TRUE;
1955 }
1956
1957 /*
1958 * Set-up the notification window
1959 */
1960 if (NULL == NotifyWnd)
1961 {
1962 HANDLE ThreadHandle;
1963 HANDLE GraphicsStartupEvent;
1964
1965 GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1966 if (NULL == GraphicsStartupEvent) return FALSE;
1967
1968 ThreadHandle = CreateThread(NULL,
1969 0,
1970 GuiConsoleGuiThread,
1971 (PVOID)&GraphicsStartupEvent,
1972 0,
1973 NULL);
1974 if (NULL == ThreadHandle)
1975 {
1976 CloseHandle(GraphicsStartupEvent);
1977 DPRINT1("CONSRV: Failed to create graphics console thread. Expect problems\n");
1978 return FALSE;
1979 }
1980 SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST);
1981 CloseHandle(ThreadHandle);
1982
1983 WaitForSingleObject(GraphicsStartupEvent, INFINITE);
1984 CloseHandle(GraphicsStartupEvent);
1985
1986 if (NULL == NotifyWnd)
1987 {
1988 DPRINT1("CONSRV: Failed to create notification window.\n");
1989 return FALSE;
1990 }
1991 }
1992
1993 // ConsInitialized = TRUE;
1994
1995 return TRUE;
1996 }
1997
1998
1999
2000 /******************************************************************************
2001 * GUI Console Driver *
2002 ******************************************************************************/
2003
2004 static VOID WINAPI
2005 GuiCleanupConsole(PCONSOLE Console)
2006 {
2007 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2008
2009 SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
2010
2011 DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
2012 GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
2013 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
2014 {
2015 DPRINT("Destroy hIcon\n");
2016 DestroyIcon(GuiData->hIcon);
2017 }
2018 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
2019 {
2020 DPRINT("Destroy hIconSm\n");
2021 DestroyIcon(GuiData->hIconSm);
2022 }
2023
2024 Console->TermIFace.Data = NULL;
2025 DeleteCriticalSection(&GuiData->Lock);
2026 RtlFreeHeap(ConSrvHeap, 0, GuiData);
2027
2028 DPRINT("Quit GuiCleanupConsole\n");
2029 }
2030
2031 static VOID WINAPI
2032 GuiWriteStream(PCONSOLE Console, SMALL_RECT* Region, LONG CursorStartX, LONG CursorStartY,
2033 UINT ScrolledLines, CHAR *Buffer, UINT Length)
2034 {
2035 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2036 PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer;
2037 LONG CursorEndX, CursorEndY;
2038 RECT ScrollRect;
2039
2040 if (NULL == GuiData || NULL == GuiData->hWindow)
2041 {
2042 return;
2043 }
2044
2045 if (0 != ScrolledLines)
2046 {
2047 ScrollRect.left = 0;
2048 ScrollRect.top = 0;
2049 ScrollRect.right = Console->ConsoleSize.X * GuiData->CharWidth;
2050 ScrollRect.bottom = Region->Top * GuiData->CharHeight;
2051
2052 ScrollWindowEx(GuiData->hWindow,
2053 0,
2054 -(int)(ScrolledLines * GuiData->CharHeight),
2055 &ScrollRect,
2056 NULL,
2057 NULL,
2058 NULL,
2059 SW_INVALIDATE);
2060 }
2061
2062 GuiDrawRegion(Console, Region);
2063
2064 if (CursorStartX < Region->Left || Region->Right < CursorStartX
2065 || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
2066 {
2067 GuiInvalidateCell(Console, CursorStartX, CursorStartY);
2068 }
2069
2070 CursorEndX = Buff->CursorPosition.X;
2071 CursorEndY = Buff->CursorPosition.Y;
2072 if ((CursorEndX < Region->Left || Region->Right < CursorEndX
2073 || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
2074 && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
2075 {
2076 GuiInvalidateCell(Console, CursorEndX, CursorEndY);
2077 }
2078
2079 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
2080 // repaint the window without having it just freeze up and stay on the screen permanently.
2081 Buff->CursorBlinkOn = TRUE;
2082 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
2083 }
2084
2085 static VOID WINAPI
2086 GuiDrawRegion(PCONSOLE Console, SMALL_RECT* Region)
2087 {
2088 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2089 RECT RegionRect;
2090
2091 SmallRectToRect(GuiData, &RegionRect, Region);
2092 InvalidateRect(GuiData->hWindow, &RegionRect, FALSE);
2093 }
2094
2095 static BOOL WINAPI
2096 GuiSetCursorInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff)
2097 {
2098 if (Console->ActiveBuffer == Buff)
2099 {
2100 GuiInvalidateCell(Console, Buff->CursorPosition.X, Buff->CursorPosition.Y);
2101 }
2102
2103 return TRUE;
2104 }
2105
2106 static BOOL WINAPI
2107 GuiSetScreenInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff, UINT OldCursorX, UINT OldCursorY)
2108 {
2109 if (Console->ActiveBuffer == Buff)
2110 {
2111 /* Redraw char at old position (removes cursor) */
2112 GuiInvalidateCell(Console, OldCursorX, OldCursorY);
2113 /* Redraw char at new position (shows cursor) */
2114 GuiInvalidateCell(Console, Buff->CursorPosition.X, Buff->CursorPosition.Y);
2115 }
2116
2117 return TRUE;
2118 }
2119
2120 static BOOL WINAPI
2121 GuiUpdateScreenInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff)
2122 {
2123 return TRUE;
2124 }
2125
2126 static BOOL WINAPI
2127 GuiIsBufferResizeSupported(PCONSOLE Console)
2128 {
2129 return TRUE;
2130 }
2131
2132 static VOID WINAPI
2133 GuiResizeTerminal(PCONSOLE Console)
2134 {
2135 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2136
2137 /* Resize the window to the user's values */
2138 GuiData->WindowSizeLock = TRUE;
2139 GuiConsoleResizeWindow(GuiData);
2140 GuiData->WindowSizeLock = FALSE;
2141 }
2142
2143 static BOOL WINAPI
2144 GuiProcessKeyCallback(PCONSOLE Console, MSG* msg, BYTE KeyStateMenu, DWORD ShiftState, UINT VirtualKeyCode, BOOL Down)
2145 {
2146 if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyStateMenu & 0x80) &&
2147 (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE))
2148 {
2149 DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
2150 return TRUE;
2151 }
2152
2153 return FALSE;
2154 }
2155
2156 static VOID WINAPI
2157 GuiRefreshInternalInfo(PCONSOLE Console)
2158 {
2159 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2160
2161 /* Update the console leader information held by the window */
2162 SetConsoleWndConsoleLeaderCID(GuiData);
2163 }
2164
2165 static VOID WINAPI
2166 GuiChangeTitle(PCONSOLE Console)
2167 {
2168 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2169 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
2170 SetWindowText(GuiData->hWindow, Console->Title.Buffer);
2171 }
2172
2173 static BOOL WINAPI
2174 GuiChangeIcon(PCONSOLE Console, HICON hWindowIcon)
2175 {
2176 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2177 HICON hIcon, hIconSm;
2178
2179 if (hWindowIcon == NULL)
2180 {
2181 hIcon = ghDefaultIcon;
2182 hIconSm = ghDefaultIconSm;
2183 }
2184 else
2185 {
2186 hIcon = CopyIcon(hWindowIcon);
2187 hIconSm = CopyIcon(hWindowIcon);
2188 }
2189
2190 if (hIcon == NULL)
2191 {
2192 return FALSE;
2193 }
2194
2195 if (hIcon != GuiData->hIcon)
2196 {
2197 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
2198 {
2199 DestroyIcon(GuiData->hIcon);
2200 }
2201 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
2202 {
2203 DestroyIcon(GuiData->hIconSm);
2204 }
2205
2206 GuiData->hIcon = hIcon;
2207 GuiData->hIconSm = hIconSm;
2208
2209 DPRINT("Set icons in GuiChangeIcon\n");
2210 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
2211 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
2212 }
2213
2214 return TRUE;
2215 }
2216
2217 static HWND WINAPI
2218 GuiGetConsoleWindowHandle(PCONSOLE Console)
2219 {
2220 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2221 return GuiData->hWindow;
2222 }
2223
2224 static VOID WINAPI
2225 GuiGetLargestConsoleWindowSize(PCONSOLE Console, PCOORD pSize)
2226 {
2227 PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
2228 HWND hDesktop;
2229 RECT desktop;
2230 LONG width, height;
2231
2232 if (!pSize) return;
2233
2234 /*
2235 * This is one solution. Surely better solutions exist :
2236 * http://stackoverflow.com/questions/4631292/how-detect-current-screen-resolution
2237 * http://www.clearevo.com/blog/programming/2011/08/30/windows_c_c++_-_get_monitor_display_screen_size_in_pixels.html
2238 */
2239 hDesktop = GetDesktopWindow();
2240 if (!hDesktop) return;
2241
2242 GetWindowRect(hDesktop, &desktop);
2243
2244 width = desktop.right;
2245 height = desktop.bottom;
2246
2247 width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
2248 height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
2249
2250 if (width < 0) width = 0;
2251 if (height < 0) height = 0;
2252
2253 pSize->X = (SHORT)(width / GuiData->CharWidth );
2254 pSize->Y = (SHORT)(height / GuiData->CharHeight);
2255 }
2256
2257 static FRONTEND_VTBL GuiVtbl =
2258 {
2259 GuiCleanupConsole,
2260 GuiWriteStream,
2261 GuiDrawRegion,
2262 GuiSetCursorInfo,
2263 GuiSetScreenInfo,
2264 GuiUpdateScreenInfo,
2265 GuiIsBufferResizeSupported,
2266 GuiResizeTerminal,
2267 GuiProcessKeyCallback,
2268 GuiRefreshInternalInfo,
2269 GuiChangeTitle,
2270 GuiChangeIcon,
2271 GuiGetConsoleWindowHandle,
2272 GuiGetLargestConsoleWindowSize
2273 };
2274
2275 NTSTATUS FASTCALL
2276 GuiInitConsole(PCONSOLE Console,
2277 /*IN*/ PCONSOLE_START_INFO ConsoleStartInfo,
2278 PCONSOLE_INFO ConsoleInfo,
2279 DWORD ProcessId,
2280 LPCWSTR IconPath,
2281 INT IconIndex)
2282 {
2283 PGUI_CONSOLE_DATA GuiData;
2284 GUI_CONSOLE_INFO TermInfo;
2285 SIZE_T Length = 0;
2286
2287 if (Console == NULL || ConsoleInfo == NULL)
2288 return STATUS_INVALID_PARAMETER;
2289
2290 /* Initialize the GUI terminal emulator */
2291 if (!GuiInit()) return STATUS_UNSUCCESSFUL;
2292
2293 /* Initialize the console */
2294 Console->TermIFace.Vtbl = &GuiVtbl;
2295
2296 GuiData = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY,
2297 sizeof(GUI_CONSOLE_DATA));
2298 if (!GuiData)
2299 {
2300 DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
2301 return STATUS_UNSUCCESSFUL;
2302 }
2303 Console->TermIFace.Data = (PVOID)GuiData;
2304 GuiData->Console = Console;
2305 GuiData->hWindow = NULL;
2306
2307 InitializeCriticalSection(&GuiData->Lock);
2308
2309
2310 /*
2311 * Load the terminal settings
2312 */
2313
2314 /***********************************************
2315 * Adapted from ConSrvInitConsole in console.c *
2316 ***********************************************/
2317
2318 /* 1. Load the default settings */
2319 GuiConsoleGetDefaultSettings(&TermInfo, ProcessId);
2320
2321 /* 2. Load the remaining console settings via the registry. */
2322 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
2323 {
2324 /* Load the terminal infos from the registry. */
2325 GuiConsoleReadUserSettings(&TermInfo, ConsoleInfo->ConsoleTitle, ProcessId);
2326
2327 /*
2328 * Now, update them with the properties the user might gave to us
2329 * via the STARTUPINFO structure before calling CreateProcess
2330 * (and which was transmitted via the ConsoleStartInfo structure).
2331 * We therefore overwrite the values read in the registry.
2332 */
2333 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
2334 {
2335 TermInfo.ShowWindow = ConsoleStartInfo->ShowWindow;
2336 }
2337 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
2338 {
2339 TermInfo.AutoPosition = FALSE;
2340 TermInfo.WindowOrigin = ConsoleStartInfo->ConsoleWindowOrigin;
2341 }
2342 /*
2343 if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
2344 {
2345 ConsoleInfo.FullScreen = TRUE;
2346 }
2347 */
2348 }
2349
2350
2351 /*
2352 * Set up the GUI data
2353 */
2354
2355 Length = min(wcslen(TermInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen
2356 wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE);
2357 GuiData->GuiInfo.FaceName[Length] = L'\0';
2358 GuiData->GuiInfo.FontFamily = TermInfo.FontFamily;
2359 GuiData->GuiInfo.FontSize = TermInfo.FontSize;
2360 GuiData->GuiInfo.FontWeight = TermInfo.FontWeight;
2361 GuiData->GuiInfo.UseRasterFonts = TermInfo.UseRasterFonts;
2362 GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow;
2363 GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition;
2364 GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin;
2365
2366 /* Initialize the icon handles to their default values */
2367 GuiData->hIcon = ghDefaultIcon;
2368 GuiData->hIconSm = ghDefaultIconSm;
2369
2370 /* Get the associated icon, if any */
2371 if (IconPath == NULL || *IconPath == L'\0')
2372 {
2373 IconPath = ConsoleStartInfo->AppPath;
2374 IconIndex = 0;
2375 }
2376 DPRINT("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex);
2377 if (IconPath)
2378 {
2379 HICON hIcon = NULL, hIconSm = NULL;
2380 PrivateExtractIconExW(IconPath,
2381 IconIndex,
2382 &hIcon,
2383 &hIconSm,
2384 1);
2385 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
2386 if (hIcon != NULL)
2387 {
2388 DPRINT("Effectively set the icons\n");
2389 GuiData->hIcon = hIcon;
2390 GuiData->hIconSm = hIconSm;
2391 }
2392 }
2393
2394 /*
2395 * We need to wait until the GUI has been fully initialized
2396 * to retrieve custom settings i.e. WindowSize etc...
2397 * Ideally we could use SendNotifyMessage for this but its not
2398 * yet implemented.
2399 */
2400 GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2401
2402 /* Create the terminal window */
2403 PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData);
2404
2405 /* Wait until initialization has finished */
2406 WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
2407 DPRINT("OK we created the console window\n");
2408 CloseHandle(GuiData->hGuiInitEvent);
2409 GuiData->hGuiInitEvent = NULL;
2410
2411 /* Check whether we really succeeded in initializing the terminal window */
2412 if (GuiData->hWindow == NULL)
2413 {
2414 DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
2415 // ConioCleanupConsole(Console);
2416 GuiCleanupConsole(Console);
2417 return STATUS_UNSUCCESSFUL;
2418 }
2419
2420 return STATUS_SUCCESS;
2421 }
2422
2423 /* EOF */