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 "Desktop.h" 
    32: 
    33: #include <gdiplus.h> 
    34: using namespace Gdiplus;
    35: 
    36: const char szDesktopName[] = "BBDesktop"; // Window class etc.
    37: 
    38: extern Desktop* pDesktop;
    39: extern Settings* pSettings;
    40: extern MenuCommon* pMenuCommon;
    41: extern Toolbar* pToolbar;
    42: extern Taskbar* pTaskbar;
    43: extern Workspaces* pWorkspaces;
    44: 
    45: int desktopMessageSubscription[] = { BB_RECONFIGURE, BB_WINDOWSHADE, BB_WINDOWLOWER, BB_WINDOWRAISE, BB_WINDOWGROWHEIGHT, BB_WINDOWGROWWIDTH, 0 };
    46: 
    47: //===========================================================================
    48: 
    49: Desktop::Desktop(HINSTANCE hInstance)
    50: {
    51:     hDesktopWnd = NULL;
    52:     hBlackboxWnd = GetBBWnd();
    53:     hDesktopInstance = hInstance;
    54:     cachedBackground = CreateCompatibleDC(NULL);
    55:     wallpaper.bitmap = NULL;
    56: 
    57:     CurrentTime[0] = CurrentWeekday[0] = CurrentDate[0] = CurrentGreeting[0] = '\0';
    58: 
    59:     fullscreenDetected = false;
    60: 
    61:     numberOfDisplayMonitors = GetSystemMetrics(SM_CMONITORS);
    62: 
    63:     //====================
    64: 
    65:     // Get size and position for the desktop window...
    66:     GetDimensions();
    67:     // ...and get the current time for the desktop clock...
    68:     GetClockText(true);
    69:     GetGreetingText(true);
    70: 
    71:     //====================
    72: 
    73:     // Register window class...
    74:     WNDCLASS wc;
    75:     ZeroMemory(&wc, sizeof(wc));
    76:     wc.hInstance = hDesktopInstance;            // hInstance
    77:     wc.lpfnWndProc = DesktopWndProc;            // window procedure
    78:     wc.lpszClassName = szDesktopName;           // window class name
    79:     wc.hbrBackground = NULL;                    // no class background brush
    80:     wc.style = CS_DBLCLKS;                      // class styles (e.g. accept doubleclicks)
    81:     wc.hCursor = LoadCursor(NULL, IDC_ARROW);   // always display the regular arrow cursor
    82: 
    83:     if (!RegisterClass(&wc))
    84:     {
    85:         MessageBox(0, "Error registering desktop window class!", szDesktopName, MB_OK | MB_ICONERROR | MB_TOPMOST);
    86:         Log("Desktop: Error registering window class", NULL);
    87:         return;
    88:     }
    89: 
    90:     // Create desktop window...
    91:     hDesktopWnd = CreateWindowEx(
    92:         WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES | WS_EX_NOACTIVATE | WS_EX_LAYERED,    // window style
    93:         szDesktopName,                                                              // window class
    94:         NULL,                                                                       // window name
    95:         WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,                               // window parameters
    96:         DesktopX,                                                                   // x position
    97:         DesktopY,                                                                   // y position
    98:         DesktopWidth,                                                               // window width
    99:         DesktopHeight,                                                              // window height
   100:         NULL,                                                                       // owner window
   101:         NULL,                                                                       // no menu
   102:         hDesktopInstance,                                                           // hInstance
   103:         NULL                                                                        // no window creation data
   104:     );
   105: 
   106:     if (!hDesktopWnd)
   107:     {
   108:         UnregisterClass(szDesktopName, hDesktopInstance); // unregister window class
   109:         MessageBox(0, "Error creating desktop window!", szDesktopName, MB_OK | MB_ICONERROR | MB_TOPMOST);
   110:         Log("Desktop: Error creating window!", NULL);
   111:         return;
   112:     }
   113: 
   114:     //====================
   115: 
   116:     // Subscribe to Blackbox messages applicable to the desktop...
   117:     SendMessage(hBlackboxWnd, BB_REGISTERMESSAGE, (WPARAM)hDesktopWnd, (LPARAM)desktopMessageSubscription);
   118: 
   119:     // Make the desktop window sticky...
   120:     MakeSticky(hDesktopWnd);
   121:     // Move the desktop window to the bottom of the application z-order... (i.e. just above the Windows desktop in our case)
   122: //  SetWindowPos(hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
   123:     SetWindowPos(hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
   124: 
   125:     // Show+update the desktop window...
   126:     ShowWindow(hDesktopWnd, SW_SHOWNOACTIVATE);
   127:     UpdateDesktopWindow();
   128: 
   129:     // Set timer for the desktop clock update checks... (every 10 seconds, but only updating the desktop/clock if needed)
   130:     if (!SetTimer(hDesktopWnd, DESKTOP_UPDATE_TIMER, 10000, (TIMERPROC)NULL)) CurrentTime[0] = '\0';
   131: }
   132: 
   133: //===========================================================================
   134: 
   135: Desktop::~Desktop()
   136: {
   137:     KillTimer(hDesktopWnd, DESKTOP_UPDATE_TIMER);
   138: 
   139:     // Unsubscribe to previously subscribed Blackbox messages...
   140:     SendMessage(hBlackboxWnd, BB_UNREGISTERMESSAGE, (WPARAM)hDesktopWnd, (LPARAM)desktopMessageSubscription);
   141: 
   142:     if (hDesktopWnd) DestroyWindow(hDesktopWnd); // Destroy window...
   143:     UnregisterClass(szDesktopName, hDesktopInstance); // Unregister window class...
   144: 
   145:     if (wallpaper.bitmap != NULL) DeleteObject(wallpaper.bitmap); // Delete any cached bitmap...
   146:     if (cachedBackground != NULL) DeleteDC(cachedBackground); // Delete the cached gradient...
   147: }
   148: 
   149: //===========================================================================
   150: 
   151: LRESULT CALLBACK DesktopWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   152: {
   153: 
   154:     switch (message)
   155:     {
   156:         //====================
   157: 
   158:         case BB_RECONFIGURE:
   159:         case WM_DISPLAYCHANGE:
   160:         {
   161:             // Update the desktop's dimensions if the screen resolution changes...
   162:             pDesktop->GetClockText(true);
   163:             pDesktop->GetGreetingText(true);
   164:             pDesktop->UpdateDesktopWindow();
   165: 
   166:             if (message == BB_RECONFIGURE) PlaySoundFX(SFX_RECONFIGURE);
   167:             else if (message == WM_DISPLAYCHANGE)
   168:             {
   169:                 int tempMonitors = GetSystemMetrics(SM_CMONITORS);
   170:                 if (pDesktop->numberOfDisplayMonitors != tempMonitors)
   171:                 {
   172:                     char msg[255];
   173:                     if (tempMonitors > pDesktop->numberOfDisplayMonitors) strcpy_s(msg, sizeofArray(msg), "System -> Display monitor attached.");
   174:                     else if (tempMonitors < pDesktop->numberOfDisplayMonitors) strcpy_s(msg, sizeofArray(msg), "System -> Display monitor removed.");
   175:                     SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
   176: 
   177:                     pDesktop->numberOfDisplayMonitors = tempMonitors;
   178:                 }
   179:             }
   180:             break;
   181:         }
   182: 
   183:         case WM_SETTINGCHANGE:
   184:         {
   185:             if (wParam == SPI_SETWORKAREA)
   186:             {
   187:                 // Update the desktop's dimensions and position if the work area changes...
   188:                 // (e.g. if the Explorer taskbar is moved to another screen edge)
   189:                 pDesktop->UpdateDesktopWindow();
   190: 
   191:                 // ...and notify plugins that the desktop work area has changed...
   192:                 SendMessage(GetBBWnd(), BB_REDRAWGUI, BBRG_DESKTOP, 0);
   193:             }
   194:             return 0;
   195:         }
   196: /*
   197:         case WM_WINDOWPOSCHANGED:
   198:         {
   199:             // Move the desktop window back to the bottom of the z-order if its place in it changes...
   200: //          SetWindowPos(pDesktop->hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
   201: //          SetWindowPos(pDesktop->hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
   202: //          SetWindowPos(pDesktop->hDesktopWnd, GetDesktopWindow(), pDesktop->DesktopX, pDesktop->DesktopX, pDesktop->DesktopWidth, pDesktop->DesktopHeight, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
   203: //          SetWindowPos(pDesktop->hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
   204:             if (pSettings->debugLogging) SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)"WM_WINDOWPOSCHANGED");
   205: //          if (pSettings->debugLogging) Log("Desktop: WM_WINDOWPOSCHANGED", "");
   206:             return 0;
   207:         }
   208: */
   209: /*
   210:         case WM_GETMINMAXINFO:
   211:         {
   212:             // Not sure if this is really needed since we don't allow the desktop window to be moved/resized anyway, but just in case... ;)
   213:             MINMAXINFO* mmi = (MINMAXINFO*)lParam;
   214:             mmi->ptMaxPosition.x = pDesktop->DesktopX;
   215:             mmi->ptMaxPosition.y = pDesktop->DesktopY;
   216:             mmi->ptMaxSize.x = pDesktop->DesktopWidth;
   217:             mmi->ptMaxSize.y = pDesktop->DesktopHeight;
   218:             return 0;
   219:         }
   220: */
   221:         //====================
   222: 
   223:         case WM_TIMER:
   224:         {
   225:             // Is it time to update the desktop clock/weekday/date?
   226:             if (!pSettings->desktopClockHidden || !pSettings->desktopWeekdayHidden || !pSettings->desktopDateHidden || !pSettings->desktopGreetingHidden)
   227:             {
   228:                 if (pDesktop->GetClockText(false))
   229:                 {
   230:                     // Clock timestamp has changed; update the other widgets too, then the Desktop...
   231:                     pDesktop->GetGreetingText(true);
   232:                     pDesktop->UpdateDesktopWindow();
   233:                 }
   234:             }
   235:             return 0;
   236:         }
   237: 
   238:         //====================
   239: 
   240:         case WM_CLOSE:
   241:             return 0;
   242: 
   243:         //====================
   244: 
   245:         case WM_MOUSEACTIVATE:
   246:             return MA_NOACTIVATE;
   247: /*
   248:         case WM_ACTIVATE:
   249:         {
   250:             if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) SetActiveWindow(hwnd);
   251:             return 0;
   252:         }
   253:         break;
   254: */
   255:         //====================
   256: 
   257:         case WM_NCHITTEST:
   258:             return HTCLIENT;
   259: 
   260:         case WM_LBUTTONDOWN:
   261:         case WM_LBUTTONUP:
   262: //      case WM_LBUTTONDBLCLK:
   263:         case WM_RBUTTONDOWN:
   264:         case WM_RBUTTONUP:
   265:         case WM_MBUTTONDOWN:
   266:         case WM_MBUTTONUP:
   267:         case WM_XBUTTONDOWN:
   268:         case WM_XBUTTONUP:
   269:         case WM_MOUSEWHEEL:
   270:         case WM_MOUSEHWHEEL:
   271:         case WM_DROPFILES:
   272:         {
   273:             // First we set focus to the main Blackbox window, in order to prevent the
   274:             // focus from going back to the previous task after e.g. the menu is closed...
   275:             SetForegroundWindow(GetBBWnd());
   276:             pDesktop->MouseAndDropHandler(pDesktop->hDesktopWnd, message, wParam, lParam);
   277:             return 0;
   278:         }
   279:         break;
   280: 
   281:         //====================
   282: 
   283:         case BB_WINDOWSHADE:
   284:             pDesktop->ShadeWindow();
   285:             break;
   286:         case BB_WINDOWGROWHEIGHT:
   287:             pDesktop->GrowWindowHeight();
   288:             break;
   289:         case BB_WINDOWGROWWIDTH:
   290:             pDesktop->GrowWindowWidth();
   291:             break;
   292:         case BB_WINDOWLOWER:
   293:             pDesktop->LowerWindow();
   294:             break;
   295:         case BB_WINDOWRAISE:
   296:             pDesktop->RaiseWindow();
   297:             break;
   298: 
   299:         //====================
   300: 
   301:         default:
   302:             return DefWindowProc(hwnd, message, wParam, lParam);
   303: 
   304:         //====================
   305:     }
   306: 
   307:     return 0;
   308: }
   309: 
   310: //===========================================================================
   311: // Function: UpdateDesktopWindow
   312: // Purpose: ...
   313: //===========================================================================
   314: 
   315: void Desktop::UpdateDesktopWindow()
   316: {
   317:     if (fullscreenDetected) return;
   318: 
   319:     // Fetch the new size and position parameters for the desktop window...
   320:     GetDimensions();
   321: 
   322:     RECT r;
   323:     SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
   324: 
   325:     HDC hdc = GetWindowDC(hDesktopWnd);
   326:     HBITMAP bufbmp = CreateCompatibleBitmap(hdc, r.right - r.left, r.bottom - r.top);
   327:     DeleteObject(SelectObject(cachedBackground, bufbmp));
   328:     ReleaseDC(hDesktopWnd, hdc);
   329: 
   330:     HDC widgetDC = CreateCompatibleDC(cachedBackground);
   331:     HBRUSH blackBrush = CreateSolidBrush(0x000000);
   332: 
   333:     //====================
   334: 
   335: 
   336:     if (!pSettings->underExplorer && (wallpaper.bitmap != NULL))
   337:     {
   338:         SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
   339:         DrawWallpaper(cachedBackground);
   340:         AlphaRect(cachedBackground, r, 255);
   341:     }
   342:     else
   343:     {
   344:         // Draw desktop background...
   345:         FillRect(cachedBackground, &r, blackBrush);
   346: 
   347:         // Apply almost full transparency -> The desktop is still there and we can click it etc, but the regular Windows wallpaper can be seen!
   348:         SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
   349:         if (!pSettings->desktopBorderHidden)
   350:         {
   351:             AlphaRect(cachedBackground, r, 64);
   352:             InflateRect(&r, (-4 * pSettings->scalingFactorHiDPI), (-4 * pSettings->scalingFactorHiDPI));
   353:         }
   354:         AlphaRect(cachedBackground, r, 1);
   355:         // if (!pSettings->desktopBorderHidden) AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, 0, 64, 1);
   356:         // AlphaApply(cachedBackground, r);
   357:     }
   358: 
   359:     //====================
   360: 
   361:     if (!pSettings->desktopWorkspaceIndicatorHidden)
   362:     {
   363:         // Draw workspace indicators... (nb. their default position is near top center, but if the toolbar placement is top something we put them near bottom center instead)
   364:         char workspacePlacement[20] = "\0";
   365:         Placement workspaceIndicators;
   366:         workspaceIndicators.width = (pSettings->numberOfWorkspaces * 20 * pSettings->scalingFactorHiDPI) - (4 * pSettings->scalingFactorHiDPI);
   367:         workspaceIndicators.height = (9 * pSettings->scalingFactorHiDPI);
   368:         if (!pSettings->toolbarHidden && (pSettings->ToolbarPlacement.placement == PLACEMENT_TOP_LEFT ||
   369:             pSettings->ToolbarPlacement.placement == PLACEMENT_TOP_CENTER || pSettings->ToolbarPlacement.placement == PLACEMENT_TOP_RIGHT))
   370:         {
   371:             sprintf_s(workspacePlacement, sizeofArray(workspacePlacement), "Manual xC y-%d", r.top + (9*pSettings->scalingFactorHiDPI));
   372:         }
   373:         else
   374:         {
   375:             sprintf_s(workspacePlacement, sizeofArray(workspacePlacement), "Manual xC y%d", r.top + (9*pSettings->scalingFactorHiDPI));
   376:         }
   377:         pSettings->ParsePlacement(workspacePlacement, &workspaceIndicators);
   378:         pSettings->PositionFromPlacement(&workspaceIndicators);
   379: //      CheckWidgetPlacement(&workspaceIndicators);
   380: 
   381:         SetRect(&r, workspaceIndicators.x, workspaceIndicators.y, workspaceIndicators.x+(16*pSettings->scalingFactorHiDPI), workspaceIndicators.y+(9*pSettings->scalingFactorHiDPI));
   382: 
   383: //      HBRUSH brush = CreateSolidBrush(0xffffff);
   384:         HBRUSH brush = CreateSolidBrush(pSettings->desktopWidgetColor);
   385:         for (int i=0; i<pSettings->numberOfWorkspaces; i++)
   386:         {
   387:             FillRect(cachedBackground, &r, brush);
   388:             if (i == pSettings->currentWorkspace) AlphaRect(cachedBackground, r, 128);
   389:             else AlphaRect(cachedBackground, r, 64);
   390:             OffsetRect(&r, (20*pSettings->scalingFactorHiDPI), 0);
   391:         }
   392:         DeleteObject(brush);
   393:     }
   394: 
   395:     //====================
   396: 
   397:     // Draw weekday...?
   398:     if (!pSettings->desktopWeekdayHidden)
   399:     {
   400:         HFONT widgetFont;
   401:         HGDIOBJ oldFont;
   402: 
   403:         if (pSettings->desktopWeekdayFontDefined)
   404:         {
   405:             widgetFont = CreateFont((pSettings->desktopWeekdayFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopWeekdayFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopWeekdayFont);
   406:         }
   407:         else
   408:         {
   409:             int fontHeight = 30 * pSettings->scalingFactorHiDPI;
   410:             widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_BOLD, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
   411:         }
   412: 
   413:         oldFont = SelectObject(cachedBackground, widgetFont);
   414:         SetBkMode(cachedBackground, TRANSPARENT);
   415:         SIZE size;
   416:         GetTextExtentPoint32W(cachedBackground, CurrentWeekday, wcslen(CurrentWeekday), &size);
   417:         pSettings->desktopWeekdayPlacement.width = size.cx;
   418:         pSettings->desktopWeekdayPlacement.height = size.cy;
   419:         SelectObject(cachedBackground, oldFont);
   420: 
   421:         if (pSettings->desktopWeekdayPlacement.placement != PLACEMENT_DEFAULT)
   422:         {
   423:             pSettings->PositionFromPlacement(&pSettings->desktopWeekdayPlacement);
   424:             CheckWidgetPlacement(&pSettings->desktopWeekdayPlacement);
   425:             SetRect(&weekdayRect, pSettings->desktopWeekdayPlacement.x, pSettings->desktopWeekdayPlacement.y, pSettings->desktopWeekdayPlacement.x + pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.y + pSettings->desktopWeekdayPlacement.height);
   426:         }
   427:         else // PLACEMENT_DEFAULT
   428:         {
   429:             weekdayRect.left = DesktopCenterX - (pSettings->desktopWeekdayPlacement.width / 2);
   430:             weekdayRect.right = weekdayRect.left + pSettings->desktopWeekdayPlacement.width;
   431:             if (pSettings->desktopWeekdayFontDefined) weekdayRect.top = 139;
   432:             else weekdayRect.top = 120;
   433:             weekdayRect.bottom = weekdayRect.top + pSettings->desktopWeekdayPlacement.height;
   434:         }
   435: 
   436:         HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height);
   437:         HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
   438: 
   439:         oldFont = SelectObject(widgetDC, widgetFont);
   440:         SetBkMode(widgetDC, TRANSPARENT);
   441:         RECT widgetRect = { 0, 0, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height };
   442:         FillRect(widgetDC, &widgetRect, blackBrush);
   443:         DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentWeekday, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   444:         AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
   445:         SelectObject(widgetDC, oldFont);
   446: 
   447:         BLENDFUNCTION bf;
   448:         ZeroMemory(&bf, sizeof(BLENDFUNCTION));
   449:         bf.BlendOp = AC_SRC_OVER;
   450:         bf.BlendFlags = 0;
   451:         bf.SourceConstantAlpha = pSettings->desktopWeekdayTransparencyAlpha;
   452:         bf.AlphaFormat = AC_SRC_ALPHA;
   453:         AlphaBlend(cachedBackground, weekdayRect.left, weekdayRect.top, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height, widgetDC, 0, 0, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height, bf);
   454: 
   455:         SelectObject(widgetDC, oldBitmap);
   456:         DeleteObject(widgetBitmap);
   457:         // DeleteObject(oldBitmap); // Not necessary (default stock object)
   458:         DeleteObject(widgetFont);
   459:         // DeleteObject(oldFont); // Not necessary (default stock object)
   460:     }
   461: 
   462:     //====================
   463: 
   464:     // Draw date...?
   465:     if (!pSettings->desktopDateHidden)
   466:     {
   467:         HFONT widgetFont;
   468:         HGDIOBJ oldFont;
   469: 
   470:         if (pSettings->desktopDateFontDefined)
   471:         {
   472:             widgetFont = CreateFont((pSettings->desktopDateFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopDateFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopDateFont);
   473:         }
   474:         else
   475:         {
   476:             int fontHeight = 30 * pSettings->scalingFactorHiDPI;
   477:             widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
   478:         }
   479: 
   480:         oldFont = SelectObject(cachedBackground, widgetFont);
   481:         SetBkMode(cachedBackground, TRANSPARENT);
   482:         SIZE size;
   483:         GetTextExtentPoint32W(cachedBackground, CurrentDate, wcslen(CurrentDate), &size);
   484:         pSettings->desktopDatePlacement.width = size.cx;
   485:         pSettings->desktopDatePlacement.height = size.cy;
   486:         SelectObject(cachedBackground, oldFont);
   487: 
   488:         if (pSettings->desktopDatePlacement.placement != PLACEMENT_DEFAULT)
   489:         {
   490:             pSettings->PositionFromPlacement(&pSettings->desktopDatePlacement);
   491:             CheckWidgetPlacement(&pSettings->desktopDatePlacement);
   492:             SetRect(&dateRect, pSettings->desktopDatePlacement.x, pSettings->desktopDatePlacement.y, pSettings->desktopDatePlacement.x + pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.y + pSettings->desktopDatePlacement.height);
   493:         }
   494:         else // PLACEMENT_DEFAULT
   495:         {
   496:             dateRect.left = DesktopCenterX - (pSettings->desktopDatePlacement.width / 2);
   497:             dateRect.right = dateRect.left + pSettings->desktopDatePlacement.width;
   498:             if (pSettings->desktopDateFontDefined) dateRect.top = 239;
   499:             else dateRect.top = 220;
   500:             dateRect.bottom = dateRect.top + pSettings->desktopDatePlacement.height;
   501:         }
   502: 
   503:         HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height);
   504:         HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
   505: 
   506:         oldFont = SelectObject(widgetDC, widgetFont);
   507:         SetBkMode(widgetDC, TRANSPARENT);
   508:         RECT widgetRect = { 0, 0, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height };
   509:         FillRect(widgetDC, &widgetRect, blackBrush);
   510:         DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentDate, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   511:         AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
   512:         SelectObject(widgetDC, oldFont);
   513: 
   514:         BLENDFUNCTION bf;
   515:         ZeroMemory(&bf, sizeof(BLENDFUNCTION));
   516:         bf.BlendOp = AC_SRC_OVER;
   517:         bf.BlendFlags = 0;
   518:         bf.SourceConstantAlpha = pSettings->desktopDateTransparencyAlpha;
   519:         bf.AlphaFormat = AC_SRC_ALPHA;
   520:         AlphaBlend(cachedBackground, dateRect.left, dateRect.top, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height, widgetDC, 0, 0, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height, bf);
   521: 
   522:         SelectObject(widgetDC, oldBitmap);
   523:         DeleteObject(widgetBitmap);
   524:         // DeleteObject(oldBitmap); // Not necessary (default stock object)
   525:         DeleteObject(widgetFont);
   526:         // DeleteObject(oldFont); // Not necessary (default stock object)
   527:     }
   528: 
   529:     //====================
   530: 
   531:     // Draw clock...?
   532:     if (!pSettings->desktopClockHidden)
   533:     {
   534:         if (!_stricmp(pSettings->desktopClockType, "Regular")) // -> Regular 24-hour clock (HH:MM)
   535:         {
   536:             HFONT widgetFont;
   537:             HGDIOBJ oldFont;
   538: 
   539:             if (pSettings->desktopClockFontDefined)
   540:             {
   541:                 widgetFont = CreateFont((pSettings->desktopClockFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopClockFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopClockFont);
   542:             }
   543:             else
   544:             {
   545:                 int fontHeight = 180 * pSettings->scalingFactorHiDPI;
   546:                 widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
   547:             }
   548: 
   549:             oldFont = SelectObject(cachedBackground, widgetFont);
   550:             SetBkMode(cachedBackground, TRANSPARENT);
   551:             SIZE size;
   552:             GetTextExtentPoint32W(cachedBackground, CurrentTime, wcslen(CurrentTime), &size);
   553:             pSettings->desktopClockPlacement.width = size.cx;
   554:             pSettings->desktopClockPlacement.height = size.cy;
   555:             SelectObject(cachedBackground, oldFont);
   556: 
   557:             if (pSettings->desktopClockPlacement.placement != PLACEMENT_DEFAULT)
   558:             {
   559:                 pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
   560:                 CheckWidgetPlacement(&pSettings->desktopClockPlacement);
   561:                 SetRect(&clockRect, pSettings->desktopClockPlacement.x, pSettings->desktopClockPlacement.y, pSettings->desktopClockPlacement.x + pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.y + pSettings->desktopClockPlacement.height);
   562:             }
   563:             else // PLACEMENT_DEFAULT
   564:             {
   565:                 clockRect.left = DesktopCenterX - (pSettings->desktopClockPlacement.width / 2);
   566:                 clockRect.right = clockRect.left + pSettings->desktopClockPlacement.width;
   567:                 if (pSettings->desktopClockFontDefined) clockRect.top = 39;
   568:                 else clockRect.top = 20;
   569:                 clockRect.bottom = clockRect.top + pSettings->desktopClockPlacement.height;
   570:             }
   571: 
   572:             HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height);
   573:             HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
   574: 
   575:             oldFont = SelectObject(widgetDC, widgetFont);
   576:             SetBkMode(widgetDC, TRANSPARENT);
   577:             RECT widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
   578:             FillRect(widgetDC, &widgetRect, blackBrush);
   579:             DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentTime, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   580:             AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
   581:             SelectObject(widgetDC, oldFont);
   582: 
   583:             BLENDFUNCTION bf;
   584:             ZeroMemory(&bf, sizeof(BLENDFUNCTION));
   585:             bf.BlendOp = AC_SRC_OVER;
   586:             bf.BlendFlags = 0;
   587:             bf.SourceConstantAlpha = pSettings->desktopClockTransparencyAlpha;
   588:             bf.AlphaFormat = AC_SRC_ALPHA;
   589:             AlphaBlend(cachedBackground, clockRect.left, clockRect.top, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, widgetDC, 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, bf);
   590: 
   591:             SelectObject(widgetDC, oldBitmap);
   592:             DeleteObject(widgetBitmap);
   593:             // DeleteObject(oldBitmap); // Not necessary (default stock object)
   594:             DeleteObject(widgetFont);
   595:             // DeleteObject(oldFont); // Not necessary (default stock object)
   596:         }
   597: 
   598:         //====================
   599: 
   600:         else if (!_stricmp(pSettings->desktopClockType, "Text")) // -> "Text clock" using simple 12 hour format, e.g. "TWENTY past EIGHT" (regardless of AM/PM)
   601:         {
   602:             char textMinutes[32] = "", textInBetween[32] = "", textHours[32] = "", textAfter[32] = "", textClock[128] = "";
   603: 
   604:             int relativeMinute = currentMinute;
   605:             if (currentMinute > 30) relativeMinute = 60 - currentMinute;
   606: 
   607:             if (relativeMinute < 30)
   608:             {
   609:                 if (relativeMinute >= 20)
   610:                 {
   611:                     strcat_s(textMinutes, sizeofArray(textMinutes), "TWENTY");
   612:                     switch (relativeMinute - 20)
   613:                     {
   614:                         case 9: { strcat_s(textMinutes, sizeofArray(textMinutes), " NINE"); break; }
   615:                         case 8: { strcat_s(textMinutes, sizeofArray(textMinutes), " EIGHT"); break; }
   616:                         case 7: { strcat_s(textMinutes, sizeofArray(textMinutes), " SEVEN"); break; }
   617:                         case 6: { strcat_s(textMinutes, sizeofArray(textMinutes), " SIX"); break; }
   618:                         case 5: { strcat_s(textMinutes, sizeofArray(textMinutes), " FIVE"); break; }
   619:                         case 4: { strcat_s(textMinutes, sizeofArray(textMinutes), " FOUR"); break; }
   620:                         case 3: { strcat_s(textMinutes, sizeofArray(textMinutes), " THREE"); break; }
   621:                         case 2: { strcat_s(textMinutes, sizeofArray(textMinutes), " TWO"); break; }
   622:                         case 1: { strcat_s(textMinutes, sizeofArray(textMinutes), " ONE"); break; }
   623:                         case 0: { break; }
   624:                     }
   625:                 }
   626:                 else if (relativeMinute >= 10)
   627:                 {
   628:                     switch (relativeMinute - 10)
   629:                     {
   630:                         case 9: { strcat_s(textMinutes, sizeofArray(textMinutes), "NINETEEN"); break; }
   631:                         case 8: { strcat_s(textMinutes, sizeofArray(textMinutes), "EIGHTEEN"); break; }
   632:                         case 7: { strcat_s(textMinutes, sizeofArray(textMinutes), "SEVENTEEN"); break; }
   633:                         case 6: { strcat_s(textMinutes, sizeofArray(textMinutes), "SIXTEEN"); break; }
   634:                         case 5: { strcat_s(textMinutes, sizeofArray(textMinutes), "QUARTER "); break; }
   635:                         case 4: { strcat_s(textMinutes, sizeofArray(textMinutes), "FOURTEEN"); break; }
   636:                         case 3: { strcat_s(textMinutes, sizeofArray(textMinutes), "THIRTEEN"); break; }
   637:                         case 2: { strcat_s(textMinutes, sizeofArray(textMinutes), "TWELVE"); break; }
   638:                         case 1: { strcat_s(textMinutes, sizeofArray(textMinutes), "ELEVEN"); break; }
   639:                         case 0: { strcat_s(textMinutes, sizeofArray(textMinutes), "TEN"); break; }
   640:                     }
   641:                 }
   642:                 else if (relativeMinute >= 0)
   643:                 {
   644:                     switch (relativeMinute)
   645:                     {
   646:                         case 9: { strcat_s(textMinutes, sizeofArray(textMinutes), "NINE"); break; }
   647:                         case 8: { strcat_s(textMinutes, sizeofArray(textMinutes), "EIGHT"); break; }
   648:                         case 7: { strcat_s(textMinutes, sizeofArray(textMinutes), "SEVEN"); break; }
   649:                         case 6: { strcat_s(textMinutes, sizeofArray(textMinutes), "SIX"); break; }
   650:                         case 5: { strcat_s(textMinutes, sizeofArray(textMinutes), "FIVE"); break; }
   651:                         case 4: { strcat_s(textMinutes, sizeofArray(textMinutes), "FOUR"); break; }
   652:                         case 3: { strcat_s(textMinutes, sizeofArray(textMinutes), "THREE"); break; }
   653:                         case 2: { strcat_s(textMinutes, sizeofArray(textMinutes), "TWO"); break; }
   654:                         case 1: { strcat_s(textMinutes, sizeofArray(textMinutes), "ONE"); break; }
   655:                     }
   656:                 }
   657: 
   658:                 if (relativeMinute == 1) strcat_s(textInBetween, sizeofArray(textInBetween), " minute ");
   659:                 else if ((relativeMinute > 1) && (relativeMinute != 15)) strcat_s(textInBetween, sizeofArray(textInBetween), " minutes ");
   660: 
   661:                 if ((currentMinute > 0) && (currentMinute <= 30)) strcat_s(textInBetween, sizeofArray(textInBetween), "past ");
   662:                 else if (relativeMinute > 0) strcat_s(textInBetween, sizeofArray(textInBetween), "to ");
   663:             }
   664:             else if (currentMinute == 30)
   665:             {
   666:                 strcat_s(textMinutes, sizeofArray(textMinutes), "HALF");
   667:                 strcat_s(textInBetween, sizeofArray(textInBetween), " past ");
   668:             }
   669: 
   670:             int relativeHour = currentHour;
   671:             if (currentMinute > 30) relativeHour += 1;
   672:             if (relativeHour >= 24) relativeHour -= 24;
   673:             switch (relativeHour)
   674:             {
   675:                 case 12: { strcat_s(textHours, sizeofArray(textHours), "NOON"); break; }
   676:                 case 11: case 23: { strcat_s(textHours, sizeofArray(textHours), "ELEVEN"); break; }
   677:                 case 10: case 22: { strcat_s(textHours, sizeofArray(textHours), "TEN"); break; }
   678:                 case 9: case 21: { strcat_s(textHours, sizeofArray(textHours), "NINE"); break; }
   679:                 case 8: case 20: { strcat_s(textHours, sizeofArray(textHours), "EIGHT"); break; }
   680:                 case 7: case 19: { strcat_s(textHours, sizeofArray(textHours), "SEVEN"); break; }
   681:                 case 6: case 18: { strcat_s(textHours, sizeofArray(textHours), "SIX"); break; }
   682:                 case 5: case 17: { strcat_s(textHours, sizeofArray(textHours), "FIVE"); break; }
   683:                 case 4: case 16: { strcat_s(textHours, sizeofArray(textHours), "FOUR"); break; }
   684:                 case 3: case 15: { strcat_s(textHours, sizeofArray(textHours), "THREE"); break; }
   685:                 case 2: case 14: { strcat_s(textHours, sizeofArray(textHours), "TWO"); break; }
   686:                 case 1: case 13: { strcat_s(textHours, sizeofArray(textHours), "ONE"); break; }
   687:                 case 0: { strcat_s(textHours, sizeofArray(textHours), "MIDNIGHT"); break; }
   688:             }
   689: 
   690:             if ((currentMinute == 0) && (currentHour != 0) && (currentHour != 12)) strcat_s(textAfter, sizeofArray(textAfter), " o'clock");
   691: 
   692:             sprintf_s(textClock, sizeofArray(textClock), "%s%s%s%s", textMinutes, textInBetween, textHours, textAfter);
   693: 
   694:             HFONT widgetFont;
   695:             HGDIOBJ oldFont;
   696: 
   697:             if (pSettings->desktopClockFontDefined)
   698:             {
   699:                 widgetFont = CreateFont((pSettings->desktopClockFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopClockFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopClockFont);
   700:             }
   701:             else
   702:             {
   703:                 int fontHeight = 30 * pSettings->scalingFactorHiDPI;
   704:                 widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_BOLD, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
   705:             }
   706: 
   707:             oldFont = SelectObject(cachedBackground, widgetFont);
   708:             SetBkMode(cachedBackground, TRANSPARENT);
   709:             SIZE size;
   710:             GetTextExtentPoint32(cachedBackground, textClock, strnlen_s(textClock, sizeofArray(textClock)), &size);
   711:             pSettings->desktopClockPlacement.width = size.cx;
   712:             pSettings->desktopClockPlacement.height = size.cy;
   713:             SelectObject(cachedBackground, oldFont);
   714: 
   715:             if (pSettings->desktopClockPlacement.placement != PLACEMENT_DEFAULT)
   716:             {
   717:                 pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
   718:                 CheckWidgetPlacement(&pSettings->desktopClockPlacement);
   719:                 SetRect(&clockRect, pSettings->desktopClockPlacement.x, pSettings->desktopClockPlacement.y, pSettings->desktopClockPlacement.x + pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.y + pSettings->desktopClockPlacement.height);
   720:             }
   721:             else // PLACEMENT_DEFAULT
   722:             {
   723:                 clockRect.left = DesktopCenterX - (pSettings->desktopClockPlacement.width / 2);
   724:                 clockRect.right = clockRect.left + pSettings->desktopClockPlacement.width;
   725: //              if (pSettings->doubleScaleHiDPI) clockRect.top = 32;
   726:                 if (pSettings->scalingFactorHiDPI > 1) clockRect.top = 32;
   727:                 else clockRect.top = 30;
   728:                 clockRect.bottom = clockRect.top + pSettings->desktopClockPlacement.height;
   729: 
   730:                 pSettings->desktopClockPlacement.x = clockRect.left; // ...we keep a copy of clockRect.left to be able to restore it later... (see below)
   731:             }
   732: 
   733:             HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height);
   734:             HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
   735: 
   736:             oldFont = SelectObject(widgetDC, widgetFont);
   737:             SetBkMode(widgetDC, TRANSPARENT);
   738:             RECT widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
   739:             FillRect(widgetDC, &widgetRect, blackBrush);
   740: 
   741:             if (strnlen_s(textMinutes, sizeofArray(textMinutes)) > 0)
   742:             {
   743:                 DrawTextWithEffects(widgetDC, widgetRect, textMinutes, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   744:                 GetTextExtentPoint32(widgetDC, textMinutes, strnlen_s(textMinutes, sizeofArray(textMinutes)), &size);
   745:                 widgetRect.left += size.cx;
   746: 
   747:                 DrawTextWithEffects(widgetDC, widgetRect, textInBetween, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0x7f7f7f, false, 0, false, 0, 0, 0);
   748:                 GetTextExtentPoint32(widgetDC, textInBetween, strnlen_s(textInBetween, sizeofArray(textInBetween)), &size);
   749:                 widgetRect.left += size.cx;
   750:             }
   751: 
   752:             DrawTextWithEffects(widgetDC, widgetRect, textHours, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   753: 
   754:             if (strnlen_s(textAfter, sizeofArray(textAfter)) > 0)
   755:             {
   756:                 GetTextExtentPoint32(widgetDC, textHours, strnlen_s(textHours, sizeofArray(textHours)), &size);
   757:                 widgetRect.left += size.cx;
   758:                 DrawTextWithEffects(widgetDC, widgetRect, textAfter, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0x7f7f7f, false, 0, false, 0, 0, 0);
   759:             }
   760: 
   761:             widgetRect.left = 0;
   762: 
   763:             AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
   764:             SelectObject(widgetDC, oldFont);
   765: 
   766:             BLENDFUNCTION bf;
   767:             ZeroMemory(&bf, sizeof(BLENDFUNCTION));
   768:             bf.BlendOp = AC_SRC_OVER;
   769:             bf.BlendFlags = 0;
   770:             bf.SourceConstantAlpha = pSettings->desktopClockTransparencyAlpha;
   771:             bf.AlphaFormat = AC_SRC_ALPHA;
   772:             AlphaBlend(cachedBackground, clockRect.left, clockRect.top, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, widgetDC, 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, bf);
   773: 
   774:             SelectObject(widgetDC, oldBitmap);
   775:             DeleteObject(widgetBitmap);
   776:             // DeleteObject(oldBitmap); // Not necessary (default stock object)
   777:             DeleteObject(widgetFont);
   778:             // DeleteObject(oldFont); // Not necessary (default stock object)
   779:         }
   780: 
   781:         //====================
   782: 
   783:         else if (!_stricmp(pSettings->desktopClockType, "Binary")) // -> Bit clock
   784:         {
   785:             wchar_t temp;
   786:             int currentDigit;
   787:             int clockBitWidth, clockBitHeight;
   788: 
   789:             if (pSettings->desktopClockPlacement.placement != PLACEMENT_DEFAULT)
   790:             {
   791:                 if (pSettings->doubleScaleHiDPI)
   792:                 {
   793:                     pSettings->desktopClockPlacement.width = 396;
   794:                     pSettings->desktopClockPlacement.height = 228;
   795:                     pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
   796:                     CheckWidgetPlacement(&pSettings->desktopClockPlacement);
   797:                     clockBitWidth = 96;
   798:                     clockBitHeight = 54;
   799:                 }
   800:                 else
   801:                 {
   802:                     pSettings->desktopClockPlacement.width = 204;
   803:                     pSettings->desktopClockPlacement.height = 120;
   804:                     pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
   805:                     CheckWidgetPlacement(&pSettings->desktopClockPlacement);
   806:                     clockBitWidth = 48;
   807:                     clockBitHeight = 27;
   808:                 }
   809:                 SetRect(&clockRect, pSettings->desktopClockPlacement.x, pSettings->desktopClockPlacement.y, pSettings->desktopClockPlacement.x + pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.y + pSettings->desktopClockPlacement.height);
   810:             }
   811:             else // PLACEMENT_DEFAULT
   812:             {
   813:                 if (pSettings->doubleScaleHiDPI) SetRect(&clockRect, DesktopCenterX-198, 49, DesktopCenterX-102, 103);
   814:                 else SetRect(&clockRect, DesktopCenterX-102, 49, DesktopCenterX-54, 76);
   815:             }
   816: 
   817:             HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height);
   818:             HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
   819: 
   820:             int mediumTransparencyAlpha = pSettings->desktopClockTransparencyAlpha / 2;
   821:             int lowTransparencyAlpha = pSettings->desktopClockTransparencyAlpha / 8;
   822: 
   823:             HBRUSH widgetHighBrush = CreateSolidBrush(0xffffff);
   824:             HBRUSH widgetMediumBrush = CreateSolidBrush(0x7f7f7f);
   825:             HBRUSH widgetLowBrush = CreateSolidBrush(0x1f1f1f);
   826: 
   827:             RECT widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
   828:             FillRect(widgetDC, &widgetRect, blackBrush);
   829:             SetRect(&widgetRect, 0, 0, clockBitWidth, clockBitHeight);
   830: 
   831:             for (int x=1; x<=4; x++)
   832:             {
   833:                 switch (x)
   834:                 {
   835:                     case 1: { temp = CurrentTime[0]; break; }
   836:                     case 2: { temp = CurrentTime[1]; break; }
   837:                     case 3: { temp = CurrentTime[3]; break; }
   838:                     case 4: { temp = CurrentTime[4]; break; }
   839:                     default: { break; }
   840:                 }
   841:                 currentDigit = _wtoi(&temp);
   842: 
   843:                 for (int y=1; y<=4; y++)
   844:                 {
   845:                     if ((x == 1) && ((y == 1) || (y == 2))) FillRect(widgetDC, &widgetRect, widgetLowBrush); // ...because the first digit can only be 0-2...
   846:                     else
   847:                     {
   848:                         if ((y == 1) && (currentDigit & 8)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
   849:                         else if ((y == 2) && (currentDigit & 4)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
   850:                         else if ((y == 3) && (currentDigit & 2)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
   851:                         else if ((y == 4) && (currentDigit & 1)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
   852:                         else FillRect(widgetDC, &widgetRect, widgetMediumBrush);
   853:                     }
   854: 
   855:                     if (pSettings->doubleScaleHiDPI) OffsetRect(&widgetRect, 0, 58);
   856:                     else OffsetRect(&widgetRect, 0, 31);
   857:                 }
   858: 
   859:                 if (pSettings->doubleScaleHiDPI) OffsetRect(&widgetRect, 100, -232);
   860:                 else OffsetRect(&widgetRect, 52, -124);
   861:             }
   862: 
   863:             widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
   864: 
   865:             AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
   866: 
   867:             BLENDFUNCTION bf;
   868:             ZeroMemory(&bf, sizeof(BLENDFUNCTION));
   869:             bf.BlendOp = AC_SRC_OVER;
   870:             bf.BlendFlags = 0;
   871:             bf.SourceConstantAlpha = pSettings->desktopClockTransparencyAlpha;
   872:             bf.AlphaFormat = AC_SRC_ALPHA;
   873:             AlphaBlend(cachedBackground, clockRect.left, clockRect.top, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, widgetDC, 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, bf);
   874: 
   875:             DeleteObject(widgetHighBrush);
   876:             DeleteObject(widgetMediumBrush);
   877:             DeleteObject(widgetLowBrush);
   878:             SelectObject(widgetDC, oldBitmap);
   879:             DeleteObject(widgetBitmap);
   880:             // DeleteObject(oldBitmap); // Not necessary (default stock object)
   881:         }
   882:     }
   883: 
   884:     //====================
   885: 
   886:     // Draw greeting...?
   887:     if (!pSettings->desktopGreetingHidden)
   888:     {
   889:         if (pSettings->desktopDisableGDIplusForWidgets) // -> Use regular GDI to draw the widget
   890:         {
   891:             HFONT widgetFont;
   892:             HGDIOBJ oldFont;
   893: 
   894:             if (pSettings->desktopGreetingFontDefined)
   895:             {
   896:                 widgetFont = CreateFont((pSettings->desktopGreetingFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopGreetingFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopGreetingFont);
   897:             }
   898:             else
   899:             {
   900:                 int fontHeight = 30 * pSettings->scalingFactorHiDPI;
   901:                 widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
   902:             }
   903: 
   904:             oldFont = SelectObject(cachedBackground, widgetFont);
   905:             SetBkMode(cachedBackground, TRANSPARENT);
   906:             SIZE size;
   907:             GetTextExtentPoint32W(cachedBackground, CurrentGreeting, wcslen(CurrentGreeting), &size);
   908:             pSettings->desktopGreetingPlacement.width = size.cx;
   909:             pSettings->desktopGreetingPlacement.height = size.cy;
   910:             SelectObject(cachedBackground, oldFont);
   911: 
   912:             if (pSettings->desktopGreetingPlacement.placement != PLACEMENT_DEFAULT)
   913:             {
   914:                 pSettings->PositionFromPlacement(&pSettings->desktopGreetingPlacement);
   915:                 CheckWidgetPlacement(&pSettings->desktopGreetingPlacement);
   916:                 SetRect(&greetingRect, pSettings->desktopGreetingPlacement.x, pSettings->desktopGreetingPlacement.y, pSettings->desktopGreetingPlacement.x + pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.y + pSettings->desktopGreetingPlacement.height);
   917:             }
   918:             else // PLACEMENT_DEFAULT
   919:             {
   920:                 greetingRect.left = DesktopCenterX - (pSettings->desktopGreetingPlacement.width / 2);
   921:                 greetingRect.right = greetingRect.left + pSettings->desktopGreetingPlacement.width;
   922:                 if (pSettings->desktopGreetingFontDefined) greetingRect.top = 339;
   923:                 else greetingRect.top = 320;
   924:                 greetingRect.bottom = greetingRect.top + pSettings->desktopGreetingPlacement.height;
   925:             }
   926: 
   927:             HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height);
   928:             HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
   929: 
   930:             oldFont = SelectObject(widgetDC, widgetFont);
   931:             SetBkMode(widgetDC, TRANSPARENT);
   932:             RECT widgetRect = { 0, 0, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height };
   933:             FillRect(widgetDC, &widgetRect, blackBrush);
   934:             DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentGreeting, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   935:             AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
   936:             SelectObject(widgetDC, oldFont);
   937: 
   938:             BLENDFUNCTION bf;
   939:             ZeroMemory(&bf, sizeof(BLENDFUNCTION));
   940:             bf.BlendOp = AC_SRC_OVER;
   941:             bf.BlendFlags = 0;
   942:             bf.SourceConstantAlpha = pSettings->desktopGreetingTransparencyAlpha;
   943:             bf.AlphaFormat = AC_SRC_ALPHA;
   944:             AlphaBlend(cachedBackground, greetingRect.left, greetingRect.top, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height, widgetDC, 0, 0, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height, bf);
   945: 
   946:             SelectObject(widgetDC, oldBitmap);
   947:             DeleteObject(widgetBitmap);
   948:             // DeleteObject(oldBitmap); // Not necessary (default stock object)
   949:             DeleteObject(widgetFont);
   950:             // DeleteObject(oldFont); // Not necessary (default stock object)
   951:         }
   952:         else // if (!pSettings->desktopDisableGDIplusForWidgets) -> Use GDI+ to draw the widget
   953:         {
   954:             Graphics graphics(cachedBackground);
   955:             graphics.SetCompositingMode(CompositingModeSourceOver);
   956:             graphics.SetCompositingQuality(CompositingQualityHighQuality);
   957: 
   958:             wchar_t fontFamilyWC[MAX_PATH];
   959:             MultiByteToWideChar(CP_ACP, 0, pSettings->desktopGreetingFont, (strnlen_s(pSettings->desktopGreetingFont, sizeofArray(pSettings->desktopGreetingFont))+1), fontFamilyWC, sizeofArray(fontFamilyWC));
   960:             FontFamily fontFamily(fontFamilyWC);
   961:             FontStyle fontStyle;
   962:             if (pSettings->desktopGreetingFontWeight == FW_BOLD) fontStyle = FontStyleBold;
   963:             else fontStyle = FontStyleRegular;
   964:             Font font(&fontFamily, (Gdiplus::REAL)(pSettings->desktopGreetingFontHeight*pSettings->scalingFactorHiDPI), fontStyle, UnitPixel);
   965: 
   966:             StringFormat stringFormat;
   967:             stringFormat.SetAlignment(StringAlignmentNear);
   968:             stringFormat.SetLineAlignment(StringAlignmentNear);
   969:             stringFormat.SetTrimming(StringTrimmingNone);
   970: 
   971:             PointF originPoint(0.0f, 0.0f);
   972:             RectF boundingRect;
   973:             graphics.MeasureString(CurrentGreeting, wcsnlen_s(CurrentGreeting, sizeofArray(CurrentGreeting)), &font, originPoint, &stringFormat, &boundingRect);
   974:             pSettings->desktopGreetingPlacement.width = (int)boundingRect.Width;
   975:             pSettings->desktopGreetingPlacement.height = (int)boundingRect.Height;
   976: 
   977:             if (pSettings->desktopGreetingPlacement.placement != PLACEMENT_DEFAULT)
   978:             {
   979:                 pSettings->PositionFromPlacement(&pSettings->desktopGreetingPlacement);
   980:                 CheckWidgetPlacement(&pSettings->desktopGreetingPlacement);
   981:                 SetRect(&greetingRect, pSettings->desktopGreetingPlacement.x, pSettings->desktopGreetingPlacement.y, pSettings->desktopGreetingPlacement.x + pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.y + pSettings->desktopGreetingPlacement.height);
   982:             }
   983:             else // PLACEMENT_DEFAULT
   984:             {
   985:                 greetingRect.left = DesktopCenterX - (pSettings->desktopGreetingPlacement.width / 2);
   986:                 greetingRect.right = greetingRect.left + pSettings->desktopGreetingPlacement.width;
   987:                 if (pSettings->desktopGreetingFontDefined) greetingRect.top = 339;
   988:                 else greetingRect.top = 320;
   989:                 greetingRect.bottom = greetingRect.top + pSettings->desktopGreetingPlacement.height;
   990:             }
   991: 
   992:             PointF pointF((Gdiplus::REAL)pSettings->desktopGreetingPlacement.x, (Gdiplus::REAL)pSettings->desktopGreetingPlacement.y);
   993:             SolidBrush brush(Color(pSettings->desktopGreetingTransparencyAlpha, GetRValue(pSettings->desktopGreetingColor), GetGValue(pSettings->desktopGreetingColor), GetBValue(pSettings->desktopGreetingColor)));
   994:             graphics.SetTextRenderingHint(TextRenderingHintAntiAlias); // (Note: Tried TextRenderingHintClearTypeGridFit/TextRenderingHintAntiAliasGridFit/etc here but for some reason it looked really ugly, so sticking with TextRenderingHintAntiAlias for now.)
   995:             graphics.DrawString(CurrentGreeting, -1, &font, pointF, &brush);
   996:         }
   997:     }
   998: 
   999:     //====================
  1000: 
  1001:     if (pSettings->desktopDisableGDIplusForWidgets)
  1002:     {
  1003:         SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
  1004:         AlphaFromRGB(cachedBackground, r, 1, 255, true, pSettings->desktopWidgetColor);
  1005:         AlphaApply(cachedBackground, r);
  1006:     }
  1007: 
  1008:     //====================
  1009: 
  1010:     DeleteObject(blackBrush);
  1011:     DeleteDC(widgetDC);
  1012:     if (bufbmp) DeleteObject(bufbmp);
  1013: 
  1014:     POINT pt;
  1015:     pt.x = DesktopX, pt.y = DesktopY;
  1016:     POINT ptSrc;
  1017:     ptSrc.x = 0, ptSrc.y = 0;
  1018: 
  1019:     BLENDFUNCTION bf;
  1020:     bf.BlendOp = AC_SRC_OVER;
  1021:     bf.BlendFlags = 0;
  1022:     bf.AlphaFormat = AC_SRC_ALPHA;
  1023:     bf.SourceConstantAlpha = (unsigned char)255;
  1024: 
  1025:     SIZE windowSize = { DesktopWidth, DesktopHeight };
  1026:     BOOL result = UpdateLayeredWindow(hDesktopWnd, NULL, &pt, &windowSize, cachedBackground, &ptSrc, 0, &bf, ULW_ALPHA);
  1027: /*
  1028:     if (!cachedBackground) SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)"xoblite -> Desktop -> Invalid cachedBackground!");
  1029: 
  1030:     if (result == 0)
  1031:     {
  1032:         int error = GetLastError();
  1033:         char msg[255];
  1034:         sprintf_s(msg, sizeofArray(msg), "xoblite -> Desktop -> UpdateLayeredWindow failed! [error code %d]", error);
  1035:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
  1036:     }
  1037:     else
  1038: */
  1039:     {
  1040:         // Finally, we move the desktop window to the bottom of the regular z-order... (i.e. just above the Windows desktop in our case)
  1041:         SetWindowPos(hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
  1042:     }
  1043: }
  1044: 
  1045: //===========================================================================
  1046: // Function: GetDimensions
  1047: // Purpose: Get the dimensions of the available desktop space ("work area")
  1048: //===========================================================================
  1049: 
  1050: void Desktop::GetDimensions()
  1051: {
  1052: /*
  1053:     if (!pSettings->explorerHidden)
  1054:     {
  1055:         RECT workArea;
  1056:         SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
  1057:         DesktopWidth = workArea.right - workArea.left;
  1058:         DesktopHeight = workArea.bottom - workArea.top;
  1059:         DesktopX = workArea.left;
  1060:         DesktopY = workArea.top;
  1061:     }
  1062:     else
  1063: */
  1064:     {
  1065:         DesktopWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
  1066:         DesktopHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  1067:         DesktopX = GetSystemMetrics(SM_XVIRTUALSCREEN);
  1068:         DesktopY = GetSystemMetrics(SM_YVIRTUALSCREEN);
  1069:     }
  1070: 
  1071:     DesktopCenterX = DesktopX + (DesktopWidth / 2);
  1072:     DesktopCenterY = DesktopY + (DesktopHeight / 2);
  1073: }
  1074: 
  1075: //===========================================================================
  1076: // Function: LoadDesktopWallpaper
  1077: // Purpose: ...
  1078: //===========================================================================
  1079: 
  1080: void Desktop::LoadWallpaper(LPSTR imagePath)
  1081: {
  1082:     if (!FileExists(imagePath)) return;
  1083: 
  1084:     if (wallpaper.bitmap != NULL) DeleteObject(wallpaper.bitmap);
  1085: 
  1086:     // Load the image using GDI+ to add support for JPG, PNG etc...
  1087:     // (nb. regular GDI LoadImage() only supports BMP images. GDI+ startup/shutdown is performed "globally" in Blackbox.cpp.)
  1088:     wchar_t imagePathWC[MAX_PATH];
  1089:     MultiByteToWideChar(CP_ACP, 0, imagePath, strlen(imagePath) + 1, imagePathWC, MAX_PATH);
  1090:     Bitmap* bitmap = new Bitmap(imagePathWC, FALSE);
  1091: 
  1092:     // ...fetch some of its properties...
  1093:     wallpaper.width = bitmap->GetWidth();
  1094:     wallpaper.height = bitmap->GetHeight();
  1095:     PixelFormat pf = bitmap->GetPixelFormat();
  1096:     if ((pf == PixelFormat32bppARGB) || (pf == PixelFormat32bppPARGB) || (pf == PixelFormat64bppARGB) || (pf == PixelFormat64bppPARGB)) wallpaper.argb = true;
  1097:     else wallpaper.argb = false;
  1098: 
  1099:     // ...and finally create our target GDI HBITMAP from the loaded GDI+ Bitmap...
  1100:     bitmap->GetHBITMAP(0, &wallpaper.bitmap);
  1101: 
  1102:     delete bitmap;
  1103: }
  1104: 
  1105: //===========================================================================
  1106: // Function: DrawWallpaper
  1107: // Purpose: ...
  1108: //===========================================================================
  1109: 
  1110: void Desktop::DrawWallpaper(HDC hdc)
  1111: {
  1112:     if (wallpaper.bitmap == NULL) return;
  1113: 
  1114:     HDC buf = CreateCompatibleDC(hdc);
  1115:     DeleteObject(SelectObject(buf, wallpaper.bitmap));
  1116:     SetStretchBltMode(hdc, HALFTONE);
  1117:     StretchBlt(hdc, 0, 0, DesktopWidth, DesktopHeight, buf, 0, 0, wallpaper.width, wallpaper.height, SRCCOPY);
  1118: 
  1119:     DeleteDC(buf);
  1120: }
  1121: 
  1122: //===========================================================================
  1123: // Function: CheckWidgetPlacement
  1124: // Purpose: Adjusts desktop widget placements to account for various factors
  1125: //===========================================================================
  1126: 
  1127: void Desktop::CheckWidgetPlacement(Placement* widget)
  1128: {
  1129:     // Adjust from screen relative to desktop relative coordinates...
  1130:     // (cf. e.g. Explorer taskbar along left or top screen edges)
  1131:     widget->x -= DesktopX;
  1132:     widget->y -= DesktopY;
  1133: 
  1134:     // Adjust for the semi-transparent desktop border... (if visible)
  1135:     if (pSettings->desktopBorderHidden) return;
  1136: 
  1137:     if ((widget->placement == PLACEMENT_TOP_LEFT)
  1138:      || (widget->placement == PLACEMENT_CENTER_LEFT)
  1139:      || (widget->placement == PLACEMENT_BOTTOM_LEFT))
  1140:     {
  1141:         widget->x += 20;
  1142:     }
  1143: 
  1144:     if ((widget->placement == PLACEMENT_TOP_RIGHT)
  1145:      || (widget->placement == PLACEMENT_CENTER_RIGHT)
  1146:      || (widget->placement == PLACEMENT_BOTTOM_RIGHT))
  1147:     {
  1148:         widget->x -= 20;
  1149:     }
  1150: 
  1151:     if ((widget->placement == PLACEMENT_TOP_LEFT)
  1152:      || (widget->placement == PLACEMENT_TOP_RIGHT))
  1153:     {
  1154:         widget->y += 20;
  1155:     }
  1156: 
  1157:     if ((widget->placement == PLACEMENT_TOP_CENTER) && !pSettings->desktopWorkspaceIndicatorHidden)
  1158:     {
  1159:         // Stay clear of the workspace indicators if they're visible...
  1160:         widget->y += 39;
  1161:     }
  1162: 
  1163:     if ((widget->placement == PLACEMENT_BOTTOM_LEFT)
  1164:      || (widget->placement == PLACEMENT_BOTTOM_CENTER)
  1165:      || (widget->placement == PLACEMENT_BOTTOM_RIGHT))
  1166:     {
  1167:         widget->y -= 20;
  1168:     }
  1169: }
  1170: 
  1171: /*
  1172: //===========================================================================
  1173: // Function: UpdatePosition
  1174: // Purpose: ...
  1175: //===========================================================================
  1176: 
  1177: void Desktop::UpdatePosition()
  1178: {
  1179:     // Fetch the new size and position parameters for our window...
  1180:     GetDimensions();
  1181: 
  1182:     // Resize the window and move it to its new position...
  1183: //  MoveWindow(hDesktopWnd, DesktopX, DesktopY, DesktopWidth, DesktopHeight, true);
  1184: //  SetWindowPos(hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
  1185: 
  1186:     // Apply transparency depending on xoblite.rc setting...
  1187: //  SetTransparency(hDesktopWnd, pSettings->desktopTransparencyAlpha);
  1188: 
  1189:     // Update the desktop window...
  1190:     UpdateDesktopWindow();
  1191: }
  1192: */
  1193: 
  1194: //===========================================================================
  1195: // Function: MouseAndDropHandler
  1196: // Purpose: Supporting function to DesktopWndProc() [see above]
  1197: //===========================================================================
  1198: 
  1199: LRESULT Desktop::MouseAndDropHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1200: {
  1201:     switch (uMsg)
  1202:     {
  1203:         //====================
  1204: 
  1205:         case WM_LBUTTONUP:
  1206:         {
  1207:             if (pMenuCommon)
  1208:             {
  1209: //              if (GetAsyncKeyState(VK_MENU) & 0x8000)
  1210: //              {
  1211: //                  SendMessage(GetBBWnd(), BB_MENU, 0, 0); // Alt+LeftClick -> Main menu
  1212: //                  PlaySoundFX(SFX_MENU_SHOW);
  1213: //              }
  1214: //              else
  1215:                 {
  1216:                     // First we set focus to the toolbar to prevent the focus from going back to the previous task after the menu is closed...
  1217: //                  if (pSettings->followActive) SetForegroundWindow(pToolbar->hToolbarWnd);
  1218: //                  if (pSettings->followActive) SetForegroundWindow(hDesktopWnd);
  1219:                     // Move the desktop window to the bottom of the application z-order... (i.e. just above the Windows desktop in our case)
  1220: //                  SetWindowPos(hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
  1221:                     SetWindowPos(hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
  1222: 
  1223:                     if (pMenuCommon) pMenuCommon->Hide();
  1224:                 }
  1225:             }
  1226:         }
  1227:         break;
  1228: 
  1229:         case WM_LBUTTONDOWN: { } break;
  1230: 
  1231:             //====================
  1232: 
  1233:         case WM_RBUTTONUP:
  1234:         {
  1235:             // Has the user defined an Right Click command override?
  1236:             if (strnlen_s(pSettings->desktopRClickOverride, sizeofArray(pSettings->desktopRClickOverride)) > 0)
  1237:             {
  1238:                 if (GetAsyncKeyState(VK_MENU) & 0x8000)
  1239:                 {
  1240:                     SendMessage(GetBBWnd(), BB_MENU, 0, 0); // Alt+Right Click -> Main menu
  1241:                     PlaySoundFX(SFX_MENU_SHOW);
  1242:                 }
  1243:                 else BBSmartExecute(pSettings->desktopRClickOverride); // Right Click without modifiers -> User defined command override
  1244:             }
  1245: 
  1246:             // We otherwise provide a couple of Right Click + key modifier actions for people with only a *2* button mouse...
  1247:             else if (GetAsyncKeyState(VK_MENU) & 0x8000)
  1248:             {
  1249:                 if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && (GetAsyncKeyState(VK_SHIFT) & 0x8000))
  1250:                 {
  1251:                     SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Ctrl+Alt+Shift+RightClick -> Configuration menu
  1252:                     PlaySoundFX(SFX_MENU_SHOW);
  1253:                 }
  1254:                 else if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite Random Wallpaper"); // Ctrl+Alt+RightClick -> Apply random wallpaper (note: matching Modifier+MidClick)
  1255:                 else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) BBSmartExecute("@xoblite Random Style"); // Shift+Alt+RightClick -> Apply random style (note: matching Modifier+MidClick)
  1256:                 else
  1257:                 {
  1258:                     SendMessage(GetBBWnd(), BB_MENU, 5, 0); // Alt+RightClick -> Styles menu
  1259:                     PlaySoundFX(SFX_MENU_SHOW);
  1260:                 }
  1261:             }
  1262:             else if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
  1263:             {
  1264:                 SendMessage(GetBBWnd(), BB_MENU, 6, 0); // Ctrl+RightClick -> Themes menu
  1265:                 PlaySoundFX(SFX_MENU_SHOW);
  1266:             }
  1267:             else if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
  1268:             {
  1269:                 SendMessage(GetBBWnd(), BB_MENU, 1, 0); // Shift+RightClick -> Workspaces menu
  1270:                 PlaySoundFX(SFX_MENU_SHOW);
  1271: //              BBSmartExecute("@xoblite Random Wallpaper"); // Shift+RightClick -> Apply random wallpaper
  1272:             }
  1273:             else
  1274:             {
  1275:                 SendMessage(GetBBWnd(), BB_MENU, 0, 0); // RightClick without modifiers -> Main menu
  1276:                 PlaySoundFX(SFX_MENU_SHOW);
  1277:             }
  1278:         }
  1279:         break;
  1280: 
  1281:         case WM_RBUTTONDOWN: { } break;
  1282: 
  1283:             //====================
  1284: 
  1285:         case WM_MBUTTONUP:
  1286:         {
  1287:             // Has the user defined an Mid Click command override?
  1288:             if (strnlen_s(pSettings->desktopMClickOverride, sizeofArray(pSettings->desktopMClickOverride)) > 0)
  1289:             {
  1290:                 if (GetAsyncKeyState(VK_MENU) & 0x8000)
  1291:                 {
  1292:                     SendMessage(GetBBWnd(), BB_MENU, 5, 0); // Alt+Mid Click -> Styles menu
  1293:                     PlaySoundFX(SFX_MENU_SHOW);
  1294:                 }
  1295:                 else BBSmartExecute(pSettings->desktopMClickOverride); // Mid Click without modifiers -> User defined command override
  1296:             }
  1297: 
  1298:             // We otherwise provide a couple of Mid Click + key modifier actions for people with only a *3* button mouse...
  1299:             else if (GetAsyncKeyState(VK_MENU) & 0x8000)
  1300:             {
  1301:                 if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite Random Wallpaper"); // Ctrl+Alt+MidClick -> Apply random wallpaper (note: matching Modifier+RightClick)
  1302:                 else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) BBSmartExecute("@xoblite Random Style"); // Shift+Alt+MidClick -> Apply random style (note: matching Modifier+RightClick)
  1303:                 else
  1304:                 {
  1305:                     SendMessage(GetBBWnd(), BB_MENU, 6, 0); // Alt+MidClick -> Themes menu
  1306:                     PlaySoundFX(SFX_MENU_SHOW);
  1307:                 }
  1308:             }
  1309:             else
  1310:             {
  1311:                 if (GetAsyncKeyState(VK_CONTROL) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 1, 0); // Ctrl+MidClick -> Workspaces menu
  1312:                 else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Shift+MidClick -> Configuration menu
  1313:                 else SendMessage(GetBBWnd(), BB_MENU, 5, 0); // MidClick without modifiers -> Styles menu
  1314:                 PlaySoundFX(SFX_MENU_SHOW);
  1315:             }
  1316:         }
  1317:         break;
  1318: 
  1319:         case WM_MBUTTONDOWN: { } break;
  1320: 
  1321:         //====================
  1322: 
  1323:         case WM_XBUTTONDOWN:
  1324:         {
  1325:             if (HIWORD(wParam) == XBUTTON1)
  1326:             {
  1327:                 if (strnlen_s(pSettings->desktopX1ClickOverride, sizeofArray(pSettings->desktopX1ClickOverride)) > 0) // Has the user defined an X1 click command override?
  1328:                 {
  1329:                     if (GetAsyncKeyState(VK_MENU) & 0x8000)
  1330:                     {
  1331:                         SendMessage(GetBBWnd(), BB_MENU, 6, 0); // Alt+X1 Click -> Themes menu
  1332:                         PlaySoundFX(SFX_MENU_SHOW);
  1333:                     }
  1334:                     else BBSmartExecute(pSettings->desktopX1ClickOverride); // X1 Click without modifiers -> User defined command override
  1335:                 }
  1336:                 else if (GetAsyncKeyState(VK_MENU) & 0x8000) BBSmartExecute("@xoblite Random Wallpaper"); // Alt+X1 Click -> Apply random wallpaper
  1337:                 else
  1338:                 {
  1339:                     // if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite About"); // Placeholder, possibly for use defined command?
  1340:                     if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Shift+X1 Click -> Configuration menu
  1341:                     else SendMessage(GetBBWnd(), BB_MENU, 6, 0); // X1 Click without modifiers -> Themes menu
  1342:                     PlaySoundFX(SFX_MENU_SHOW);
  1343:                 }
  1344:             }
  1345:             else if (HIWORD(wParam) == XBUTTON2)
  1346:             {
  1347:                 if (strnlen_s(pSettings->desktopX2ClickOverride, sizeofArray(pSettings->desktopX2ClickOverride)) > 0) // Has the user defined an X2 click command override?
  1348:                 {
  1349:                     if (GetAsyncKeyState(VK_MENU) & 0x8000)
  1350:                     {
  1351:                         SendMessage(GetBBWnd(), BB_MENU, 1, 0); // Alt+X2 Click -> Workspaces menu
  1352:                         PlaySoundFX(SFX_MENU_SHOW);
  1353:                     }
  1354:                     else BBSmartExecute(pSettings->desktopX2ClickOverride); // X2 Click without modifiers -> User defined command override
  1355:                 }
  1356:                 else if (GetAsyncKeyState(VK_MENU) & 0x8000) BBSmartExecute("@xoblite Random Style"); // Alt+X2 Click -> Apply random style
  1357:                 else
  1358:                 {
  1359:                     // if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite About"); // Placeholder, possibly for use defined command?
  1360:                     if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Shift+X2 Click -> Configuration menu
  1361:                     else SendMessage(GetBBWnd(), BB_MENU, 1, 0); // X2 Click without modifiers -> Workspaces menu
  1362:                     PlaySoundFX(SFX_MENU_SHOW);
  1363:                 }
  1364:             }
  1365:         }
  1366:         break;
  1367: 
  1368:         case WM_XBUTTONUP: { } break;
  1369: 
  1370:         //====================
  1371: 
  1372:         case WM_MOUSEWHEEL:
  1373:         {
  1374:             if (pSettings->mousewheelChanging)
  1375:             {
  1376:                 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) pWorkspaces->NextWorkspace(); // Mousewheel down -> Move to the next workspace...
  1377:                 else pWorkspaces->PreviousWorkspace(); // Mousewheel up -> Move to the previous workspace...
  1378:             }
  1379:         }
  1380:         break;
  1381: 
  1382:         // case WM_MOUSEHWHEEL: { } break;
  1383: 
  1384:         //====================
  1385: 
  1386:         case WM_DROPFILES:
  1387:         {
  1388:             return ParseDropFiles(hWnd, wParam);
  1389:         }
  1390:         break;
  1391: 
  1392:         //====================
  1393: 
  1394:         default:
  1395:             return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1396: 
  1397:         //====================
  1398:     }
  1399:     return 0;
  1400: }
  1401: 
  1402: //===========================================================================
  1403: 
  1404: bool Desktop::GetClockText(bool forceUpdate)
  1405: {
  1406:     time(&systemTime);
  1407:     localtime_s(&localTime, &systemTime);
  1408:     currentHour = localTime.tm_hour;
  1409:     currentMinute = localTime.tm_min;
  1410: 
  1411:     // Only update the desktop clock if the current minute has changed...
  1412:     if ((currentMinute != displayedMinute) || forceUpdate)
  1413:     {
  1414:         // Get the time string for the regular clock...
  1415:         if (IsInString(pSettings->toolbarClockFormat, "%I")) wcsftime(CurrentTime, sizeof(CurrentTime), L"%I:%M %p", &localTime); // 12-hour format including AM/PM indicator
  1416:         else wcsftime(CurrentTime, sizeof(CurrentTime), L"%H:%M", &localTime); // 24-hour format
  1417:         // Remember minute shown in display, now wait until minute changes before updating again...
  1418:         displayedMinute = currentMinute;
  1419: 
  1420:         // Get the weekday string...
  1421:         wcsftime(CurrentWeekday, sizeof(CurrentWeekday), L"%A", &localTime); // Day of the week as per the current locale (e.g. "Saturday")
  1422: 
  1423:         // Get the configurable date string... (nb. we need to convert the format string from ANSI to Unicode first)
  1424:         wchar_t desktopDateFormatInUnicode[MAX_LINE_LENGTH];
  1425:         MultiByteToWideChar(CP_UTF8, 0, pSettings->desktopDateFormat, strnlen_s(pSettings->desktopDateFormat, sizeofArray(pSettings->desktopDateFormat))+1, desktopDateFormatInUnicode, MAX_LINE_LENGTH);
  1426:         wcsftime(CurrentDate, sizeof(CurrentDate), desktopDateFormatInUnicode, &localTime); // Configurable date string (cf. the toolbar clock's format string and related options)
  1427: 
  1428:         if (!forceUpdate)
  1429:         {
  1430:             if (currentMinute == 0) PlaySoundFX(SFX_CLOCK_HOUR);
  1431:             else if (currentMinute == 30) PlaySoundFX(SFX_CLOCK_HALFHOUR);
  1432:         }
  1433: 
  1434:         return true;
  1435:     }
  1436:     else return false;
  1437: }
  1438: 
  1439: //===========================================================================
  1440: 
  1441: bool Desktop::GetGreetingText(bool forceUpdate)
  1442: {
  1443:     time(&systemTime);
  1444:     localtime_s(&localTime, &systemTime);
  1445: 
  1446:     if (localTime.tm_hour < 6) wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingNight);                // 00-06 -> "Good night" (default)
  1447:     else if (localTime.tm_hour < 12) wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingMorning);        // 06-12 -> "Good morning" (default)
  1448:     else if (localTime.tm_hour < 18) wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingAfternoon);      // 12-18 -> "Good afternoon" (default)
  1449:     else wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingEvening);                                    // 18-24 -> "Good evening" (default)
  1450: 
  1451:     return true;
  1452: }
  1453: 
  1454: //===========================================================================
  1455: // Function: FullscreenDetected
  1456: // Purpose: Hide the desktop (no updating) if an application goes fullscreen
  1457: //===========================================================================
  1458: 
  1459: void Desktop::FullscreenDetected(bool fullscreen)
  1460: {
  1461:     if (fullscreen)
  1462:     {
  1463:         fullscreenDetected = true;
  1464:         ShowWindow(hDesktopWnd, SW_HIDE);
  1465:     }
  1466:     else
  1467:     {
  1468:         fullscreenDetected = false;
  1469:         ShowWindow(hDesktopWnd, SW_SHOWNOACTIVATE);
  1470:         UpdateDesktopWindow();
  1471:     }
  1472: }
  1473: 
  1474: //===========================================================================
  1475: // Function: ShadeWindow
  1476: // Purpose: Toggles a window shaded/unshaded
  1477: //          (see also supporting functions below)
  1478: //===========================================================================
  1479: 
  1480: void Desktop::ShadeWindow()
  1481: {
  1482:     HWND currentWnd = pTaskbar->GetActiveWindow();
  1483:     BOOL isMaximized = IsZoomed(currentWnd);
  1484: 
  1485:     RECT r;
  1486:     GetWindowRect(currentWnd, &r);
  1487:     int titleBarHeight = GetSystemMetrics(SM_CYMIN);
  1488:     bool isUnshaded = (r.bottom-r.top > titleBarHeight);
  1489: 
  1490: //  i = IsFlaggedShaded(currentWnd);
  1491:     int i = (int)(GetProp(currentWnd, "BBNormalHeight") != NULL);
  1492: 
  1493:     if (isMaximized)
  1494:     {
  1495:         if (isUnshaded) WinShade(currentWnd, -1, isMaximized);
  1496:         else WinUnshade(currentWnd, -1, isMaximized);
  1497:     }
  1498:     else
  1499:     {
  1500:         if (i != -1) // Was the window found in our list?
  1501:         {
  1502:             if (isUnshaded) // Shade the window...
  1503:             {
  1504:                 RemoveProp(currentWnd, "BBNormalHeight");
  1505:                 WinShade(currentWnd, -1, isMaximized);
  1506:             }
  1507:             else // Unshade the window...
  1508:             {
  1509:                 WinUnshade(currentWnd, i, isMaximized);
  1510:             }
  1511:         }
  1512:         else WinShade(currentWnd, i, isMaximized);
  1513:     }
  1514: }
  1515: 
  1516: //===========================================================================
  1517: // Function: WinShade
  1518: // Purpose: Shrink a window to only its titlebar,
  1519: //          while remembering its original height
  1520: //===========================================================================
  1521: 
  1522: void Desktop::WinShade(HWND hwnd, int listIndex, bool maximized)
  1523: {
  1524:     if (listIndex != -1) return;
  1525: 
  1526:     RECT r;
  1527:     SetForegroundWindow(hwnd);
  1528:     GetWindowRect(hwnd, &r);
  1529: 
  1530:     // Remember the window's original height...
  1531:     if (!maximized) SetProp(hwnd, "BBNormalHeight", (HANDLE)(r.bottom - r.top));
  1532:     // ...and then shrink it vertically to only show its titlebar...
  1533:     SetWindowPos(hwnd, HWND_TOP, r.left, r.top, r.right-r.left, GetSystemMetrics(SM_CYMIN), SWP_NOOWNERZORDER | SWP_NOSENDCHANGING);
  1534: }
  1535: 
  1536: //===========================================================================
  1537: // Function: WinUnshade
  1538: // Purpose: Restores a previously shaded window to its original height
  1539: //===========================================================================
  1540: 
  1541: void Desktop::WinUnshade(HWND hwnd, int listIndex, bool maximized)
  1542: {
  1543:     if (listIndex == -1 && !maximized) return;
  1544: 
  1545:     RECT r;
  1546:     int height;
  1547:     SetForegroundWindow(hwnd);
  1548: 
  1549:     if (maximized)
  1550:     {
  1551:         // This is a maximized window, so its restored height should be the height of the screen's work area...
  1552:         RECT workArea;
  1553:         SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
  1554:         height = workArea.bottom - workArea.top;
  1555:     }
  1556:     else
  1557:     {
  1558:         // This is a non-maximized window, so we restore it to the original height previously saved...
  1559:         height = (long)GetProp(hwnd, "BBNormalHeight");
  1560:         RemoveProp(hwnd, "BBNormalHeight");
  1561:     }
  1562: 
  1563:     if (height == 0) return;
  1564: 
  1565:     GetWindowRect(hwnd, &r);
  1566:     MoveWindow(hwnd, r.left, r.top, r.right-r.left, height, true);
  1567:     InvalidateRect(hwnd, NULL, false);
  1568: }
  1569: 
  1570: //===========================================================================
  1571: // Functions: GrowWindowHeight / GrowWindowWidth
  1572: // Purpose: ...
  1573: //===========================================================================
  1574: 
  1575: void Desktop::GrowWindowHeight()
  1576: {
  1577:     HWND currentWnd = pTaskbar->GetActiveWindow();
  1578:     RECT r, workArea;
  1579:     GetWindowRect(currentWnd, &r);
  1580:     SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
  1581:     MoveWindow(currentWnd, r.left, 0, r.right-r.left, workArea.bottom-workArea.top, true);
  1582:     InvalidateRect(currentWnd, NULL, false);
  1583:     SetForegroundWindow(currentWnd);
  1584: }
  1585: 
  1586: //====================
  1587: 
  1588: void Desktop::GrowWindowWidth()
  1589: {
  1590:     HWND currentWnd = pTaskbar->GetActiveWindow();
  1591:     RECT r, workArea;
  1592:     GetWindowRect(currentWnd, &r);
  1593:     SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
  1594:     MoveWindow(currentWnd, 0, r.top, workArea.right-workArea.left, r.bottom-r.top, true);
  1595:     GetWindowRect(currentWnd, &r);
  1596:     SetForegroundWindow(currentWnd);
  1597: }
  1598: 
  1599: //===========================================================================
  1600: // Functions: LowerWindow / RaiseWindow
  1601: // Purpose: ...
  1602: //===========================================================================
  1603: 
  1604: void Desktop::LowerWindow()
  1605: {
  1606:     HWND currentWnd = pTaskbar->GetActiveWindow();
  1607: 
  1608:     // First we fetch the bottom window...
  1609:     HWND bottomWnd = GetWindow(hBlackboxWnd, GW_HWNDLAST);
  1610:     HWND hProgManWnd = FindWindow("Progman", "Program Manager");
  1611:     if (bottomWnd == hProgManWnd) bottomWnd = GetWindow(bottomWnd, GW_HWNDPREV);
  1612: 
  1613:     while (!IsWindowVisible(bottomWnd) || IsIconic(bottomWnd) || (GetWindowTextLength(bottomWnd) == 0))
  1614:     {
  1615:         bottomWnd = GetWindow(bottomWnd, GW_HWNDPREV);
  1616:         if (bottomWnd == hProgManWnd) bottomWnd = GetWindow(bottomWnd, GW_HWNDPREV);
  1617:     }
  1618: 
  1619:     // ...and then we put the current window behind that...
  1620:     if (IsWindow(currentWnd) && IsWindow(bottomWnd))
  1621:     {
  1622:         SetWindowPos(currentWnd, bottomWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING);
  1623:     }
  1624: }
  1625: 
  1626: //====================
  1627: 
  1628: void Desktop::RaiseWindow()
  1629: {
  1630:     HWND currentWnd = pTaskbar->GetActiveWindow();
  1631:     // Raise the current window to the top...
  1632:     SetWindowPos(currentWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING);
  1633: }
  1634: 
  1635: //===========================================================================
  1636: