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: