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