Convert title from UNICODE_STRING to nul-terminated
[reactos.git] / reactos / subsys / csrss / win32csr / guiconsole.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: subsys/csrss/win32csr/guiconsole.c
6 * PURPOSE: Implementation of gui-mode consoles
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "w32csr.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* Not defined in any header file */
17 extern VOID STDCALL PrivateCsrssManualGuiCheck(LONG Check);
18
19 /* GLOBALS *******************************************************************/
20
21 typedef struct GUI_CONSOLE_DATA_TAG
22 {
23 HFONT Font;
24 unsigned CharWidth;
25 unsigned CharHeight;
26 PWCHAR LineBuffer;
27 BOOL CursorBlinkOn;
28 BOOL ForceCursorOff;
29 CRITICAL_SECTION Lock;
30 HDC MemoryDC;
31 HBITMAP MemoryBitmap;
32 RECT Selection;
33 POINT SelectionStart;
34 BOOL MouseDown;
35 } GUI_CONSOLE_DATA, *PGUI_CONSOLE_DATA;
36
37 #ifndef WM_APP
38 #define WM_APP 0x8000
39 #endif
40 #define PM_CREATE_CONSOLE (WM_APP + 1)
41 #define PM_DESTROY_CONSOLE (WM_APP + 2)
42
43 #define CURSOR_BLINK_TIME 500
44
45 static BOOL ConsInitialized = FALSE;
46 static HWND NotifyWnd;
47
48 /* FUNCTIONS *****************************************************************/
49
50 static VOID FASTCALL
51 GuiConsoleGetDataPointers(HWND hWnd, PCSRSS_CONSOLE *Console, PGUI_CONSOLE_DATA *GuiData)
52 {
53 *Console = (PCSRSS_CONSOLE) GetWindowLongPtrW(hWnd, GWL_USERDATA);
54 *GuiData = (NULL == *Console ? NULL : (*Console)->PrivateData);
55 }
56
57 static BOOL FASTCALL
58 GuiConsoleHandleNcCreate(HWND hWnd, CREATESTRUCTW *Create)
59 {
60 RECT Rect;
61 PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) Create->lpCreateParams;
62 PGUI_CONSOLE_DATA GuiData;
63 HDC Dc;
64 HFONT OldFont;
65 TEXTMETRICW Metrics;
66
67 GuiData = HeapAlloc(Win32CsrApiHeap, HEAP_ZERO_MEMORY,
68 sizeof(GUI_CONSOLE_DATA) +
69 (Console->Size.X + 1) * sizeof(WCHAR));
70 if (NULL == GuiData)
71 {
72 DPRINT1("GuiConsoleNcCreate: HeapAlloc failed\n");
73 return FALSE;
74 }
75
76 InitializeCriticalSection(&GuiData->Lock);
77
78 GuiData->LineBuffer = (PWCHAR)(GuiData + 1);
79
80 GuiData->Font = CreateFontW(12, 0, 0, TA_BASELINE, FW_NORMAL,
81 FALSE, FALSE, FALSE, OEM_CHARSET,
82 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
83 NONANTIALIASED_QUALITY, FIXED_PITCH | FF_DONTCARE,
84 L"Bitstream Vera Sans Mono");
85 if (NULL == GuiData->Font)
86 {
87 DPRINT1("GuiConsoleNcCreate: CreateFont failed\n");
88 DeleteCriticalSection(&GuiData->Lock);
89 HeapFree(Win32CsrApiHeap, 0, GuiData);
90 return FALSE;
91 }
92 Dc = GetDC(hWnd);
93 if (NULL == Dc)
94 {
95 DPRINT1("GuiConsoleNcCreate: GetDC failed\n");
96 DeleteObject(GuiData->Font);
97 DeleteCriticalSection(&GuiData->Lock);
98 HeapFree(Win32CsrApiHeap, 0, GuiData);
99 return FALSE;
100 }
101 OldFont = SelectObject(Dc, GuiData->Font);
102 if (NULL == OldFont)
103 {
104 DPRINT1("GuiConsoleNcCreate: SelectObject failed\n");
105 ReleaseDC(hWnd, Dc);
106 DeleteObject(GuiData->Font);
107 DeleteCriticalSection(&GuiData->Lock);
108 HeapFree(Win32CsrApiHeap, 0, GuiData);
109 return FALSE;
110 }
111 if (! GetTextMetricsW(Dc, &Metrics))
112 {
113 DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n");
114 SelectObject(Dc, OldFont);
115 ReleaseDC(hWnd, Dc);
116 DeleteObject(GuiData->Font);
117 DeleteCriticalSection(&GuiData->Lock);
118 HeapFree(Win32CsrApiHeap, 0, GuiData);
119 return FALSE;
120 }
121 GuiData->CharWidth = Metrics.tmMaxCharWidth;
122 GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
123 SelectObject(Dc, OldFont);
124
125 GuiData->MemoryDC = CreateCompatibleDC(Dc);
126 GuiData->MemoryBitmap = CreateCompatibleBitmap(Dc,
127 Console->Size.X * GuiData->CharWidth,
128 Console->Size.Y * GuiData->CharHeight);
129 /* NOTE: Don't delete the "first bitmap", it's done in DeleteDC. */
130 SelectObject(GuiData->MemoryDC, GuiData->MemoryBitmap);
131 /* NOTE: Don't delete stock font. */
132 SelectObject(GuiData->MemoryDC, GuiData->Font);
133
134 ReleaseDC(hWnd, Dc);
135 GuiData->CursorBlinkOn = TRUE;
136 GuiData->ForceCursorOff = FALSE;
137
138 GuiData->Selection.left = -1;
139
140 Console->PrivateData = GuiData;
141 SetWindowLongPtrW(hWnd, GWL_USERDATA, (DWORD_PTR) Console);
142
143 GetWindowRect(hWnd, &Rect);
144 Rect.right = Rect.left + Console->Size.X * GuiData->CharWidth +
145 2 * GetSystemMetrics(SM_CXFIXEDFRAME);
146 Rect.bottom = Rect.top + Console->Size.Y * GuiData->CharHeight +
147 2 * GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION);
148 MoveWindow(hWnd, Rect.left, Rect.top, Rect.right - Rect.left,
149 Rect.bottom - Rect.top, FALSE);
150
151 SetTimer(hWnd, 1, CURSOR_BLINK_TIME, NULL);
152
153 return (BOOL) DefWindowProcW(hWnd, WM_NCCREATE, 0, (LPARAM) Create);
154 }
155
156 static COLORREF FASTCALL
157 GuiConsoleRGBFromAttribute(BYTE Attribute)
158 {
159 int Red = (Attribute & 0x04 ? (Attribute & 0x08 ? 0xff : 0x80) : 0x00);
160 int Green = (Attribute & 0x02 ? (Attribute & 0x08 ? 0xff : 0x80) : 0x00);
161 int Blue = (Attribute & 0x01 ? (Attribute & 0x08 ? 0xff : 0x80) : 0x00);
162
163 return RGB(Red, Green, Blue);
164 }
165
166 static VOID FASTCALL
167 GuiConsoleSetTextColors(HDC Dc, BYTE Attribute)
168 {
169 SetTextColor(Dc, GuiConsoleRGBFromAttribute(Attribute & 0x0f));
170 SetBkColor(Dc, GuiConsoleRGBFromAttribute((Attribute & 0xf0) >> 4));
171 }
172
173 static VOID FASTCALL
174 GuiConsoleGetLogicalCursorPos(PCSRSS_SCREEN_BUFFER Buff, ULONG *CursorX, ULONG *CursorY)
175 {
176 *CursorX = Buff->CurrentX;
177 if (Buff->CurrentY < Buff->ShowY)
178 {
179 *CursorY = Buff->MaxY - Buff->ShowY + Buff->CurrentY;
180 }
181 else
182 {
183 *CursorY = Buff->CurrentY - Buff->ShowY;
184 }
185 }
186
187
188 static VOID FASTCALL
189 GuiConsoleUpdateSelection(HWND hWnd, PRECT rc, PGUI_CONSOLE_DATA GuiData)
190 {
191 RECT oldRect = GuiData->Selection;
192
193 if(rc != NULL)
194 {
195 RECT changeRect = *rc;
196
197 GuiData->Selection = *rc;
198
199 changeRect.left *= GuiData->CharWidth;
200 changeRect.top *= GuiData->CharHeight;
201 changeRect.right *= GuiData->CharWidth;
202 changeRect.bottom *= GuiData->CharHeight;
203
204 if(rc->left != oldRect.left ||
205 rc->top != oldRect.top ||
206 rc->right != oldRect.right ||
207 rc->bottom != oldRect.bottom)
208 {
209 if(oldRect.left != -1)
210 {
211 HRGN rgn1, rgn2;
212
213 oldRect.left *= GuiData->CharWidth;
214 oldRect.top *= GuiData->CharHeight;
215 oldRect.right *= GuiData->CharWidth;
216 oldRect.bottom *= GuiData->CharHeight;
217
218 /* calculate the region that needs to be updated */
219 if((rgn1 = CreateRectRgnIndirect(&oldRect)))
220 {
221 if((rgn2 = CreateRectRgnIndirect(&changeRect)))
222 {
223 if(CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR)
224 {
225 InvalidateRgn(hWnd, rgn1, FALSE);
226 }
227
228 DeleteObject(rgn2);
229 }
230 DeleteObject(rgn1);
231 }
232 }
233 else
234 {
235 InvalidateRect(hWnd, &changeRect, FALSE);
236 }
237 }
238 }
239 else if(oldRect.left != -1)
240 {
241 /* clear the selection */
242 GuiData->Selection.left = -1;
243 oldRect.left *= GuiData->CharWidth;
244 oldRect.top *= GuiData->CharHeight;
245 oldRect.right *= GuiData->CharWidth;
246 oldRect.bottom *= GuiData->CharHeight;
247 InvalidateRect(hWnd, &oldRect, FALSE);
248 }
249 }
250
251
252 VOID FASTCALL
253 GuiConsoleUpdateBitmap(HWND hWnd, RECT rc)
254 {
255 PCSRSS_CONSOLE Console;
256 PGUI_CONSOLE_DATA GuiData;
257 PCSRSS_SCREEN_BUFFER Buff;
258 HDC Dc;
259 ULONG TopLine, BottomLine, LeftChar, RightChar;
260 ULONG Line, Char, Start;
261 PBYTE From;
262 PWCHAR To;
263 BYTE LastAttribute, Attribute;
264 ULONG CursorX, CursorY, CursorHeight;
265 HBRUSH CursorBrush, OldBrush;
266
267 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
268 if (NULL != Console && NULL != GuiData && NULL != Console->ActiveBuffer)
269 {
270 Buff = Console->ActiveBuffer;
271 EnterCriticalSection(&Buff->Header.Lock);
272 Dc = GetDC(hWnd);
273 if (rc.right <= rc.left || rc.bottom <= rc.top)
274 {
275 ReleaseDC(hWnd, Dc);
276 LeaveCriticalSection(&Buff->Header.Lock);
277 return;
278 }
279
280 EnterCriticalSection(&GuiData->Lock);
281
282 TopLine = rc.top / GuiData->CharHeight;
283 BottomLine = (rc.bottom + (GuiData->CharHeight - 1)) / GuiData->CharHeight - 1;
284 LeftChar = rc.left / GuiData->CharWidth;
285 RightChar = (rc.right + (GuiData->CharWidth - 1)) / GuiData->CharWidth - 1;
286 LastAttribute = Buff->Buffer[(TopLine * Buff->MaxX + LeftChar) * 2 + 1];
287 GuiConsoleSetTextColors(GuiData->MemoryDC, LastAttribute);
288
289 for (Line = TopLine; Line <= BottomLine; Line++)
290 {
291 if (Line + Buff->ShowY < Buff->MaxY)
292 {
293 From = Buff->Buffer + ((Line + Buff->ShowY) * Buff->MaxX + LeftChar) * 2;
294 }
295 else
296 {
297 From = Buff->Buffer +
298 ((Line - (Buff->MaxY - Buff->ShowY)) * Buff->MaxX + LeftChar) * 2;
299 }
300 Start = LeftChar;
301 To = GuiData->LineBuffer;
302 for (Char = LeftChar; Char <= RightChar; Char++)
303 {
304 if (*(From + 1) != LastAttribute)
305 {
306 TextOutW(GuiData->MemoryDC, Start * GuiData->CharWidth, Line * GuiData->CharHeight,
307 GuiData->LineBuffer, Char - Start);
308 Start = Char;
309 To = GuiData->LineBuffer;
310 Attribute = *(From + 1);
311 if (Attribute != LastAttribute)
312 {
313 GuiConsoleSetTextColors(GuiData->MemoryDC, Attribute);
314 LastAttribute = Attribute;
315 }
316 }
317 MultiByteToWideChar(Console->OutputCodePage, 0, (PCHAR)From, 1, To, 1);
318 To++;
319 From += 2;
320 }
321 TextOutW(GuiData->MemoryDC, Start * GuiData->CharWidth, Line * GuiData->CharHeight,
322 GuiData->LineBuffer, RightChar - Start + 1);
323 }
324
325 if (Buff->CursorInfo.bVisible && GuiData->CursorBlinkOn
326 &&! GuiData->ForceCursorOff)
327 {
328 GuiConsoleGetLogicalCursorPos(Buff, &CursorX, &CursorY);
329 if (LeftChar <= CursorX && CursorX <= RightChar &&
330 TopLine <= CursorY && CursorY <= BottomLine)
331 {
332 CursorHeight = (GuiData->CharHeight * Buff->CursorInfo.dwSize) / 100;
333 if (CursorHeight < 1)
334 {
335 CursorHeight = 1;
336 }
337 From = Buff->Buffer + (Buff->CurrentY * Buff->MaxX + Buff->CurrentX) * 2 + 1;
338 CursorBrush = CreateSolidBrush(GuiConsoleRGBFromAttribute(*From));
339 OldBrush = SelectObject(GuiData->MemoryDC, CursorBrush);
340 PatBlt(GuiData->MemoryDC, CursorX * GuiData->CharWidth,
341 CursorY * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
342 GuiData->CharWidth, CursorHeight, PATCOPY);
343 SelectObject(GuiData->MemoryDC, OldBrush);
344 DeleteObject(CursorBrush);
345 }
346 }
347
348 LeaveCriticalSection(&GuiData->Lock);
349 ReleaseDC(hWnd, Dc);
350 LeaveCriticalSection(&Buff->Header.Lock);
351 InvalidateRect(hWnd, &rc, FALSE);
352 }
353 }
354
355 VOID FASTCALL
356 GuiConsoleHandlePaint(HWND hWnd)
357 {
358 PAINTSTRUCT Ps;
359 HDC Dc;
360 PCSRSS_CONSOLE Console;
361 PGUI_CONSOLE_DATA GuiData;
362
363 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
364 if (NULL != Console && NULL != GuiData)
365 {
366 EnterCriticalSection(&GuiData->Lock);
367 Dc = BeginPaint (hWnd, &Ps);
368 BitBlt(Dc, Ps.rcPaint.left, Ps.rcPaint.top,
369 Ps.rcPaint.right - Ps.rcPaint.left + 1,
370 Ps.rcPaint.bottom - Ps.rcPaint.top + 1, GuiData->MemoryDC,
371 Ps.rcPaint.left, Ps.rcPaint.top, SRCCOPY);
372
373 if (GuiData->Selection.left != -1)
374 {
375 RECT rc = GuiData->Selection;
376
377 rc.left *= GuiData->CharWidth;
378 rc.top *= GuiData->CharHeight;
379 rc.right *= GuiData->CharWidth;
380 rc.bottom *= GuiData->CharHeight;
381
382 if (IntersectRect(&rc, &Ps.rcPaint, &rc))
383 {
384 PatBlt(Dc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, DSTINVERT);
385 }
386 }
387
388 EndPaint (hWnd, &Ps);
389 LeaveCriticalSection(&GuiData->Lock);
390 }
391 else
392 {
393 Dc = BeginPaint (hWnd, &Ps);
394 EndPaint (hWnd, &Ps);
395 }
396 }
397
398 static VOID FASTCALL
399 GuiConsoleHandleKey(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
400 {
401 PCSRSS_CONSOLE Console;
402 PGUI_CONSOLE_DATA GuiData;
403 MSG Message;
404
405 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
406 Message.hwnd = hWnd;
407 Message.message = msg;
408 Message.wParam = wParam;
409 Message.lParam = lParam;
410
411 if(msg == WM_CHAR || msg == WM_SYSKEYDOWN)
412 {
413 /* clear the selection */
414 GuiConsoleUpdateSelection(hWnd, NULL, GuiData);
415 }
416
417 ConioProcessKey(&Message, Console, FALSE);
418 }
419
420 static VOID FASTCALL
421 GuiIntDrawRegion(PGUI_CONSOLE_DATA GuiData, HWND Wnd, RECT *Region)
422 {
423 RECT RegionRect;
424
425 RegionRect.left = Region->left * GuiData->CharWidth;
426 RegionRect.top = Region->top * GuiData->CharHeight;
427 RegionRect.right = (Region->right + 1) * GuiData->CharWidth;
428 RegionRect.bottom = (Region->bottom + 1) * GuiData->CharHeight;
429
430 GuiConsoleUpdateBitmap(Wnd, RegionRect);
431 }
432
433 static VOID STDCALL
434 GuiDrawRegion(PCSRSS_CONSOLE Console, RECT *Region)
435 {
436 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA) Console->PrivateData;
437
438 if (NULL == Console->hWindow || NULL == GuiData)
439 {
440 return;
441 }
442
443 GuiIntDrawRegion(GuiData, Console->hWindow, Region);
444 }
445
446 static VOID FASTCALL
447 GuiInvalidateCell(PGUI_CONSOLE_DATA GuiData, HWND Wnd, UINT x, UINT y)
448 {
449 RECT CellRect;
450
451 CellRect.left = x;
452 CellRect.top = y;
453 CellRect.right = x;
454 CellRect.bottom = y;
455
456 GuiIntDrawRegion(GuiData, Wnd, &CellRect);
457 }
458
459 static VOID STDCALL
460 GuiWriteStream(PCSRSS_CONSOLE Console, RECT *Region, LONG CursorStartX, LONG CursorStartY,
461 UINT ScrolledLines, CHAR *Buffer, UINT Length)
462 {
463 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA) Console->PrivateData;
464 PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer;
465 LONG CursorEndX, CursorEndY;
466 RECT Source, Dest;
467
468 if (NULL == Console->hWindow || NULL == GuiData)
469 {
470 return;
471 }
472
473 if (0 != ScrolledLines)
474 {
475 Source.left = 0;
476 Source.top = ScrolledLines;
477 Source.right = Console->Size.X - 1;
478 Source.bottom = ScrolledLines + Region->top - 1;
479 Dest.left = 0;
480 Dest.top = 0;
481 Dest.right = Console->Size.X - 1;
482 Dest.bottom = Region->top - 1;
483
484 GuiConsoleCopyRegion(Console->hWindow, &Source, &Dest);
485 }
486
487 GuiIntDrawRegion(GuiData, Console->hWindow, Region);
488
489 if (CursorStartX < Region->left || Region->right < CursorStartX
490 || CursorStartY < Region->top || Region->bottom < CursorStartY)
491 {
492 GuiInvalidateCell(GuiData, Console->hWindow, CursorStartX, CursorStartY);
493 }
494
495 ConioPhysicalToLogical(Buff, Buff->CurrentX, Buff->CurrentY,
496 &CursorEndX, &CursorEndY);
497 if ((CursorEndX < Region->left || Region->right < CursorEndX
498 || CursorEndY < Region->top || Region->bottom < CursorEndY)
499 && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
500 {
501 GuiInvalidateCell(GuiData, Console->hWindow, CursorEndX, CursorEndY);
502 }
503 }
504
505 static BOOL STDCALL
506 GuiSetCursorInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff)
507 {
508 RECT UpdateRect;
509
510 if (Console->ActiveBuffer == Buff)
511 {
512 ConioPhysicalToLogical(Buff, Buff->CurrentX, Buff->CurrentY,
513 &UpdateRect.left, &UpdateRect.top);
514 UpdateRect.right = UpdateRect.left;
515 UpdateRect.bottom = UpdateRect.top;
516 ConioDrawRegion(Console, &UpdateRect);
517 }
518
519 return TRUE;
520 }
521
522 static BOOL STDCALL
523 GuiSetScreenInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff, UINT OldCursorX, UINT OldCursorY)
524 {
525 RECT UpdateRect;
526
527 if (Console->ActiveBuffer == Buff)
528 {
529 /* Redraw char at old position (removes cursor) */
530 UpdateRect.left = OldCursorX;
531 UpdateRect.top = OldCursorY;
532 UpdateRect.right = OldCursorX;
533 UpdateRect.bottom = OldCursorY;
534 ConioDrawRegion(Console, &UpdateRect);
535 /* Redraw char at new position (shows cursor) */
536 ConioPhysicalToLogical(Buff, Buff->CurrentX, Buff->CurrentY,
537 &(UpdateRect.left), &(UpdateRect.top));
538 UpdateRect.right = UpdateRect.left;
539 UpdateRect.bottom = UpdateRect.top;
540 ConioDrawRegion(Console, &UpdateRect);
541 }
542
543 return TRUE;
544 }
545
546 static VOID FASTCALL
547 GuiConsoleHandleTimer(HWND hWnd)
548 {
549 PCSRSS_CONSOLE Console;
550 PGUI_CONSOLE_DATA GuiData;
551 RECT CursorRect;
552 ULONG CursorX, CursorY;
553
554 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
555 GuiData->CursorBlinkOn = ! GuiData->CursorBlinkOn;
556
557 GuiConsoleGetLogicalCursorPos(Console->ActiveBuffer, &CursorX, &CursorY);
558 CursorRect.left = CursorX;
559 CursorRect.top = CursorY;
560 CursorRect.right = CursorX;
561 CursorRect.bottom = CursorY;
562 GuiDrawRegion(Console, &CursorRect);
563 }
564
565 static VOID FASTCALL
566 GuiConsoleHandleClose(HWND hWnd)
567 {
568 PCSRSS_CONSOLE Console;
569 PGUI_CONSOLE_DATA GuiData;
570 PLIST_ENTRY current_entry;
571 PCSRSS_PROCESS_DATA current;
572
573 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
574
575 EnterCriticalSection(&Console->Header.Lock);
576
577 current_entry = Console->ProcessList.Flink;
578 while (current_entry != &Console->ProcessList)
579 {
580 current = CONTAINING_RECORD(current_entry, CSRSS_PROCESS_DATA, ProcessEntry);
581 current_entry = current_entry->Flink;
582
583 ConioConsoleCtrlEvent(CTRL_CLOSE_EVENT, current);
584 }
585
586 LeaveCriticalSection(&Console->Header.Lock);
587 }
588
589 static VOID FASTCALL
590 GuiConsoleHandleNcDestroy(HWND hWnd)
591 {
592 PCSRSS_CONSOLE Console;
593 PGUI_CONSOLE_DATA GuiData;
594
595 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
596 KillTimer(hWnd, 1);
597 Console->PrivateData = NULL;
598 DeleteDC(GuiData->MemoryDC);
599 DeleteCriticalSection(&GuiData->Lock);
600 HeapFree(Win32CsrApiHeap, 0, GuiData);
601 }
602
603 static VOID FASTCALL
604 GuiConsoleLeftMouseDown(HWND hWnd, LPARAM lParam)
605 {
606 PCSRSS_CONSOLE Console;
607 PGUI_CONSOLE_DATA GuiData;
608 POINTS pt;
609 RECT rc;
610
611 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
612 if (Console == NULL || GuiData == NULL) return;
613
614 pt = MAKEPOINTS(lParam);
615
616 rc.left = pt.x / GuiData->CharWidth;
617 rc.top = pt.y / GuiData->CharHeight;
618 rc.right = rc.left + 1;
619 rc.bottom = rc.top + 1;
620
621 GuiData->SelectionStart.x = rc.left;
622 GuiData->SelectionStart.y = rc.top;
623
624 SetCapture(hWnd);
625
626 GuiData->MouseDown = TRUE;
627
628 GuiConsoleUpdateSelection(hWnd, &rc, GuiData);
629 }
630
631 static VOID FASTCALL
632 GuiConsoleLeftMouseUp(HWND hWnd, LPARAM lParam)
633 {
634 PCSRSS_CONSOLE Console;
635 PGUI_CONSOLE_DATA GuiData;
636 RECT rc;
637 POINTS pt;
638
639 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
640 if (Console == NULL || GuiData == NULL) return;
641 if (GuiData->Selection.left == -1 || !GuiData->MouseDown) return;
642
643 pt = MAKEPOINTS(lParam);
644
645 rc.left = GuiData->SelectionStart.x;
646 rc.top = GuiData->SelectionStart.y;
647 rc.right = (pt.x >= 0 ? (pt.x / GuiData->CharWidth) + 1 : 0);
648 rc.bottom = (pt.y >= 0 ? (pt.y / GuiData->CharHeight) + 1 : 0);
649
650 /* exchange left/top with right/bottom if required */
651 if(rc.left >= rc.right)
652 {
653 LONG tmp;
654 tmp = rc.left;
655 rc.left = max(rc.right - 1, 0);
656 rc.right = tmp + 1;
657 }
658 if(rc.top >= rc.bottom)
659 {
660 LONG tmp;
661 tmp = rc.top;
662 rc.top = max(rc.bottom - 1, 0);
663 rc.bottom = tmp + 1;
664 }
665
666 GuiData->MouseDown = FALSE;
667
668 GuiConsoleUpdateSelection(hWnd, &rc, GuiData);
669
670 ReleaseCapture();
671 }
672
673 static VOID FASTCALL
674 GuiConsoleMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
675 {
676 PCSRSS_CONSOLE Console;
677 PGUI_CONSOLE_DATA GuiData;
678 RECT rc;
679 POINTS pt;
680
681 if (!(wParam & MK_LBUTTON)) return;
682
683 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
684 if (Console == NULL || GuiData == NULL || !GuiData->MouseDown) return;
685
686 pt = MAKEPOINTS(lParam);
687
688 rc.left = GuiData->SelectionStart.x;
689 rc.top = GuiData->SelectionStart.y;
690 rc.right = (pt.x >= 0 ? (pt.x / GuiData->CharWidth) + 1 : 0);
691 rc.bottom = (pt.y >= 0 ? (pt.y / GuiData->CharHeight) + 1 : 0);
692
693 /* exchange left/top with right/bottom if required */
694 if(rc.left >= rc.right)
695 {
696 LONG tmp;
697 tmp = rc.left;
698 rc.left = max(rc.right - 1, 0);
699 rc.right = tmp + 1;
700 }
701 if(rc.top >= rc.bottom)
702 {
703 LONG tmp;
704 tmp = rc.top;
705 rc.top = max(rc.bottom - 1, 0);
706 rc.bottom = tmp + 1;
707 }
708
709 GuiConsoleUpdateSelection(hWnd, &rc, GuiData);
710 }
711
712 static VOID FASTCALL
713 GuiConsoleRightMouseDown(HWND hWnd)
714 {
715 PCSRSS_CONSOLE Console;
716 PGUI_CONSOLE_DATA GuiData;
717
718 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
719 if (Console == NULL || GuiData == NULL) return;
720
721 if (GuiData->Selection.left == -1)
722 {
723 /* FIXME - paste text from clipboard */
724 }
725 else
726 {
727 /* FIXME - copy selection to clipboard */
728
729 GuiConsoleUpdateSelection(hWnd, NULL, GuiData);
730 }
731
732 }
733
734 static LRESULT CALLBACK
735 GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
736 {
737 LRESULT Result = 0;
738
739 switch(msg)
740 {
741 case WM_NCCREATE:
742 Result = (LRESULT) GuiConsoleHandleNcCreate(hWnd, (CREATESTRUCTW *) lParam);
743 break;
744 case WM_PAINT:
745 GuiConsoleHandlePaint(hWnd);
746 break;
747 case WM_KEYDOWN:
748 case WM_KEYUP:
749 case WM_SYSKEYDOWN:
750 case WM_SYSKEYUP:
751 case WM_CHAR:
752 GuiConsoleHandleKey(hWnd, msg, wParam, lParam);
753 break;
754 case WM_TIMER:
755 GuiConsoleHandleTimer(hWnd);
756 break;
757 case WM_CLOSE:
758 GuiConsoleHandleClose(hWnd);
759 break;
760 case WM_NCDESTROY:
761 GuiConsoleHandleNcDestroy(hWnd);
762 break;
763 case WM_LBUTTONDOWN:
764 GuiConsoleLeftMouseDown(hWnd, lParam);
765 break;
766 case WM_LBUTTONUP:
767 GuiConsoleLeftMouseUp(hWnd, lParam);
768 break;
769 case WM_RBUTTONDOWN:
770 GuiConsoleRightMouseDown(hWnd);
771 break;
772 case WM_MOUSEMOVE:
773 GuiConsoleMouseMove(hWnd, wParam, lParam);
774 break;
775 default:
776 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
777 break;
778 }
779
780 return Result;
781 }
782
783 static LRESULT CALLBACK
784 GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
785 {
786 HWND NewWindow;
787 LONG WindowCount;
788 MSG Msg;
789 PWCHAR Buffer, Title;
790 PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) lParam;
791
792 switch(msg)
793 {
794 case WM_CREATE:
795 SetWindowLongW(hWnd, GWL_USERDATA, 0);
796 return 0;
797 case PM_CREATE_CONSOLE:
798 Buffer = HeapAlloc(Win32CsrApiHeap, 0,
799 Console->Title.Length + sizeof(WCHAR));
800 if (NULL != Buffer)
801 {
802 memcpy(Buffer, Console->Title.Buffer, Console->Title.Length);
803 Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
804 Title = Buffer;
805 }
806 else
807 {
808 Title = L"";
809 }
810 NewWindow = CreateWindowW(L"ConsoleWindowClass",
811 Title,
812 WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
813 CW_USEDEFAULT,
814 CW_USEDEFAULT,
815 CW_USEDEFAULT,
816 CW_USEDEFAULT,
817 NULL,
818 NULL,
819 (HINSTANCE) GetModuleHandleW(NULL),
820 (PVOID) Console);
821 if (NULL != Buffer)
822 {
823 HeapFree(Win32CsrApiHeap, 0, Buffer);
824 }
825 Console->hWindow = NewWindow;
826 if (NULL != NewWindow)
827 {
828 SetWindowLongW(hWnd, GWL_USERDATA, GetWindowLongW(hWnd, GWL_USERDATA) + 1);
829 ShowWindow(NewWindow, SW_SHOW);
830 }
831 return (LRESULT) NewWindow;
832 case PM_DESTROY_CONSOLE:
833 /* Window creation is done using a PostMessage(), so it's possible that the
834 * window that we want to destroy doesn't exist yet. So first empty the message
835 * queue */
836 while(PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
837 {
838 TranslateMessage(&Msg);
839 DispatchMessageW(&Msg);
840 }
841 DestroyWindow(Console->hWindow);
842 Console->hWindow = NULL;
843 WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
844 WindowCount--;
845 SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
846 if (0 == WindowCount)
847 {
848 NotifyWnd = NULL;
849 DestroyWindow(hWnd);
850 PrivateCsrssManualGuiCheck(-1);
851 PostQuitMessage(0);
852 }
853 return 0;
854 default:
855 return DefWindowProcW(hWnd, msg, wParam, lParam);
856 }
857 }
858
859 static DWORD STDCALL
860 GuiConsoleGuiThread(PVOID Data)
861 {
862 MSG msg;
863 PHANDLE GraphicsStartupEvent = (PHANDLE) Data;
864
865 NotifyWnd = CreateWindowW(L"Win32CsrCreateNotify",
866 L"",
867 WS_OVERLAPPEDWINDOW,
868 CW_USEDEFAULT,
869 CW_USEDEFAULT,
870 CW_USEDEFAULT,
871 CW_USEDEFAULT,
872 NULL,
873 NULL,
874 (HINSTANCE) GetModuleHandleW(NULL),
875 NULL);
876 if (NULL == NotifyWnd)
877 {
878 PrivateCsrssManualGuiCheck(-1);
879 SetEvent(*GraphicsStartupEvent);
880 return 1;
881 }
882
883 SetEvent(*GraphicsStartupEvent);
884
885 while(GetMessageW(&msg, NULL, 0, 0))
886 {
887 TranslateMessage(&msg);
888 DispatchMessageW(&msg);
889 }
890
891 return 1;
892 }
893
894 static BOOL FASTCALL
895 GuiInit(VOID)
896 {
897 WNDCLASSEXW wc;
898
899 if (NULL == NotifyWnd)
900 {
901 PrivateCsrssManualGuiCheck(+1);
902 }
903
904 wc.cbSize = sizeof(WNDCLASSEXW);
905 wc.lpszClassName = L"Win32CsrCreateNotify";
906 wc.lpfnWndProc = GuiConsoleNotifyWndProc;
907 wc.style = 0;
908 wc.hInstance = (HINSTANCE) GetModuleHandleW(NULL);
909 wc.hIcon = NULL;
910 wc.hCursor = NULL;
911 wc.hbrBackground = NULL;
912 wc.lpszMenuName = NULL;
913 wc.cbClsExtra = 0;
914 wc.cbWndExtra = 0;
915 wc.hIconSm = NULL;
916 if (RegisterClassExW(&wc) == 0)
917 {
918 DPRINT1("Failed to register notify wndproc\n");
919 return FALSE;
920 }
921
922 wc.cbSize = sizeof(WNDCLASSEXW);
923 wc.lpszClassName = L"ConsoleWindowClass";
924 wc.lpfnWndProc = GuiConsoleWndProc;
925 wc.style = 0;
926 wc.hInstance = (HINSTANCE) GetModuleHandleW(NULL);
927 wc.hIcon = LoadIconW(Win32CsrDllHandle, MAKEINTRESOURCEW(1));
928 wc.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
929 wc.hbrBackground = NULL;
930 wc.lpszMenuName = NULL;
931 wc.cbClsExtra = 0;
932 wc.cbWndExtra = 0;
933 wc.hIconSm = LoadImageW(Win32CsrDllHandle, MAKEINTRESOURCEW(1), IMAGE_ICON,
934 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
935 LR_SHARED);
936 if (RegisterClassExW(&wc) == 0)
937 {
938 DPRINT1("Failed to register console wndproc\n");
939 return FALSE;
940 }
941
942 return TRUE;
943 }
944
945 static VOID STDCALL
946 GuiInitScreenBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buffer)
947 {
948 Buffer->DefaultAttrib = 0x0f;
949 }
950
951 STATIC BOOL STDCALL
952 GuiChangeTitle(PCSRSS_CONSOLE Console)
953 {
954 PWCHAR Buffer, Title;
955
956 Buffer = HeapAlloc(Win32CsrApiHeap, 0,
957 Console->Title.Length + sizeof(WCHAR));
958 if (NULL != Buffer)
959 {
960 memcpy(Buffer, Console->Title.Buffer, Console->Title.Length);
961 Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
962 Title = Buffer;
963 }
964 else
965 {
966 Title = L"";
967 }
968 SendMessageW(Console->hWindow, WM_SETTEXT, 0, (LPARAM) Title);
969 if (NULL != Buffer)
970 {
971 HeapFree(Win32CsrApiHeap, 0, Buffer);
972 }
973
974 return TRUE;
975 }
976
977 STATIC BOOL STDCALL
978 GuiChangeIcon(PCSRSS_CONSOLE Console)
979 {
980 SendMessageW(Console->hWindow, WM_SETICON, ICON_BIG, (LPARAM)Console->hWindowIcon);
981 SendMessageW(Console->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)Console->hWindowIcon);
982
983 return TRUE;
984 }
985
986 static VOID STDCALL
987 GuiCleanupConsole(PCSRSS_CONSOLE Console)
988 {
989 SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM) Console);
990 }
991
992 static CSRSS_CONSOLE_VTBL GuiVtbl =
993 {
994 GuiInitScreenBuffer,
995 GuiWriteStream,
996 GuiDrawRegion,
997 GuiSetCursorInfo,
998 GuiSetScreenInfo,
999 GuiChangeTitle,
1000 GuiCleanupConsole,
1001 GuiChangeIcon
1002 };
1003
1004 NTSTATUS FASTCALL
1005 GuiInitConsole(PCSRSS_CONSOLE Console)
1006 {
1007 HANDLE GraphicsStartupEvent;
1008 HANDLE ThreadHandle;
1009
1010 if (! ConsInitialized)
1011 {
1012 ConsInitialized = TRUE;
1013 if (! GuiInit())
1014 {
1015 ConsInitialized = FALSE;
1016 return STATUS_UNSUCCESSFUL;
1017 }
1018 }
1019
1020 Console->Vtbl = &GuiVtbl;
1021 Console->Size.X = 80;
1022 Console->Size.Y = 25;
1023 if (NULL == NotifyWnd)
1024 {
1025 GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1026 if (NULL == GraphicsStartupEvent)
1027 {
1028 return STATUS_UNSUCCESSFUL;
1029 }
1030
1031 ThreadHandle = CreateThread(NULL,
1032 0,
1033 GuiConsoleGuiThread,
1034 (PVOID) &GraphicsStartupEvent,
1035 0,
1036 NULL);
1037 if (NULL == ThreadHandle)
1038 {
1039 NtClose(GraphicsStartupEvent);
1040 DPRINT1("Win32Csr: Failed to create graphics console thread. Expect problems\n");
1041 return STATUS_UNSUCCESSFUL;
1042 }
1043 SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST);
1044 CloseHandle(ThreadHandle);
1045
1046 WaitForSingleObject(GraphicsStartupEvent, INFINITE);
1047 CloseHandle(GraphicsStartupEvent);
1048
1049 if (NULL == NotifyWnd)
1050 {
1051 DPRINT1("Win32Csr: Failed to create notification window.\n");
1052 return STATUS_UNSUCCESSFUL;
1053 }
1054 }
1055
1056 PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, 0, (LPARAM) Console);
1057
1058 return STATUS_SUCCESS;
1059 }
1060
1061 VOID STDCALL
1062 GuiConsoleCopyRegion(HWND hWnd,
1063 RECT *Source,
1064 RECT *Dest)
1065 {
1066 RECT ScrollRect;
1067 PGUI_CONSOLE_DATA GuiData;
1068 PCSRSS_CONSOLE Console;
1069
1070
1071 GuiConsoleGetDataPointers(hWnd, &Console, &GuiData);
1072
1073 ScrollRect.left = Dest->left * GuiData->CharWidth;
1074 ScrollRect.right = (Dest->right + 1) * GuiData->CharWidth;
1075 ScrollRect.top = Dest->top * GuiData->CharHeight;
1076 ScrollRect.bottom = (Dest->bottom + 1) * GuiData->CharHeight;
1077 EnterCriticalSection(&GuiData->Lock);
1078 BitBlt(GuiData->MemoryDC, ScrollRect.left, ScrollRect.top,
1079 ScrollRect.right - ScrollRect.left, ScrollRect.bottom - ScrollRect.top,
1080 GuiData->MemoryDC, Source->left * GuiData->CharWidth, Source->top * GuiData->CharHeight, SRCCOPY);
1081
1082 LeaveCriticalSection(&GuiData->Lock);
1083
1084 InvalidateRect(hWnd, &ScrollRect, FALSE);
1085 }
1086
1087 /* EOF */