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