1: /*
2: ============================================================================
3: xoblite™ -> an advanced "extended shell" for Microsoft® Windows® 11
4: Copyright © 2002-2026 Karl Henrik Henriksson [qwilk/@xoblite]
5: Copyright © 2001-2004 The Blackbox for Windows Development Team
6: http://xoblite.net/ + https://github.com/xoblite/
7: ============================================================================
8:
9: Blackbox for Windows is free software, released under the
10: GNU General Public License (GPL version 2 or later), with an extension
11: that allows linking of proprietary modules under a controlled interface.
12: What this means is that plugins etc. are allowed to be released
13: under any license the author wishes. Please note, however, that the
14: original Blackbox gradient math code used in Blackbox for Windows
15: is available under the BSD license.
16:
17: http://www.fsf.org/licenses/gpl.html
18: http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
19: http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
20:
21: This program is distributed in the hope that it will be useful,
22: but WITHOUT ANY WARRANTY; without even the implied warranty of
23: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24: GNU General Public License for more details.
25:
26: For additional license information, please read the included license.html.
27:
28: ============================================================================
29: */
30:
31: #include "Console.h"
32:
33: const char szConsoleName[] = "BBConsole"; // Window class etc.
34:
35: extern Console *pConsole;
36: extern Settings *pSettings;
37: extern BImage *pBImage;
38: extern Toolbar *pToolbar;
39: extern Desktop *pDesktop;
40:
41: int consoleMessageSubscription[] = { BB_TOGGLECONSOLE, BB_CONSOLEMESSAGE, BB_RECONFIGURE, 0 };
42:
43: //===========================================================================
44:
45: Console::Console(HINSTANCE hInstance)
46: {
47: hConsoleWnd = NULL;
48: hConsoleInstance = hInstance;
49: hBlackboxWnd = GetBBWnd();
50:
51: cachedBackground = CreateCompatibleDC(NULL);
52:
53: ZeroMemory(&messageList, sizeof(messageList));
54: messageToBlock[0] = '\0';
55:
56: fullscreenDetected = false;
57:
58: //====================
59:
60: // Get size and position for our window...
61: GetDimensions();
62:
63: //====================
64:
65: // Register window class...
66: WNDCLASS wc;
67: ZeroMemory(&wc,sizeof(wc));
68: wc.hInstance = hConsoleInstance; // hInstance
69: wc.lpfnWndProc = ConsoleWndProc; // window procedure
70: wc.lpszClassName = szConsoleName; // window class name
71: wc.hbrBackground = NULL; // no class background brush
72: wc.style = CS_DBLCLKS; // class styles (e.g. accept doubleclicks)
73: wc.hCursor = LoadCursor(NULL, IDC_ARROW); // always display the regular arrow cursor
74:
75: if (!RegisterClass(&wc))
76: {
77: MessageBox(0, "Error registering console window class!", szConsoleName, MB_OK | MB_ICONERROR | MB_TOPMOST);
78: Log("Console", "Error registering window class!");
79: return;
80: }
81:
82: // Create console window...
83: hConsoleWnd = CreateWindowEx(
84: WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES | WS_EX_NOACTIVATE | WS_EX_LAYERED, // window style
85: szConsoleName, // window class
86: NULL, // window name
87: WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // window parameters
88: ConsoleX, // x position
89: ConsoleY, // y position
90: ConsoleWidth, // window width
91: ConsoleHeight, // window height
92: NULL, //pDesktop->hDesktopWnd, // owner window
93: NULL, // no menu
94: hConsoleInstance, // hInstance
95: NULL // no window creation data
96: );
97:
98: if (!hConsoleWnd)
99: {
100: UnregisterClass(szConsoleName,hConsoleInstance); // unregister window class
101: MessageBox(0, "Error creating console window!", szConsoleName, MB_OK | MB_ICONERROR | MB_TOPMOST);
102: Log("Console", "Error creating window!");
103: return;
104: }
105:
106: //====================
107:
108: // Subscribe to Blackbox messages applicable to the console...
109: SendMessage(hBlackboxWnd, BB_REGISTERMESSAGE, (WPARAM)hConsoleWnd, (LPARAM)consoleMessageSubscription);
110:
111: // Make the console window sticky...
112: MakeSticky(hConsoleWnd);
113: // Set console window z-order position to be right above the desktop...
114: SetWindowPos(hConsoleWnd, pDesktop->hDesktopWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
115:
116: // Update the console window...
117: ShowWindow(hConsoleWnd, pSettings->consoleHidden ? SW_HIDE : SW_SHOWNOACTIVATE);
118: UpdateConsoleWindow();
119: }
120:
121: //===========================================================================
122:
123: Console::~Console()
124: {
125: // Unsubscribe to previously subscribed Blackbox messages...
126: SendMessage(hBlackboxWnd, BB_UNREGISTERMESSAGE, (WPARAM)hConsoleWnd, (LPARAM)consoleMessageSubscription);
127:
128: if (hConsoleWnd) DestroyWindow(hConsoleWnd); // Destroy the console window...
129: UnregisterClass(szConsoleName, hConsoleInstance); // Unregister the console window class...
130:
131: if (cachedBackground) DeleteDC(cachedBackground); // Delete the cached gradient...
132:
133: ClearHistory(false); // Clear all elements in the messageList...
134: }
135:
136: //===========================================================================
137:
138: LRESULT CALLBACK ConsoleWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
139: {
140: switch (message)
141: {
142: //====================
143:
144: case BB_CONSOLEMESSAGE:
145: {
146: if (strnlen_s(pConsole->messageToBlock, sizeofArray(pConsole->messageToBlock)) > 0)
147: {
148: if (!_stricmp((LPCSTR)lParam, pConsole->messageToBlock))
149: {
150: // This message (typically a bro@m part of an @Script) should not be echoed in the console...
151: pConsole->messageToBlock[0] = '\0';
152: break;
153: }
154: }
155:
156: //====================
157:
158: if ((int)pConsole->messageList.size() >= 50)
159: {
160: // We already have the maximum 50 messages in the list, remove the oldest...
161: if (pConsole->messageList[0]->icon != NULL) DeleteObject(&pConsole->messageList[0]->icon);
162: delete pConsole->messageList[0];
163: pConsole->messageList.erase(pConsole->messageList.begin());
164: }
165:
166: //====================
167:
168: messageListItem *newMessage = new messageListItem;
169:
170: newMessage->type = (int)wParam;
171:
172: //====================
173:
174: if (newMessage->type == CONSOLE_INFORMATION_MESSAGE) newMessage->icon = LoadIcon(NULL, IDI_INFORMATION);
175: else if (newMessage->type == CONSOLE_WARNING_MESSAGE) newMessage->icon = LoadIcon(NULL, IDI_WARNING);
176: else if (newMessage->type == CONSOLE_ERROR_MESSAGE) newMessage->icon = LoadIcon(NULL, IDI_ERROR);
177: else if (newMessage->type == CONSOLE_SHELL_MESSAGE) newMessage->icon = LoadIcon(pConsole->hConsoleInstance, MAKEINTRESOURCE(IDI_XOBLITE));
178: else newMessage->icon = NULL; // CONSOLE_REGULAR_MESSAGE, CONSOLE_PLAIN_MESSAGE, CONSOLE_INDENTED_MESSAGE, CONSOLE_SEPARATOR
179:
180: //====================
181:
182: if (newMessage->type != CONSOLE_SEPARATOR)
183: {
184: char message[MAX_LINE_LENGTH];
185: if (newMessage->type == CONSOLE_REGULAR_MESSAGE)
186: {
187: // Add a timestamp to regular messages...
188: _strtime(message);
189: strncat_s(message, sizeof(message), " -> ", _TRUNCATE);
190: strncat_s(message, sizeof(message), (LPCSTR)lParam, _TRUNCATE);
191: }
192: else strncpy_s(message, sizeof(message), (LPCSTR)lParam, _TRUNCATE);
193: message[sizeof(message)-1] = '\0'; // Failsafe
194:
195: // Replace any \r\n in the string with a single space character...
196: LPSTR ptr;
197: while (ptr = strchr(message, '\r'))
198: {
199: // int nLen = strlen(ptr);
200: // memmove(ptr+3, ptr, nLen);
201: // strncpy(ptr, " -> ", 4);
202: ptr[0] = ' ';
203: }
204: while (ptr = strchr(message, '\n'))
205: {
206: int nLen = strlen(ptr);
207: memmove(ptr, ptr+1, nLen);
208: }
209:
210: // strncpy(newMessage->msg, message, sizeof(newMessage->msg));
211: strncpy_s(newMessage->msg, sizeof(newMessage->msg), message, _TRUNCATE);
212: newMessage->msg[sizeof(newMessage->msg)-1] = '\0';
213:
214: //====================
215:
216: // Detect any URL in the message and if found make it clickable...
217: char* url = StrStrI(newMessage->msg, "http://");
218: if (url == NULL) url = StrStrI(newMessage->msg, "https://");
219: if (url == NULL) url = StrStrI(newMessage->msg, "file://");
220: if (url == NULL) newMessage->url[0] = '\0';
221: else
222: {
223: strncpy_s(newMessage->url, sizeof(newMessage->url), url, _TRUNCATE);
224: newMessage->url[sizeof(newMessage->url)-1] = '\0';
225:
226: // Check whether the URL is part of an @Script, and if so perform some additional operations on the two strings... (URL+message)
227: if (IsInString(newMessage->msg, "@Script"))
228: {
229: char* first = newMessage->url;
230: char* pipe = StrStrI(newMessage->url, "|");
231: if (pipe != NULL) newMessage->url[pipe-first] = '\0';
232: else if (newMessage->url[strnlen_s(newMessage->url, sizeofArray(newMessage->url))-1] == ']') newMessage->url[strnlen_s(newMessage->url, sizeofArray(newMessage->url))-1] = '\0';
233:
234: char updatedMsg[350];
235: updatedMsg[sizeof(updatedMsg) - 1] = '\0';
236: int n = url - newMessage->msg;
237: strncpy_s(updatedMsg, sizeofArray(updatedMsg), newMessage->msg, n);
238: strcat_s(updatedMsg, sizeofArray(updatedMsg), " "); // ...to avoid the later URL highlighting covering the character immediately *before* the URL...
239: strcat_s(updatedMsg, sizeofArray(updatedMsg), newMessage->url);
240: strcat_s(updatedMsg, sizeofArray(updatedMsg), " "); // ...to avoid the later URL highlighting covering the character immediately *after* the URL...
241: strcat_s(updatedMsg, sizeofArray(updatedMsg), &url[strnlen_s(newMessage->url, sizeofArray(newMessage->url))]);
242: strncpy_s(newMessage->msg, sizeof(newMessage->msg), updatedMsg, _TRUNCATE);
243: newMessage->msg[sizeof(newMessage->msg)-1] = '\0';
244: }
245:
246: // If not part of an @Script, we remove everything else after the URL (using blank space as delimiter) including any ending parantheses and commas...
247: else if (strchr(newMessage->url, ' '))
248: {
249: char temp[sizeof(newMessage->url)];
250: Tokenize(newMessage->url, temp, " ");
251: strcpy_s(newMessage->url, sizeofArray(newMessage->url), temp);
252: switch(newMessage->url[strnlen_s(newMessage->url, sizeofArray(newMessage->url))-1])
253: {
254: case ')':
255: case '>':
256: case ',':
257: {
258: newMessage->url[strnlen_s(newMessage->url, sizeofArray(newMessage->url))-1] = '\0';
259: }
260: }
261: }
262: }
263: }
264:
265: //====================
266:
267: pConsole->messageList.push_back(newMessage);
268:
269: //====================
270:
271: if (!pSettings->consoleHidden)
272: {
273: pConsole->UpdateConsoleWindow();
274:
275: /*
276: if (!SetTimer(pConsole->hConsoleWnd, CONSOLE_SETLABEL_TIMER, 4000, (TIMERPROC)NULL))
277: {
278: MessageBox(0, "Error creating console timer!", szConsoleName, MB_OK | MB_ICONERROR | MB_TOPMOST);
279: Log("Could not create console timer!", NULL);
280: return 0;
281: }
282: */
283: }
284: }
285: break;
286:
287: //===========================================================================
288: /*
289: case WM_TIMER:
290: {
291: if (wParam == CONSOLE_SETLABEL_TIMER)
292: {
293: KillTimer(pConsole->hConsoleWnd, CONSOLE_SETLABEL_TIMER);
294: if (pSettings->consoleHidden) ShowWindow(pConsole->hConsoleWnd, SW_HIDE);
295: }
296:
297: return 0;
298: }
299: break;
300: */
301: //====================
302:
303: case BB_RECONFIGURE:
304: case WM_DISPLAYCHANGE:
305: {
306: pConsole->UpdatePosition();
307: }
308: break;
309:
310: case WM_SETTINGCHANGE:
311: {
312: // Update the console's position (and maybe also dimensions) if the work area changes...
313: // (e.g. if the Explorer taskbar is moved to another screen edge)
314: if (wParam == SPI_SETWORKAREA) pConsole->UpdatePosition();
315: return 0;
316: }
317:
318: //====================
319:
320: case BB_TOGGLECONSOLE:
321: {
322: if (pSettings->consoleHidden)
323: {
324: // Show window and force update...
325: pSettings->consoleHidden = false;
326: ShowWindow(pConsole->hConsoleWnd, SW_SHOWNOACTIVATE);
327: pConsole->UpdateConsoleWindow();
328: }
329: else
330: {
331: // Hide window...
332: pSettings->consoleHidden = true;
333: ShowWindow(pConsole->hConsoleWnd, SW_HIDE);
334: }
335:
336: PlaySoundFX(SFX_TOGGLE_ELEMENT);
337:
338: WriteBool(pSettings->xobrcFile, "xoblite.console.hidden:", pSettings->consoleHidden);
339: }
340: break;
341:
342: //====================
343: /*
344: case WM_WINDOWPOSCHANGING:
345: {
346: // ##### FOR SOME REASON THIS WILL CRASH THE SHELL...? #####
347: if (pDesktop) SetWindowPos(pConsole->hConsoleWnd, pDesktop->hDesktopWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
348: return 0;
349: }
350: */
351: //====================
352:
353: case WM_GETMINMAXINFO:
354: {
355: MINMAXINFO *mmi = (MINMAXINFO*)lParam;
356: mmi->ptMaxSize.y = pConsole->ConsoleHeight;
357: return 0;
358: }
359:
360: //====================
361:
362: case WM_CLOSE:
363: return 0;
364:
365: //====================
366:
367: case WM_MOUSEACTIVATE:
368: return MA_NOACTIVATE;
369:
370: // case WM_ACTIVATE:
371: // if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) SetActiveWindow(hwnd);
372: // return 0;
373:
374: //====================
375:
376: case WM_NCHITTEST:
377: {
378: if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) return HTCAPTION;
379: else return HTCLIENT;
380: }
381: break;
382:
383: //====================
384:
385: case WM_LBUTTONDBLCLK:
386: {
387: // Check which message was clicked...
388: POINT pt;
389: pt.x = LOWORD(lParam);
390: pt.y = HIWORD(lParam);
391:
392: int i = 0;
393: for (i; i < (int)pConsole->messageList.size(); i++)
394: {
395: if (PtInRect(&pConsole->messageList[i]->r, pt)) break;
396: }
397: if (i == (int)pConsole->messageList.size()) break; // No message clicked
398:
399: //====================
400:
401: if (strnlen_s(pConsole->messageList[i]->url, sizeofArray(pConsole->messageList[i]->url)) > 0) // Message including a clickable URL...
402: {
403: BBExecute(GetDesktopWindow(), NULL, pConsole->messageList[i]->url, NULL, NULL, SW_SHOWNORMAL, true);
404: }
405: else
406: {
407: // Relay all mouse actions and drag'n'dropped files to the desktop...
408: // SendMessage(pDesktop->hDesktopWnd, message, wParam, lParam);
409: }
410: }
411: break;
412:
413:
414: //====================
415:
416: case WM_LBUTTONDOWN:
417: case WM_LBUTTONUP:
418: case WM_RBUTTONDOWN:
419: case WM_RBUTTONUP:
420: case WM_MBUTTONDOWN:
421: case WM_MBUTTONUP:
422: case WM_XBUTTONDOWN:
423: case WM_XBUTTONUP:
424: case WM_MOUSEWHEEL:
425: case WM_MOUSEHWHEEL:
426: case WM_DROPFILES:
427: {
428: if (message == WM_LBUTTONUP)
429: {
430: POINT pt;
431: pt.x = LOWORD(lParam);
432: pt.y = HIWORD(lParam);
433:
434: // Did the user click the maximize/restore button?
435: if (PtInRect(&pConsole->consoleZoomButtonRect, pt))
436: {
437: pConsole->ToggleMaximized();
438: break;
439: }
440: }
441:
442: // If not, we relay all mouse actions and drag'n'dropped files to the desktop...
443: pDesktop->MouseAndDropHandler(pDesktop->hDesktopWnd, message, wParam, lParam);
444: }
445: break;
446:
447: //====================
448:
449: default:
450: return DefWindowProc(hwnd,message,wParam,lParam);
451:
452: //====================
453: }
454: return 0;
455: }
456:
457: //===========================================================================
458: // Function: UpdateConsoleWindow
459: // Purpose: ...
460: //===========================================================================
461:
462: void Console::UpdateConsoleWindow()
463: {
464: if (fullscreenDetected) return;
465:
466: // Fetch the new size and position parameters for our window...
467: // GetDimensions();
468:
469: RECT r;
470: SetRect(&r, 0, 0, ConsoleWidth, ConsoleHeight);
471:
472: HDC hdc = GetWindowDC(hConsoleWnd);
473: HBITMAP bufbmp = CreateCompatibleBitmap(hdc, r.right-r.left, r.bottom-r.top);
474: DeleteObject(SelectObject(cachedBackground, bufbmp));
475: ReleaseDC(hConsoleWnd, hdc);
476:
477: //====================
478:
479: // Draw console background...
480: HBRUSH brush;
481: if (pSettings->consoleDesktopMode) brush = CreateSolidBrush(0x000000);
482: else brush = CreateSolidBrush(pSettings->Console->Color);
483: FillRect(cachedBackground, &r, brush);
484: DeleteObject(brush);
485:
486: //====================
487:
488: if (!pSettings->consoleDesktopMode)
489: {
490: // Draw maximize/restore button background...
491: brush = CreateSolidBrush(pSettings->Console->PicColor);
492: FillRect(cachedBackground, &consoleZoomButtonRect, brush);
493: DeleteObject(brush);
494:
495: // Draw maximize/restore button glyph...
496: HPEN zoomPen = CreatePen(PS_SOLID, 1, pSettings->Console->Color);
497: HPEN oldPen = (HPEN) SelectObject(cachedBackground, zoomPen);
498:
499: int offsetY = consoleZoomButtonRect.top + 7;
500: int offsetX = consoleZoomButtonRect.left + 7;
501:
502: if (pSettings->consoleMaximized)
503: {
504: MoveToEx(cachedBackground, offsetX-4, offsetY-1, NULL);
505: LineTo(cachedBackground, offsetX+1, offsetY-1);
506: LineTo(cachedBackground, offsetX+1, offsetY+4);
507: LineTo(cachedBackground, offsetX-4, offsetY+4);
508: LineTo(cachedBackground, offsetX-4, offsetY-1);
509: MoveToEx(cachedBackground, offsetX-4, offsetY, NULL);
510: LineTo(cachedBackground, offsetX+1, offsetY);
511:
512: MoveToEx(cachedBackground, offsetX-1, offsetY-4, NULL);
513: LineTo(cachedBackground, offsetX+4, offsetY-4);
514: LineTo(cachedBackground, offsetX+4, offsetY+1);
515: LineTo(cachedBackground, offsetX+1, offsetY+1);
516: MoveToEx(cachedBackground, offsetX-1, offsetY, NULL);
517: LineTo(cachedBackground, offsetX-1, offsetY-4);
518: MoveToEx(cachedBackground, offsetX-1, offsetY-3, NULL);
519: LineTo(cachedBackground, offsetX+4, offsetY-3);
520: }
521: else
522: {
523: MoveToEx(cachedBackground, offsetX-4, offsetY-4, NULL);
524: LineTo(cachedBackground, offsetX+4, offsetY-4);
525: LineTo(cachedBackground, offsetX+4, offsetY+4);
526: LineTo(cachedBackground, offsetX-4, offsetY+4);
527: LineTo(cachedBackground, offsetX-4, offsetY-4);
528: MoveToEx(cachedBackground, offsetX-4, offsetY-3, NULL);
529: LineTo(cachedBackground, offsetX+4, offsetY-3);
530: }
531:
532: // if (pSettings->doubleScaleHiDPI) StretchBlt(cachedBackground, consoleZoomButtonRect.left, consoleZoomButtonRect.top, 30, 30, cachedBackground, consoleZoomButtonRect.left, consoleZoomButtonRect.top, 15, 15, SRCCOPY);
533: StretchBlt(cachedBackground, consoleZoomButtonRect.left, consoleZoomButtonRect.top, (15 * pSettings->scalingFactorHiDPI), (15 * pSettings->scalingFactorHiDPI), cachedBackground, consoleZoomButtonRect.left, consoleZoomButtonRect.top, 15, 15, SRCCOPY);
534:
535: SelectObject(cachedBackground, oldPen);
536: DeleteObject(zoomPen);
537:
538: //====================
539:
540: // Draw "lock" indicator if write protection is enabled...
541: if (pSettings->writeProtection)
542: {
543: // Draw "lock" background...
544: brush = CreateSolidBrush(pSettings->Console->PicColor); //0x000066
545: FillRect(cachedBackground, &consoleLockGlyphRect, brush);
546: DeleteObject(brush);
547:
548: // Draw "lock" glyph...
549: HPEN protectPen = CreatePen(PS_SOLID, 1, pSettings->Console->Color);
550: HPEN oldPen = (HPEN) SelectObject(cachedBackground, protectPen);
551:
552: MoveToEx(cachedBackground, consoleLockGlyphRect.left+4, consoleLockGlyphRect.top+7, NULL);
553: LineTo(cachedBackground, consoleLockGlyphRect.left+11, consoleLockGlyphRect.top+7);
554: MoveToEx(cachedBackground, consoleLockGlyphRect.left+4, consoleLockGlyphRect.top+8, NULL);
555: LineTo(cachedBackground, consoleLockGlyphRect.left+11, consoleLockGlyphRect.top+8);
556: MoveToEx(cachedBackground, consoleLockGlyphRect.left+4, consoleLockGlyphRect.top+9, NULL);
557: LineTo(cachedBackground, consoleLockGlyphRect.left+11, consoleLockGlyphRect.top+9);
558: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 4, consoleLockGlyphRect.top + 10, NULL);
559: LineTo(cachedBackground, consoleLockGlyphRect.left + 11, consoleLockGlyphRect.top + 10);
560: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 4, consoleLockGlyphRect.top + 11, NULL);
561: LineTo(cachedBackground, consoleLockGlyphRect.left + 11, consoleLockGlyphRect.top + 11);
562:
563: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 6, consoleLockGlyphRect.top + 3, NULL);
564: LineTo(cachedBackground, consoleLockGlyphRect.left + 9, consoleLockGlyphRect.top + 3);
565: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 5, consoleLockGlyphRect.top + 4, NULL);
566: LineTo(cachedBackground, consoleLockGlyphRect.left + 5, consoleLockGlyphRect.top + 7);
567: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 9, consoleLockGlyphRect.top + 4, NULL);
568: LineTo(cachedBackground, consoleLockGlyphRect.left + 9, consoleLockGlyphRect.top + 7);
569:
570: if (pSettings->scalingFactorHiDPI >= 2)
571: {
572: StretchBlt(cachedBackground, consoleLockGlyphRect.left, consoleLockGlyphRect.top, 30, 30, cachedBackground, consoleLockGlyphRect.left, consoleLockGlyphRect.top, 15, 15, SRCCOPY);
573: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 8, consoleLockGlyphRect.top + 13, NULL);
574: LineTo(cachedBackground, consoleLockGlyphRect.left + 22, consoleLockGlyphRect.top + 13);
575: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 11, consoleLockGlyphRect.top + 7, NULL);
576: LineTo(cachedBackground, consoleLockGlyphRect.left + 13, consoleLockGlyphRect.top + 9);
577: MoveToEx(cachedBackground, consoleLockGlyphRect.left + 18, consoleLockGlyphRect.top + 7, NULL);
578: LineTo(cachedBackground, consoleLockGlyphRect.left + 16, consoleLockGlyphRect.top + 9);
579:
580: if (pSettings->scalingFactorHiDPI > 2)
581: {
582: // Temporary lazy solution... (ie. for now just stretching the 2x glyph, with no further pixel fine-tuning like for 2x above)
583: StretchBlt(cachedBackground, consoleLockGlyphRect.left, consoleLockGlyphRect.top, (15 * pSettings->scalingFactorHiDPI), (15 * pSettings->scalingFactorHiDPI), cachedBackground, consoleLockGlyphRect.left, consoleLockGlyphRect.top, 30, 30, SRCCOPY);
584: }
585: }
586:
587: SelectObject(cachedBackground, oldPen);
588: DeleteObject(protectPen);
589: }
590: }
591:
592: //====================
593:
594: // Draw messages...
595: SetRect(&r, 0, 0, ConsoleWidth, ConsoleHeight);
596:
597: HFONT font = CreateFont(pSettings->Console->FontHeight, 0, 0, 0, pSettings->Console->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, pSettings->Console->Font);
598: HGDIOBJ oldfont = SelectObject(cachedBackground, font);
599: SetBkMode(cachedBackground, TRANSPARENT);
600:
601: for (int i=0; i < (int)messageList.size(); i++) SetRectEmpty(&messageList[i]->r); // Flush any prior message RECTs to enable detection of clicks on currently visible messages only...
602:
603: int messagesToDraw;
604: if (pSettings->consoleMaximized) messagesToDraw = pSettings->consoleMaximizedMessages;
605: else messagesToDraw = pSettings->consoleRestoredMessages;
606:
607: if (messagesToDraw > 1)
608: {
609: if (!pSettings->consoleDesktopMode) InflateRect(&r, (-5 * pSettings->scalingFactorHiDPI), (-5 * pSettings->scalingFactorHiDPI));
610: r.bottom = r.top + consoleTrueFontHeight;
611:
612: int i = 0;
613: int mlSize = (int)messageList.size();
614: if (mlSize > messagesToDraw) i = mlSize - messagesToDraw; // Display latest received messages
615: for (i; i < (int)messageList.size(); i++)
616: {
617: DrawMessage(cachedBackground, r, messageList[i]);
618: CopyRect(&messageList[i]->r, &r);
619: OffsetRect(&r, 0, lineSpacing);
620: }
621: }
622: else
623: {
624: if (!pSettings->consoleDesktopMode) InflateRect(&r, (-5 * pSettings->scalingFactorHiDPI), 0);
625: r.top = (r.bottom - r.top - consoleTrueFontHeight) / 2;
626: r.bottom = r.top + consoleTrueFontHeight;
627:
628: int latestMsg = (int)messageList.size() - 1;
629: if (latestMsg >= 0)
630: {
631: DrawMessage(cachedBackground, r, messageList[latestMsg]);
632: CopyRect(&messageList[latestMsg]->r, &r);
633: }
634: }
635:
636: DeleteObject(SelectObject(cachedBackground, oldfont));
637:
638: //====================
639:
640: SetRect(&r, 0, 0, ConsoleWidth, ConsoleHeight);
641:
642: if (!pSettings->consoleDesktopMode)
643: {
644: // Apply per pixel alpha, selectively per placement, creating nice rounded corners... 8)
645: AlphaRect(cachedBackground, r, pSettings->consoleTransparencyAlpha);
646:
647: if (pSettings->consoleRoundedCorners)
648: {
649: switch (pSettings->ConsolePlacement.placement)
650: {
651: /*
652: // Original: Rounding all applicable corners
653: case PLACEMENT_MANUAL: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
654: case PLACEMENT_TOP_LEFT: AlphaCorner(cachedBackground, r, CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
655: case PLACEMENT_CENTER_LEFT: AlphaCorner(cachedBackground, r, CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
656: case PLACEMENT_BOTTOM_LEFT: AlphaCorner(cachedBackground, r, CORNER_TOPRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
657: case PLACEMENT_TOP_CENTER: AlphaCorner(cachedBackground, r, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
658: case PLACEMENT_CENTER_CENTER: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
659: case PLACEMENT_BOTTOM_CENTER: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_TOPRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
660: case PLACEMENT_TOP_RIGHT: AlphaCorner(cachedBackground, r, CORNER_BOTTOMLEFT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
661: case PLACEMENT_CENTER_RIGHT: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_BOTTOMLEFT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
662: case PLACEMENT_BOTTOM_RIGHT: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
663: default: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha);
664: */
665: // Alternative: Rounding all applicable corners *except* the top right one since that's where the square-sized "window controls" are located
666: case PLACEMENT_MANUAL: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
667: case PLACEMENT_TOP_LEFT: AlphaCorner(cachedBackground, r, CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
668: case PLACEMENT_CENTER_LEFT: AlphaCorner(cachedBackground, r, CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
669: case PLACEMENT_BOTTOM_LEFT: AlphaCorner(cachedBackground, r, 0, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
670: case PLACEMENT_TOP_CENTER: AlphaCorner(cachedBackground, r, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
671: case PLACEMENT_CENTER_CENTER: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
672: case PLACEMENT_BOTTOM_CENTER: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
673: case PLACEMENT_TOP_RIGHT: AlphaCorner(cachedBackground, r, CORNER_BOTTOMLEFT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
674: case PLACEMENT_CENTER_RIGHT: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_BOTTOMLEFT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
675: case PLACEMENT_BOTTOM_RIGHT: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha); break;
676: default: AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->Toolbar->borderWidth, 0, pSettings->consoleTransparencyAlpha);
677: }
678: }
679: }
680: else AlphaFromRGB(cachedBackground, r, 0, pSettings->consoleTransparencyAlpha, true, pSettings->desktopWidgetColor); // Console "desktop mode" -> Fully transparent console background, white text with varying transparency, etc
681:
682: AlphaApply(cachedBackground, r);
683:
684: //====================
685:
686: if (bufbmp) DeleteObject(bufbmp);
687:
688: POINT pt;
689: pt.x = ConsoleX, pt.y = ConsoleY;
690: POINT ptSrc;
691: ptSrc.x = 0, ptSrc.y = 0;
692:
693: BLENDFUNCTION bf;
694: bf.BlendOp = AC_SRC_OVER;
695: bf.BlendFlags = 0;
696: bf.AlphaFormat = AC_SRC_ALPHA;
697: bf.SourceConstantAlpha = (unsigned char)255;
698:
699: SIZE windowSize = {ConsoleWidth, ConsoleHeight};
700: BOOL result = UpdateLayeredWindow(hConsoleWnd, NULL, &pt, &windowSize, cachedBackground, &ptSrc, 0, &bf, ULW_ALPHA);
701: /*
702: if (!cachedBackground) SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)"xoblite -> Console -> Invalid cachedBackground!");
703:
704: if (result == 0)
705: {
706: int error = GetLastError();
707: char msg[255];
708: sprintf_s(msg, sizeofArray(msg), "xoblite -> Console -> UpdateLayeredWindow failed! [error code %d]", error);
709: SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
710: }
711: */
712:
713: // Finally, we move the console window to be just above the desktop in the z-order...
714: if (pDesktop) SetWindowPos(hConsoleWnd, pDesktop->hDesktopWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
715: }
716:
717: //===========================================================================
718: // Function: DrawMessage
719: // Purpose: ...
720: //===========================================================================
721:
722: void Console::DrawMessage(HDC hdc, RECT r, messageListItem* mLI)
723: {
724: RECT msgRect, tempRect;
725: CopyRect(&msgRect, &r);
726:
727: if (!pSettings->consoleDesktopMode)
728: {
729: if (IntersectRect(&tempRect, &msgRect, &consoleZoomButtonRect)) msgRect.right = consoleZoomButtonRect.left - (5 * pSettings->scalingFactorHiDPI);
730: if (pSettings->writeProtection && IntersectRect(&tempRect, &msgRect, &consoleLockGlyphRect)) msgRect.right = consoleLockGlyphRect.left - (5 * pSettings->scalingFactorHiDPI);
731: }
732:
733: COLORREF colorHighAlpha, colorMediumAlpha;
734: if (pSettings->consoleDesktopMode)
735: {
736: colorHighAlpha = RGB(pSettings->consoleTransparencyAlpha, pSettings->consoleTransparencyAlpha, pSettings->consoleTransparencyAlpha);
737: colorMediumAlpha = RGB(pSettings->consoleTransparencyAlpha/2, pSettings->consoleTransparencyAlpha/2, pSettings->consoleTransparencyAlpha/2);
738: }
739:
740: //====================
741:
742: if (mLI->type == CONSOLE_SEPARATOR)
743: {
744: int separatorOffset = (msgRect.bottom - msgRect.top) / 2;
745: msgRect.top = msgRect.top + separatorOffset - 1;
746: msgRect.bottom = msgRect.top + 2;
747: if (pSettings->doubleScaleHiDPI) InflateRect(&msgRect, 0, 1);
748: HBRUSH background;
749: if (pSettings->consoleDesktopMode) background = CreateSolidBrush(colorMediumAlpha);
750: // else background = CreateSolidBrush(pSettings->Console->TextColor);
751: else background = CreateSolidBrush(pSettings->MixColors(pSettings->Console->Color, pSettings->Console->TextColor));
752: FillRect(hdc, &msgRect, background);
753: DeleteObject(background);
754: }
755:
756: //====================
757:
758: else if (mLI->type == CONSOLE_REGULAR_MESSAGE || mLI->type == CONSOLE_PLAIN_MESSAGE)
759: {
760: InflateRect(&msgRect, -3, 0);
761: if (pSettings->consoleDesktopMode)
762: {
763: char timestamp[16];
764: // strncpy(timestamp, mLI->msg, 12);
765: strncpy_s(timestamp, sizeof(timestamp), mLI->msg, 12);
766: timestamp[12] = '\0';
767: DrawTextWithEffects(hdc, msgRect, mLI->msg, DT_LEFT | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, colorHighAlpha, false, 0, false, 0, 0, 0);
768: DrawTextWithEffects(hdc, msgRect, timestamp, DT_LEFT | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, colorMediumAlpha, false, 0, false, 0, 0, 0);
769: }
770: else DrawTextWithEffects(hdc, msgRect, mLI->msg, DT_LEFT | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, pSettings->Console->TextColor, false, 0, false, 0, 0, 0);
771:
772: // Highlight clickable URL?
773: if (strnlen_s(mLI->url, sizeofArray(mLI->url)) > 0)
774: {
775: char msgWithoutURL[sizeof(mLI->msg)];
776: strcpy_s(msgWithoutURL, sizeofArray(msgWithoutURL), mLI->msg);
777: char* msgPtr = msgWithoutURL;
778: char* urlPtr = StrStrI(msgWithoutURL, "http://");
779: if (urlPtr == NULL) urlPtr = StrStrI(msgWithoutURL, "https://");
780: if (urlPtr == NULL) urlPtr = StrStrI(msgWithoutURL, "file://");
781: if (urlPtr != NULL)
782: {
783: int n = urlPtr - msgPtr;
784: msgWithoutURL[n] = '\0';
785:
786: SIZE sizeMsg, sizeURL, sizeSpace;
787: HDC fonthdc = CreateDC("DISPLAY", NULL, NULL, NULL);
788: HFONT tempFont = CreateFont(pSettings->Console->FontHeight, 0, 0, 0, pSettings->Console->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, pSettings->Console->Font);
789: HGDIOBJ oldFont = SelectObject(fonthdc, tempFont);
790: GetTextExtentPoint32(fonthdc, msgWithoutURL, strnlen_s(msgWithoutURL, sizeofArray(msgWithoutURL)), &sizeMsg);
791: GetTextExtentPoint32(fonthdc, mLI->url, strnlen_s(mLI->url, sizeofArray(mLI->url)), &sizeURL);
792: GetTextExtentPoint32(fonthdc, " ", 1, &sizeSpace);
793: if (sizeSpace.cx & 1) sizeSpace.cx += 1; // Note: Even number of pixels -> Extra URL background width always divideable by two (see below)
794: if (sizeSpace.cx < 4) sizeSpace.cx = 4;
795: DeleteObject(SelectObject(fonthdc, oldFont));
796: DeleteDC(fonthdc);
797:
798: msgRect.left = msgRect.left + sizeMsg.cx - (sizeSpace.cx/2);
799: msgRect.right = msgRect.left + sizeURL.cx + sizeSpace.cx;
800:
801: // URLs are drawn with inverted colours...
802: HBRUSH background;
803: if (pSettings->consoleDesktopMode) background = CreateSolidBrush(colorHighAlpha);
804: else background = CreateSolidBrush(pSettings->Console->TextColor);
805: FillRect(hdc, &msgRect, background);
806: DeleteObject(background);
807: if (pSettings->consoleDesktopMode) DrawTextWithEffects(hdc, msgRect, mLI->url, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, 0x000000, false, 0, false, 0, 0, 0);
808: else DrawTextWithEffects(hdc, msgRect, mLI->url, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, pSettings->Console->Color, false, 0, false, 0, 0, 0);
809: }
810: }
811: }
812:
813: //====================
814:
815: else
816: {
817: int msgHeight = msgRect.bottom - msgRect.top;
818: int iconSize = msgHeight - 2;
819: // if (iconSize > 16) iconSize = 16;
820: if (iconSize > 32) iconSize = 32;
821: // else if (iconSize > 16) iconSize = 16;
822:
823: // Draw message icon...
824: RECT iconRect;
825: if (mLI->type != CONSOLE_INDENTED_MESSAGE)
826: {
827: iconRect.left = msgRect.left + 4;
828: iconRect.top = msgRect.top + (msgHeight/2) - (iconSize/2);
829: iconRect.right = iconRect.left + iconSize;
830: iconRect.bottom = iconRect.top + iconSize;
831: // HICON bIcon = LoadIcon(NULL, mLI->icon);
832: // DrawIconEx(hdc, iconRect.left, iconRect.top, bIcon, iconSize, iconSize, 0, NULL, DI_NORMAL);
833: // DeleteObject(bIcon);
834: DrawIconEx(hdc, iconRect.left, iconRect.top, mLI->icon, iconSize, iconSize, 0, NULL, DI_NORMAL);
835: }
836: else if (mLI->type == CONSOLE_INDENTED_MESSAGE) iconRect.right = msgRect.left + (iconSize/2) + 1;
837:
838: // Draw message text...
839: msgRect.left = iconRect.right + 3;
840: if (pSettings->doubleScaleHiDPI) msgRect.left += 3;
841: msgRect.right -= 2;
842: if (pSettings->consoleDesktopMode) DrawTextWithEffects(hdc, msgRect, mLI->msg, DT_LEFT | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_EXPANDTABS | DT_WORD_ELLIPSIS, colorHighAlpha, false, 0, false, 0, 0, 0);
843: else DrawTextWithEffects(hdc, msgRect, mLI->msg, DT_LEFT | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_EXPANDTABS | DT_WORD_ELLIPSIS, pSettings->Console->TextColor, false, 0, false, 0, 0, 0);
844: }
845: }
846:
847: //===========================================================================
848: // Function: GetDimensions
849: // Purpose: ...
850: //===========================================================================
851:
852: void Console::GetDimensions()
853: {
854: if (!pSettings->explorerHidden)
855: {
856: RECT workArea;
857: SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
858: ScreenWidth = workArea.right - workArea.left;
859: ScreenHeight = workArea.bottom - workArea.top;
860: }
861: else
862: {
863: ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
864: ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
865: }
866:
867: //====================
868:
869: // First we need to check the *real* size of the font as some style
870: // authors use non-matching font heights with bitmap fonts just to
871: // tweak the appearance of the toolbar (e.g. to shrink its size)
872: SIZE size;
873: HDC fonthdc = CreateDC("DISPLAY", NULL, NULL, NULL);
874: HFONT tempFont = CreateFont(pSettings->Console->FontHeight, 0, 0, 0, pSettings->Console->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, pSettings->Console->Font);
875: HGDIOBJ oldFont = SelectObject(fonthdc, tempFont);
876: GetTextExtentPoint32(fonthdc, "TQkgfp/", 8, &size);
877: consoleTrueFontHeight = size.cy + 1;
878: if (consoleTrueFontHeight < pSettings->Console->FontHeight) consoleTrueFontHeight = pSettings->Console->FontHeight;
879: DeleteObject(SelectObject(fonthdc, oldFont));
880: DeleteDC(fonthdc);
881:
882: lineSpacing = consoleTrueFontHeight + 2;
883: // if (pSettings->doubleScaleHiDPI) lineSpacing += 2;
884: lineSpacing += (2 * pSettings->scalingFactorHiDPI);
885:
886: //====================
887:
888: if (pSettings->consoleMaximized) // Console window size "maximized" (nb. each state's #rows etc fully configurable though)
889: {
890: if (pSettings->consoleMaximizedWidth > 0)
891: {
892: if (pSettings->consoleMaximizedWidth > 100) ConsoleWidth = pSettings->consoleMaximizedWidth;
893: else ConsoleWidth = (ScreenWidth * pSettings->consoleMaximizedWidth) / 100;
894: }
895: else if (pSettings->consoleMaximizedWidth < 0) ConsoleWidth = ScreenWidth + pSettings->consoleMaximizedWidth;
896: else ConsoleWidth = ScreenWidth - 10; // Default used when consoleMaximizedWidth == 0
897:
898: if (pSettings->consoleMaximizedMessages == 1)
899: {
900: if (!pSettings->consoleDesktopMode)
901: {
902: /*
903: ConsoleHeight = consoleTrueFontHeight + 10;
904: if (pSettings->doubleScaleHiDPI)
905: {
906: ConsoleHeight += 10;
907: if (ConsoleHeight & 1) ConsoleHeight++;
908: }
909: else
910: {
911: ConsoleHeight = ConsoleHeight | 1;
912: if (ConsoleHeight < 25) ConsoleHeight = 25;
913: }
914: */
915: ConsoleHeight = consoleTrueFontHeight + (10 * pSettings->scalingFactorHiDPI);
916: if ((pSettings->scalingFactorHiDPI == 2) || (pSettings->scalingFactorHiDPI == 4))
917: {
918: if (ConsoleHeight & 1) ConsoleHeight++;
919: }
920: else // if ((pSettings->scalingFactorHiDPI == 1) || (pSettings->scalingFactorHiDPI == 3))
921: {
922: ConsoleHeight = ConsoleHeight | 1;
923: if (ConsoleHeight < 25) ConsoleHeight = 25;
924: }
925: }
926: else ConsoleHeight = consoleTrueFontHeight;
927: }
928: else
929: {
930: if (!pSettings->consoleDesktopMode)
931: {
932: // ConsoleHeight = (lineSpacing * pSettings->consoleMaximizedMessages) + 8 + pSettings->consolePadding; // Padding 5+5 pixels minus 2 pixels from lineSpacing
933: // if (pSettings->doubleScaleHiDPI) ConsoleHeight += 8; // Padding 10+10 pixels minus 4 pixels from lineSpacing
934: ConsoleHeight = (lineSpacing * pSettings->consoleMaximizedMessages) + (10 * pSettings->scalingFactorHiDPI) + pSettings->consolePadding - (2 * pSettings->scalingFactorHiDPI);;
935: }
936: else
937: {
938: ConsoleHeight = (lineSpacing * pSettings->consoleMaximizedMessages);
939: // if (pSettings->doubleScaleHiDPI) ConsoleHeight -= 4; // No padding and minus 4 pixels from lineSpacing
940: // else ConsoleHeight -= 2; // No padding and minus 2 pixels from lineSpacing
941: ConsoleHeight -= (2 * pSettings->scalingFactorHiDPI);
942: }
943: }
944: }
945: else // Console window size "restored" (nb. each state's #rows etc fully configurable though)
946: {
947: if (pSettings->consoleRestoredWidth > 0)
948: {
949: if (pSettings->consoleRestoredWidth > 100) ConsoleWidth = pSettings->consoleRestoredWidth;
950: else ConsoleWidth = (ScreenWidth * pSettings->consoleRestoredWidth) / 100;
951: }
952: else if (pSettings->consoleRestoredWidth < 0) ConsoleWidth = ScreenWidth + pSettings->consoleRestoredWidth;
953: else ConsoleWidth = ScreenWidth / 3; // Default used when consoleRestoredWidth == 0
954:
955: if (pSettings->consoleRestoredMessages == 1)
956: {
957: if (!pSettings->consoleDesktopMode)
958: {
959: /*
960: ConsoleHeight = consoleTrueFontHeight + 10;
961: if (pSettings->doubleScaleHiDPI)
962: {
963: ConsoleHeight += 10;
964: if (ConsoleHeight & 1) ConsoleHeight++;
965: }
966: else
967: {
968: ConsoleHeight = ConsoleHeight | 1;
969: if (ConsoleHeight < 25) ConsoleHeight = 25;
970: }
971: */
972: ConsoleHeight = consoleTrueFontHeight + (10 * pSettings->scalingFactorHiDPI);
973: if ((pSettings->scalingFactorHiDPI == 2) || (pSettings->scalingFactorHiDPI == 4))
974: {
975: if (ConsoleHeight & 1) ConsoleHeight++;
976: }
977: else // if ((pSettings->scalingFactorHiDPI == 1) || (pSettings->scalingFactorHiDPI == 3))
978: {
979: ConsoleHeight = ConsoleHeight | 1;
980: if (ConsoleHeight < 25) ConsoleHeight = 25;
981: }
982: }
983: else ConsoleHeight = consoleTrueFontHeight;
984: }
985: else
986: {
987: if (!pSettings->consoleDesktopMode)
988: {
989: // ConsoleHeight = (lineSpacing * pSettings->consoleRestoredMessages) + 8 + pSettings->consolePadding; // Padding 5+5 pixels minus 2 pixels padding from last lineSpacing
990: // if (pSettings->doubleScaleHiDPI) ConsoleHeight += 8; // Padding 10+10 pixels minus 4 pixels padding from last lineSpacing
991: ConsoleHeight = (lineSpacing * pSettings->consoleRestoredMessages) + (10 * pSettings->scalingFactorHiDPI) + pSettings->consolePadding - (2 * pSettings->scalingFactorHiDPI);;
992: }
993: else
994: {
995: ConsoleHeight = (lineSpacing * pSettings->consoleRestoredMessages);
996: // if (pSettings->doubleScaleHiDPI) ConsoleHeight -= 4; // No padding and minus 4 pixels from lineSpacing
997: // else ConsoleHeight -= 2; // No padding and minus 2 pixels from lineSpacing
998: ConsoleHeight -= (2 * pSettings->scalingFactorHiDPI);
999: }
1000: }
1001: }
1002:
1003: if (ConsoleWidth < 300) ConsoleWidth = 300; // Minimum console width is 300 pixels
1004:
1005: //====================
1006:
1007: if ((pSettings->consoleMaximized && (pSettings->consoleMaximizedMessages > 1)) || (!pSettings->consoleMaximized && (pSettings->consoleRestoredMessages > 1)))
1008: {
1009: SetRect(&consoleZoomButtonRect, ConsoleWidth - (20 * pSettings->scalingFactorHiDPI), (5 * pSettings->scalingFactorHiDPI), ConsoleWidth - (5 * pSettings->scalingFactorHiDPI), (20 * pSettings->scalingFactorHiDPI));
1010: }
1011: else
1012: {
1013: int padding = ((ConsoleHeight - (15 * pSettings->scalingFactorHiDPI)) / 2);
1014: SetRect(&consoleZoomButtonRect, ConsoleWidth - (20 * pSettings->scalingFactorHiDPI), padding, ConsoleWidth - (5 * pSettings->scalingFactorHiDPI), padding + (15 * pSettings->scalingFactorHiDPI));
1015: }
1016:
1017: CopyRect(&consoleLockGlyphRect, &consoleZoomButtonRect);
1018: OffsetRect(&consoleLockGlyphRect, (-20 * pSettings->scalingFactorHiDPI), 0);
1019:
1020: //====================
1021:
1022: pSettings->ConsolePlacement.width = ConsoleWidth;
1023: pSettings->ConsolePlacement.height = ConsoleHeight;
1024:
1025: pSettings->PositionFromPlacement(&pSettings->ConsolePlacement);
1026:
1027: ConsoleX = pSettings->ConsolePlacement.x;
1028: ConsoleY = pSettings->ConsolePlacement.y;
1029: }
1030:
1031: //===========================================================================
1032: // Function: UpdatePosition
1033: // Purpose: ...
1034: //===========================================================================
1035:
1036: void Console::UpdatePosition()
1037: {
1038: // Get the new size and position for our window...
1039: GetDimensions();
1040: // Update the console window...
1041: UpdateConsoleWindow();
1042: }
1043:
1044: //===========================================================================
1045: // Function: ToggleMaximized
1046: // Purpose: ...
1047: //===========================================================================
1048:
1049: void Console::ToggleMaximized()
1050: {
1051: if (pSettings->consoleMaximized) pSettings->consoleMaximized = false;
1052: else pSettings->consoleMaximized = true;
1053:
1054: // Resize the console window... (restored<->maximized)
1055: UpdatePosition();
1056: PlaySoundFX(SFX_TOGGLE_ELEMENT);
1057:
1058: // Save setting to xoblite.rc...
1059: WriteBool(pSettings->xobrcFile, "xoblite.console.maximized:", pSettings->consoleMaximized);
1060: }
1061:
1062: //===========================================================================
1063: // Function: BlockMessage
1064: // Purpose: ...
1065: //===========================================================================
1066:
1067: void Console::BlockMessage(char* message)
1068: {
1069: strcpy_s(messageToBlock, sizeofArray(messageToBlock), message);
1070: }
1071:
1072: //===========================================================================
1073: // Function: ClearHistory
1074: // Purpose: ...
1075: //===========================================================================
1076:
1077: void Console::ClearHistory(bool redraw)
1078: {
1079: // Clear all elements in the messageList...
1080: for (int i=0; i<(int)messageList.size(); i++)
1081: {
1082: if (messageList[i]->icon != NULL) DeleteObject(&messageList[i]->icon);
1083: delete messageList[i];
1084: }
1085: messageList.clear();
1086: // ZeroMemory(&messageList, sizeof(messageList));
1087:
1088: // Update the console window...
1089: if (redraw) UpdatePosition();
1090: }
1091:
1092: //===========================================================================
1093: // Function: FullscreenDetected
1094: // Purpose: Hide the console (no updating) if an application goes fullscreen
1095: //===========================================================================
1096:
1097: void Console::FullscreenDetected(bool fullscreen)
1098: {
1099: if (fullscreen)
1100: {
1101: fullscreenDetected = true;
1102: ShowWindow(hConsoleWnd, SW_HIDE);
1103: }
1104: else
1105: {
1106: fullscreenDetected = false;
1107: if (!pSettings->consoleHidden) ShowWindow(hConsoleWnd, SW_SHOWNOACTIVATE);
1108: UpdateConsoleWindow();
1109: }
1110: }
1111:
1112: //===========================================================================
1113: