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