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 "Wallpaper.h" 
    32: 
    33: extern Settings* pSettings;
    34: 
    35: //===========================================================================
    36: // Functions: ExecuteRootCommand
    37: // Purpose: Executes style rootCommands using the built-in
    38: //          bsetbg.exe and bsetroot.exe replacement functions (see below)
    39: //===========================================================================
    40: 
    41: void Wallpaper::ExecuteRootCommand()
    42: {
    43:     if (IsInString(pSettings->rootCommand, "bsetbg")) ParseBSetBG();
    44:     else if (IsInString(pSettings->rootCommand, "bsetroot")) ParseBSetRoot();
    45: 
    46:     pSettings->wallpaperPerWorkspaceSemaphore = false; // Reset if previously set by switchToDesktop() in Workspaces.cpp (i.e. when wallpaper per workspace is enabled)
    47: }
    48: 
    49: //===========================================================================
    50: // Function: ParseBSetBG
    51: // Purpose: Parses bsetbg.exe syntax rootCommands
    52: //===========================================================================
    53: 
    54: void Wallpaper::ParseBSetBG()
    55: {
    56:     // bsetbg.exe <-tile|-full|-center> <image.jpg>
    57: 
    58:     char szOption_1[MAX_LINE_LENGTH], szOption_2[MAX_LINE_LENGTH], szOption_3[MAX_LINE_LENGTH], szOption_4[MAX_LINE_LENGTH];
    59:     char* tokens[4];
    60:     tokens[0] = szOption_1;
    61:     tokens[1] = szOption_2;
    62:     tokens[2] = szOption_3;
    63:     tokens[3] = szOption_4;
    64:     szOption_1[0] = szOption_2[0] = szOption_3[0] = szOption_4[0] = '\0';
    65:     BBTokenize(pSettings->rootCommand, tokens, 4, NULL);
    66: 
    67:     SetWallpaper(szOption_3, szOption_2);
    68: }
    69: 
    70: //===========================================================================
    71: // Function: ParseBSetRoot
    72: // Purpose: Parses bsetroot.exe syntax rootCommands
    73: //===========================================================================
    74: 
    75: void Wallpaper::ParseBSetRoot()
    76: {
    77:     // bsetroot.exe -solid <colour>
    78:     // bsetroot.exe -bitmap <tile|stretch|center> <image.bmp>
    79:     // bsetroot.exe -bitmap <tile|stretch|center> <image.bmp> -solid <colour>
    80:     // bsetroot.exe -gradient <type+interlaced> -from <colour> -to <colour>
    81:     // bsetroot.exe -mod <x> <y> -fg <colour> -bg <colour>
    82: 
    83:     char szOption_1[MAX_LINE_LENGTH], szOption_2[MAX_LINE_LENGTH], szOption_3[MAX_LINE_LENGTH], szOption_4[MAX_LINE_LENGTH], szOption_5[MAX_LINE_LENGTH], szOption_6[MAX_LINE_LENGTH], szOption_7[MAX_LINE_LENGTH], szOption_8[MAX_LINE_LENGTH];
    84:     char* tokens[8];
    85:     tokens[0] = szOption_1;
    86:     tokens[1] = szOption_2;
    87:     tokens[2] = szOption_3;
    88:     tokens[3] = szOption_4;
    89:     tokens[4] = szOption_5;
    90:     tokens[5] = szOption_6;
    91:     tokens[6] = szOption_7;
    92:     tokens[7] = szOption_8;
    93:     szOption_1[0] = szOption_2[0] = szOption_3[0] = szOption_4[0] = szOption_5[0] = szOption_6[0] = szOption_7[0] = szOption_8[0] = '\0';
    94:     BBTokenize(pSettings->rootCommand, tokens, 8, NULL);
    95: 
    96:     char msg[350];
    97: 
    98:     //====================
    99: 
   100:     // Execute any -solid commands... (see above)
   101:     if (IsInString(pSettings->rootCommand, "-solid"))
   102:     {
   103:         COLORREF solidColor;
   104: 
   105:         if (!IsInString(pSettings->rootCommand, "-bitmap")) // bsetroot.exe -solid <colour>
   106:         {
   107:             SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (PVOID)"(None)", SPIF_SENDCHANGE); // Remove any existing wallpaper
   108:             solidColor = ReadColor(NULL, szOption_3, szOption_3);
   109:         }
   110:         else solidColor = ReadColor(NULL, szOption_6, szOption_6); // bsetroot.exe -bitmap <tile|stretch|center> <image.bmp> -solid <colour>
   111: 
   112:         int lpaElements[1] = { COLOR_BACKGROUND };
   113:         COLORREF lpaRgbValues[1] = { solidColor };
   114: 
   115:         // Set the new desktop colour...
   116:         if (!pSettings->wallpaperPerWorkspaceSemaphore)
   117:         {
   118:             sprintf_s(msg, sizeofArray(msg), "Setting the desktop colour to %s .", szOption_3);
   119:             SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
   120:         }
   121:         SetSysColors(1, lpaElements, lpaRgbValues);
   122: 
   123:         if (!IsInString(pSettings->rootCommand, "-bitmap")) return;
   124:     }
   125: 
   126:     //====================
   127: 
   128:     // Execute any -bitmap commands... (see above)
   129:     if (IsInString(pSettings->rootCommand, "-bitmap"))
   130:     {
   131:         SetWallpaper(szOption_4, szOption_3);
   132:         return;
   133:     }
   134: 
   135:     //====================
   136: 
   137:     // Execute any -mod commands... (see above)
   138:     if (IsInString(szOption_2, "-mod"))
   139:     {
   140:         int x = 4, y = 4;
   141:         x = atoi(szOption_3);
   142:         y = atoi(szOption_4);
   143: 
   144:         COLORREF fg = 0xffffff, bg = 0x000000;
   145:         if (IsInString(szOption_5, "-fg")) fg = ReadColor(NULL, szOption_6, szOption_6);
   146:         if (IsInString(szOption_5, "-bg")) bg = ReadColor(NULL, szOption_6, szOption_6);
   147:         if (IsInString(szOption_7, "-fg")) fg = ReadColor(NULL, szOption_8, szOption_8);
   148:         if (IsInString(szOption_7, "-bg")) bg = ReadColor(NULL, szOption_8, szOption_8);
   149: 
   150:         // sprintf_s(msg, sizeofArray(msg), "bsetroot %s %d %d %s %s %s %s", szOption_2, x, y, szOption_5, szOption_6, szOption_7, szOption_8);
   151:         // SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)msg);
   152: 
   153:         SetModula(x, y, fg, bg);
   154:         return;
   155:     }
   156: 
   157:     //====================
   158: 
   159:     // Execute any -gradient commands... (see above)
   160:     if (IsInString(szOption_2, "-gradient"))
   161:     {
   162:         // First we check the gradient "type"...
   163:         int type = B_HORIZONTAL; // B_HORIZONTAL is the default type...
   164:         if (IsInString(szOption_3, "vertical")) type = B_VERTICAL;
   165:         else if (IsInString(szOption_3, "crossdiagonal")) type = B_CROSSDIAGONAL; // Note: Must be checked before "diagonal"... :)
   166:         else if (IsInString(szOption_3, "diagonal")) type = B_DIAGONAL;
   167:         else if (IsInString(szOption_3, "pipecross")) type = B_PIPECROSS;
   168:         else if (IsInString(szOption_3, "elliptic")) type = B_ELLIPTIC;
   169:         else if (IsInString(szOption_3, "rectangle")) type = B_RECTANGLE;
   170:         else if (IsInString(szOption_3, "pyramid")) type = B_PYRAMID;
   171: 
   172:         // ...then we check the "interlaced" flag...
   173:         bool interlaced = false;
   174:         if (IsInString(szOption_3, "interlaced")) interlaced = true;
   175: 
   176:         // ...and finally the "from" and "to" colours...
   177:         COLORREF from = 0xffffff, to = 0x000000;
   178:         if (IsInString(szOption_4, "-from")) from = GetColorRef(szOption_5);
   179:         if (IsInString(szOption_6, "-to")) to = GetColorRef(szOption_7);
   180: 
   181:         SetGradient(type, interlaced, from, to);
   182:         return;
   183:     }
   184: 
   185:     //====================
   186: 
   187:     // Unknown bsetroot command
   188:     SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)"Error parsing style rootCommand! (unknown bsetroot command)");
   189: }
   190: 
   191: //===========================================================================
   192: // Function: GetColorRef
   193: // Purpose: Simple helper function to ParseBSetRoot() -gradient ...
   194: //===========================================================================
   195: 
   196: COLORREF Wallpaper::GetColorRef(LPSTR colorAsString)
   197: {
   198:     COLORREF color;
   199:     if (colorAsString[0] == '#') color = strtol(&colorAsString[1], NULL, 16);
   200:     else color = strtol(colorAsString, NULL, 16);
   201: 
   202:     return RGB(GetBValue(color), GetGValue(color), GetRValue(color)); // Convert to BGR format...
   203: }
   204: 
   205: //===========================================================================
   206: // Function: SetDesktopColor
   207: // Purpose: Sets the desktop colour
   208: //===========================================================================
   209: 
   210: void Wallpaper::SetDesktopColor(COLORREF color)
   211: {
   212:     SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (PVOID)"(None)", SPIF_SENDCHANGE); // Remove any existing wallpaper
   213:     int lpaElements[1] = { COLOR_BACKGROUND };
   214:     COLORREF lpaRgbValues[1] = { color };
   215: 
   216:     char colorAsString[10];
   217:     sprintf_s(colorAsString, sizeofArray(colorAsString), "#%02x%02x%02x", (BYTE)GetRValue(color), (BYTE)GetGValue(color), (BYTE)GetBValue(color));
   218: 
   219:     if (!pSettings->wallpaperPerWorkspaceSemaphore)
   220:     {
   221:         char msg[50];
   222:         sprintf_s(msg, sizeofArray(msg), "Setting the desktop colour to %s.", colorAsString);
   223:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
   224:     }
   225: 
   226:     // Remember the manually applied desktop colour as the "last applied" rootCommand in case Designer mode wants to Save it later on... (see Broams.cpp)
   227:     sprintf_s(pSettings->rootCommand, sizeofArray(pSettings->rootCommand), "bsetroot -solid %s", colorAsString);
   228:     CopyStringToClipboard(pSettings->rootCommand);
   229: 
   230:     SetSysColors(1, lpaElements, lpaRgbValues);
   231: }
   232: 
   233: //===========================================================================
   234: // Function: SetWallpaper
   235: // Purpose: Sets a JPG/JPEG/PNG/BMP image to be the new desktop wallpaper
   236: //===========================================================================
   237: 
   238: void Wallpaper::SetWallpaper(LPSTR wpPath, LPSTR wpStyle)
   239: {
   240:     if (strchr(wpPath, '\"')) StrRemoveEncap(wpPath);
   241:     if (strchr(wpPath, '$')) ReplaceShellFolders(wpPath);
   242:     if (strchr(wpPath, '%')) ReplaceEnvVars(wpPath);
   243: 
   244:     char path[MAX_PATH];
   245:     if (IsInString(wpPath, ":")) strcpy_s(path, sizeofArray(path), wpPath); // Full path (i.e. includes ":" which is a reserved character for drive names)
   246:     else if (!IsInString(wpPath, "\\")) // Filename only -> Look for the image in the default wallpaper folder
   247:     {
   248:         strcpy_s(path, sizeofArray(path), pSettings->wallpapersFolder);
   249:         strcat_s(path, sizeofArray(path), "\\");
   250:         strcat_s(path, sizeofArray(path), wpPath);
   251:     }
   252:     else if (!IsInString(wpPath, pSettings->SF_blackboxPath)) // Relative path from the Blackbox folder
   253:     {
   254:         strcpy_s(path, sizeofArray(path), pSettings->SF_blackboxPath);
   255:         strcat_s(path, sizeofArray(path), "\\");
   256:         strcat_s(path, sizeofArray(path), wpPath);
   257:     }
   258: 
   259:     if (!FileExists(path))
   260:     {
   261:         char msg[MAX_LINE_LENGTH];
   262:         sprintf_s(msg, sizeofArray(msg), "Error setting the desktop wallpaper - File not found! (%s).", path);
   263:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
   264:         return;
   265:     }
   266: 
   267:     //====================
   268: 
   269:     int pointer = strnlen_s(path, sizeofArray(path)) - 4;
   270: 
   271:     // We only need to check the last 4 characters to get the extension...
   272:     // (we allow JPG/JPEG, PNG, and BMP files as wallpaper images)
   273:     if (!_stricmp(&path[pointer], ".jpg")
   274:         || !_stricmp(&path[pointer], ".png")
   275:         || !_stricmp(&path[pointer], ".bmp")
   276:         || !_stricmp(&path[pointer], "jpeg"))
   277:     {
   278:         /*
   279:             if (GetAsyncKeyState(VK_CONTROL) & 0x8000) strcpy_s(arguments, sizeofArray(arguments), "-center "); // Is the control key held down? -> Centered mode
   280:             else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) strcpy_s(arguments, sizeofArray(arguments), "-full "); // Is the shift key held down? -> Stretched mode
   281:             else strcpy_s(arguments, sizeofArray(arguments), "-tile "); // Default -> Tiled mode
   282:         */
   283: 
   284:         char wallpaperStyle[10];
   285: 
   286:         // Update the registry keys defining if the wallpaper should be tiled/centered/stretched...
   287:         // * WallpaperStyle key -> Fill 10, Fit 6, Stretch 2, Tile 0, Center 0, Span 22.
   288:         // * TileWallpaper key -> Set to 1 if Tile, 0 if any other setting.
   289:         HKEY key;
   290:         if (RegOpenKeyEx(HKEY_CURRENT_USER, "Control Panel\\Desktop\\", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
   291:         {
   292:             LSTATUS result;
   293:             if (IsInString(wpStyle, "tile"))
   294:             {
   295:                 result = RegSetValueEx(key, "WallpaperStyle", 0, REG_SZ, (unsigned char*)"0", 2);
   296:                 result = RegSetValueEx(key, "TileWallpaper", 0, REG_SZ, (unsigned char*)"1", 2);
   297:                 strcpy_s(wallpaperStyle, sizeofArray(wallpaperStyle), "tiled");
   298:             }
   299:             else if (IsInString(wpStyle, "center"))
   300:             {
   301:                 result = RegSetValueEx(key, "WallpaperStyle", 0, REG_SZ, (unsigned char*)"1", 2);
   302:                 result = RegSetValueEx(key, "TileWallpaper", 0, REG_SZ, (unsigned char*)"0", 2);
   303:                 strcpy_s(wallpaperStyle, sizeofArray(wallpaperStyle), "centered");
   304:             }
   305:             else if (IsInString(wpStyle, "span")) // -> A single wallpaper spanning across multiple monitors
   306:             {
   307:                 result = RegSetValueEx(key, "WallpaperStyle", 0, REG_SZ, (unsigned char*)"22", 2);
   308:                 result = RegSetValueEx(key, "TileWallpaper", 0, REG_SZ, (unsigned char*)"0", 2);
   309:                 strcpy_s(wallpaperStyle, sizeofArray(wallpaperStyle), "spanned");
   310:             }
   311:             else // if (IsInString(wpStyle, "stretch") || IsInString(wpStyle, "full"))
   312:             {
   313:                 result = RegSetValueEx(key, "WallpaperStyle", 0, REG_SZ, (unsigned char*)"2", 2);
   314:                 result = RegSetValueEx(key, "TileWallpaper", 0, REG_SZ, (unsigned char*)"0", 2);
   315:                 strcpy_s(wallpaperStyle, sizeofArray(wallpaperStyle), "stretched");
   316:             }
   317: 
   318:             RegCloseKey(key);
   319: 
   320:             if (result != ERROR_SUCCESS)
   321:             {
   322:                 SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)"Error writing to the wallpaper style registry keys!");
   323:                 return;
   324:             }
   325:         }
   326:         else
   327:         {
   328:             SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)"Error opening the wallpaper style registry keys!");
   329:             return;
   330:         }
   331: 
   332:         // ...set the image to be the new desktop wallpaper...
   333:         if (!pSettings->wallpaperPerWorkspaceSemaphore)
   334:         {
   335:             char msg[1024];
   336:             if (IsInString(path, pSettings->SF_currentThemePath)) sprintf_s(msg, sizeofArray(msg), "Setting the desktop wallpaper to $CurrentTheme$%s (%s).", &path[strnlen_s(pSettings->SF_currentThemePath, sizeofArray(pSettings->SF_currentThemePath))], wallpaperStyle);
   337:             else if (IsInString(path, pSettings->SF_blackboxPath)) sprintf_s(msg, sizeofArray(msg), "Setting the desktop wallpaper to $Blackbox$%s (%s).", &path[strnlen_s(pSettings->SF_blackboxPath, sizeofArray(pSettings->SF_blackboxPath))], wallpaperStyle);
   338:             else sprintf_s(msg, sizeofArray(msg), "Setting the desktop wallpaper to %s (%s).", path, wallpaperStyle);
   339:             SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
   340:         }
   341:         SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (void*)path, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
   342: 
   343:         if (!pSettings->wallpaperPerWorkspaceSemaphore)
   344:         {
   345:             // ...and copy a matching rootCommand to the clipboard for
   346:             // possibly updating of a style in case it was a good match! =]
   347:             char* stylePtr = wpStyle;
   348:             if (wpStyle[0] == '-') stylePtr++;
   349:             char rootCmd[MAX_LINE_LENGTH];
   350:             if (IsInString(path, pSettings->SF_currentThemePath)) sprintf_s(rootCmd, sizeofArray(rootCmd), "rootCommand: bsetbg -%s \"$CurrentTheme$%s\"", stylePtr, &path[strnlen_s(pSettings->SF_currentThemePath, sizeofArray(pSettings->SF_currentThemePath))]);
   351:             else if (IsInString(path, pSettings->SF_blackboxPath)) sprintf_s(rootCmd, sizeofArray(rootCmd), "rootCommand: bsetbg -%s \"%s\"", stylePtr, &path[strnlen_s(pSettings->SF_blackboxPath, sizeofArray(pSettings->SF_blackboxPath)) + 1]);
   352:             else sprintf_s(rootCmd, sizeofArray(rootCmd), "rootCommand: bsetbg -%s \"%s\"", stylePtr, path);
   353:             CopyStringToClipboard(rootCmd);
   354: 
   355:             // Finally, remember the manually applied wallpaper as the "last applied" rootCommand in case Designer mode wants to Save it later on... (see Broams.cpp)
   356:             strcpy_s(pSettings->rootCommand, sizeofArray(pSettings->rootCommand), &rootCmd[13]);
   357:         }
   358:     }
   359:     else SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)"Error setting the desktop wallpaper - only JPG/JPEG, PNG and BMP files are supported.");
   360: }
   361: 
   362: //===========================================================================
   363: // Function: SetModula
   364: // Purpose: Sets a bsetroot "modula" as the new desktop wallpaper
   365: //===========================================================================
   366: 
   367: void Wallpaper::SetModula(int x, int y, COLORREF fg, COLORREF bg)
   368: {
   369:     PAINTSTRUCT ps;
   370:     HWND dwnd = GetDesktopWindow();
   371:     HDC hdc = BeginPaint(dwnd, &ps);
   372:     HDC buf = CreateCompatibleDC(NULL);
   373:     PBYTE pixels;
   374: 
   375: //  int width = GetSystemMetrics(SM_CXSCREEN);
   376: //  int height = GetSystemMetrics(SM_CYSCREEN);
   377:     int width = x;
   378:     int height = y;
   379:     if (pSettings->scalingFactorHiDPI > 1) // Adapt bsetroot parameters to the current HiDPI scaling factor? (-> aligned with scaling aware style rendering etc)
   380:     {
   381:         width *= pSettings->scalingFactorHiDPI;
   382:         height *= pSettings->scalingFactorHiDPI;
   383:     }
   384: 
   385:     BITMAPINFOHEADER bv4info;
   386:     bv4info.biSize = sizeof(BITMAPINFOHEADER);
   387:     bv4info.biWidth = width;
   388:     bv4info.biHeight = height;
   389:     bv4info.biPlanes = 1;
   390:     bv4info.biBitCount = 32;
   391:     bv4info.biCompression = BI_RGB;
   392: 
   393:     BITMAPINFO bminfo;
   394:     bminfo.bmiHeader = bv4info;
   395: 
   396:     HBITMAP bitmap = CreateDIBSection(hdc, &bminfo, DIB_RGB_COLORS, (PVOID*)&pixels, NULL, 0);
   397:     if (bitmap) SelectObject(buf, bitmap);
   398: 
   399:     //====================
   400: 
   401:     int count = 0;
   402: //  for (int vertical = 1; vertical < height; vertical++)
   403:     for (int vertical = 0; vertical < height; vertical++)
   404:     {
   405:         for (int horizontal = 0; horizontal < width; horizontal++)
   406:         {
   407: //          if ((vertical % y) == 0)
   408:             if ((vertical == 0) || (pSettings->doubleScaleHiDPI && (vertical == 1)))
   409:             {
   410:                 pixels[count + 2] = GetRValue(fg);
   411:                 pixels[count + 1] = GetGValue(fg);
   412:                 pixels[count] = GetBValue(fg);
   413:             }
   414:             else
   415:             {
   416: //              if ((horizontal % x) == 0)
   417:                 if ((horizontal == 0) || (pSettings->doubleScaleHiDPI && (horizontal == 1)))
   418:                 {
   419:                     pixels[count + 2] = GetRValue(fg);
   420:                     pixels[count + 1] = GetGValue(fg);
   421:                     pixels[count] = GetBValue(fg);
   422:                 }
   423:                 else
   424:                 {
   425:                     pixels[count + 2] = GetRValue(bg);
   426:                     pixels[count + 1] = GetGValue(bg);
   427:                     pixels[count] = GetBValue(bg);
   428:                 }
   429:             }
   430:             count += 4;
   431:         }
   432:     }
   433: 
   434:     //====================
   435: 
   436:     char path[MAX_PATH];
   437:     GetBlackboxPath(path, sizeof(path));
   438:     strcat_s(path, sizeofArray(path), "bsetroot.bmp");
   439: 
   440:     PBITMAPINFO pbmpi = CreateBitmapInfoStruct(bitmap);
   441:     CreateBMPFile(path, pbmpi, bitmap);
   442: 
   443:     DeleteDC(buf);
   444:     DeleteObject(bitmap);
   445:     EndPaint(dwnd, &ps);
   446: 
   447:     SetWallpaper(path, "tile");
   448: }
   449: 
   450: //===========================================================================
   451: // Function: SetGradient
   452: // Purpose: Sets a bsetroot "gradient" as the new desktop wallpaper
   453: //===========================================================================
   454: 
   455: void Wallpaper::SetGradient(int type, bool interlaced, COLORREF from, COLORREF to)
   456: {
   457:     SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)"Sorry, bsetroot -gradient is not yet supported... (stay tuned).");
   458:     return;
   459: }
   460: 
   461: //===========================================================================
   462: // Function: CreateBitmapInfoStruct
   463: // Purpose: Support function to SetModula (see above)
   464: // (this code is from the original "bsetroot.exe" part of Blackbox for Windows,
   465: // which was in turn based on slightly modified sample code from MSDN)
   466: //===========================================================================
   467: 
   468: PBITMAPINFO Wallpaper::CreateBitmapInfoStruct(HBITMAP hBitmap)
   469: {
   470:     BITMAP bmp;
   471:     PBITMAPINFO pbmi;
   472: 
   473:     // Retrieve the bitmap's color format, width and height...
   474:     if (!GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmp)) return NULL;
   475: 
   476:     // Convert the bitmap's color format to depth in bits...
   477:     WORD cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
   478:     if (cClrBits == 1) cClrBits = 1;
   479:     else if (cClrBits <= 4) cClrBits = 4;
   480:     else if (cClrBits <= 8) cClrBits = 8;
   481:     else if (cClrBits <= 16) cClrBits = 16;
   482:     else if (cClrBits <= 24) cClrBits = 24;
   483:     else cClrBits = 32;
   484: 
   485:     // Allocate memory for the BITMAPINFO structure... (this contains a BITMAPINFOHEADER structure and an array of RGBQUAD data structures) 
   486:     if (cClrBits != 24) pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) * (1 << cClrBits)));
   487:     else pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)); // There is no RGBQUAD array for the 24-bit-per-pixel format...
   488: 
   489:     // Initialize the fields in the BITMAPINFO structure...
   490:     pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   491:     pbmi->bmiHeader.biWidth = bmp.bmWidth;
   492:     pbmi->bmiHeader.biHeight = bmp.bmHeight;
   493:     pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
   494:     pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
   495:     pbmi->bmiHeader.biCompression = BI_RGB; // The bitmap is not compressed
   496:     pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits + 31) & ~31) / 8 * pbmi->bmiHeader.biHeight;
   497:     if (cClrBits < 16) pbmi->bmiHeader.biClrUsed = (1 << cClrBits);
   498:     pbmi->bmiHeader.biClrImportant = 0;
   499: 
   500:     return pbmi;
   501: }
   502: 
   503: //===========================================================================
   504: // Function: CreateBMPFile
   505: // Purpose: Support function to SetModula (see above)
   506: // (this code is from the original "bsetroot.exe" part of Blackbox for Windows,
   507: // which was in turn based on slightly modified sample code from MSDN)
   508: //===========================================================================
   509: 
   510: void Wallpaper::CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBitmap)
   511: {
   512:     HANDLE hf;                  // File handle 
   513:     BITMAPFILEHEADER hdr;       // Bitmap file-header 
   514:     PBITMAPINFOHEADER pbih;     // Bitmap info-header 
   515:     LPBYTE lpBits;              // Memory pointer 
   516:     DWORD dwTotal;              // Total count of bytes 
   517:     DWORD cb;                   // Incremental count of bytes 
   518:     BYTE* hp;                   // Byte pointer 
   519:     DWORD dwTmp;
   520: 
   521:     HDC hDC;
   522: 
   523:     pbih = (PBITMAPINFOHEADER)pbi;
   524:     lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
   525:     if (!lpBits) return;
   526: 
   527:     hDC = CreateCompatibleDC(NULL);
   528: 
   529:     if (!GetDIBits(hDC, hBitmap, 0, (WORD)pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS))
   530:     {
   531:         DeleteDC(hDC);
   532:         GlobalFree((HGLOBAL)lpBits);
   533:     }
   534: 
   535:     // Create the .bmp file...
   536:     hf = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD)0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
   537:     if (hf != INVALID_HANDLE_VALUE)
   538:     {
   539:         hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
   540: 
   541:         // Calculate the size of the entire file...
   542:         hdr.bfSize = (DWORD)(sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
   543:         hdr.bfReserved1 = 0;
   544:         hdr.bfReserved2 = 0;
   545: 
   546:         // Calculate the offset to the array of color indices...
   547:         hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
   548: 
   549:         // Copy the BITMAPFILEHEADER into the file...
   550:         if (WriteFile(hf, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), (LPDWORD)&dwTmp, NULL))
   551:         {
   552:             // Copy the BITMAPINFOHEADER and RGBQUAD array into the file...
   553:             if (WriteFile(hf, (LPVOID)pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof(RGBQUAD), (LPDWORD)&dwTmp, (NULL)))
   554:             {
   555:                 // Copy the array of color indices into the file...
   556:                 dwTotal = cb = pbih->biSizeImage;
   557:                 hp = lpBits;
   558:                 if (WriteFile(hf, (LPSTR)hp, (int)cb, (LPDWORD)&dwTmp, NULL))
   559:                 {
   560:                     // Close the file...
   561:                     CloseHandle(hf);
   562:                 }
   563:             }
   564:         }
   565:     }
   566: 
   567:     DeleteDC(hDC);
   568:     GlobalFree((HGLOBAL)lpBits);
   569: }
   570: 
   571: //===========================================================================
   572: