From: Sergei Chernyadyev serg.cherniadjev@gmail.com
--- dlls/shell32/systray.c | 135 ++++++++++++++++++++++++------------ programs/explorer/systray.c | 32 ++++++++- 2 files changed, 123 insertions(+), 44 deletions(-)
diff --git a/dlls/shell32/systray.c b/dlls/shell32/systray.c index 443bf470b2c..a34a5ad84c7 100644 --- a/dlls/shell32/systray.c +++ b/dlls/shell32/systray.c @@ -61,6 +61,7 @@ struct notify_data /* platform-independent format for NOTIFYICONDATA */ WCHAR szInfoTitle[64]; DWORD dwInfoFlags; GUID guidItem; + struct notify_data_icon balloon_icon_info; /* balloon icon bitmap info */ BYTE icon_data[]; };
@@ -123,6 +124,58 @@ BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid) return Shell_NotifyIconW(dwMessage, &nidW); }
+ +/************************************************************************* + * get_bitmap_info Helper function for filling BITMAP structs and calculating buffer size in bits + */ +static void get_bitmap_info( ICONINFO* iconinfo, BITMAP* bmMask, BITMAP* bmColour, LONG* cbMaskBits, LONG* cbColourBits ) +{ + if ((iconinfo->hbmMask && !GetObjectW(iconinfo->hbmMask, sizeof(*bmMask), bmMask)) || + (iconinfo->hbmColor && !GetObjectW(iconinfo->hbmColor, sizeof(*bmColour), bmColour))) + { + DeleteObject(iconinfo->hbmMask); + if (iconinfo->hbmColor) DeleteObject(iconinfo->hbmColor); + memset(iconinfo, 0, sizeof(*iconinfo)); + return; + } + + if (iconinfo->hbmMask) + *cbMaskBits = (bmMask->bmPlanes * bmMask->bmWidth * bmMask->bmHeight * bmMask->bmBitsPixel + 15) / 16 * 2; + if (iconinfo->hbmColor) + *cbColourBits = (bmColour->bmPlanes * bmColour->bmWidth * bmColour->bmHeight * bmColour->bmBitsPixel + 15) / 16 * 2; +} + + +/************************************************************************* + * fill_icon_info Helper function for filling struct image metadata and buffer with bitmap data + */ +static void fill_icon_info( const BITMAP* bmMask, const BITMAP* bmColour, LONG cbMaskBits, LONG cbColourBits, + ICONINFO* iconinfo, struct notify_data_icon* msg_icon_info, BYTE* image_data_buffer ) +{ + if (iconinfo->hbmColor) + { + msg_icon_info->width = bmColour->bmWidth; + msg_icon_info->height = bmColour->bmHeight; + msg_icon_info->planes = bmColour->bmPlanes; + msg_icon_info->bpp = bmColour->bmBitsPixel; + } + else + { + msg_icon_info->width = bmMask->bmWidth; + msg_icon_info->height = bmMask->bmHeight / 2; + msg_icon_info->planes = 1; + msg_icon_info->bpp = 1; + } + + GetBitmapBits(iconinfo->hbmMask, cbMaskBits, image_data_buffer); + if (iconinfo->hbmColor) + { + GetBitmapBits(iconinfo->hbmColor, cbColourBits, image_data_buffer + cbMaskBits); + DeleteObject(iconinfo->hbmColor); + } + DeleteObject(iconinfo->hbmMask); +} + /************************************************************************* * Shell_NotifyIconW [SHELL32.298] */ @@ -132,6 +185,11 @@ BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW nid) COPYDATASTRUCT cds; struct notify_data data_buffer; struct notify_data *data = &data_buffer; + ICONINFO iconinfo = { 0 }, ballooniconinfo = { 0 }; + BITMAP bmMask, bmBalloonMask; + BITMAP bmColour, bmBalloonColour; + LONG cbMaskBits = 0, balloonCbMaskBits = 0; + LONG cbColourBits = 0, balloonCbColourBits = 0; BOOL ret;
TRACE("dwMessage = %ld, nid->cbSize=%ld\n", dwMessage, nid->cbSize); @@ -165,63 +223,54 @@ BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW nid) /* FIXME: if statement only needed because we don't support interprocess * icon handles */ if (nid->uFlags & NIF_ICON) - { - ICONINFO iconinfo; - BITMAP bmMask; - BITMAP bmColour; - LONG cbMaskBits; - LONG cbColourBits = 0; - BYTE *buffer; - - if (!GetIconInfo(nid->hIcon, &iconinfo)) - goto noicon; - - if (!GetObjectW(iconinfo.hbmMask, sizeof(bmMask), &bmMask) || - (iconinfo.hbmColor && !GetObjectW(iconinfo.hbmColor, sizeof(bmColour), &bmColour))) - { - DeleteObject(iconinfo.hbmMask); - if (iconinfo.hbmColor) DeleteObject(iconinfo.hbmColor); - goto noicon; - } + GetIconInfo(nid->hIcon, &iconinfo); + + if ((nid->uFlags & NIF_INFO) && (nid->dwInfoFlags & NIIF_USER)) + GetIconInfo(nid->hBalloonIcon, &ballooniconinfo); + + get_bitmap_info( &iconinfo, &bmMask, &bmColour, &cbMaskBits, &cbColourBits ); + cds.cbData += cbMaskBits + cbColourBits; + + get_bitmap_info( &ballooniconinfo, &bmBalloonMask, &bmBalloonColour, &balloonCbMaskBits, &balloonCbColourBits ); + cds.cbData += balloonCbMaskBits + balloonCbColourBits;
- cbMaskBits = (bmMask.bmPlanes * bmMask.bmWidth * bmMask.bmHeight * bmMask.bmBitsPixel + 15) / 16 * 2; - if (iconinfo.hbmColor) - cbColourBits = (bmColour.bmPlanes * bmColour.bmWidth * bmColour.bmHeight * bmColour.bmBitsPixel + 15) / 16 * 2; - cds.cbData = sizeof(*data) + cbMaskBits + cbColourBits; + if (cds.cbData > sizeof(*data)) + { + char *buffer; + BYTE* image_data_buffer; buffer = malloc(cds.cbData); if (!buffer) { - DeleteObject(iconinfo.hbmMask); - if (iconinfo.hbmColor) DeleteObject(iconinfo.hbmColor); + if (iconinfo.hbmMask) + DeleteObject(iconinfo.hbmMask); + + if (iconinfo.hbmColor) + DeleteObject(iconinfo.hbmColor); + + if (ballooniconinfo.hbmMask) + DeleteObject(ballooniconinfo.hbmMask); + + if (ballooniconinfo.hbmColor) + DeleteObject(ballooniconinfo.hbmColor); + SetLastError(E_OUTOFMEMORY); return FALSE; }
data = (struct notify_data *)buffer; + image_data_buffer = &data->icon_data[0]; memset( data, 0, sizeof(*data) ); - buffer = data->icon_data; - GetBitmapBits(iconinfo.hbmMask, cbMaskBits, buffer); - if (!iconinfo.hbmColor) - { - data->icon_info.width = bmMask.bmWidth; - data->icon_info.height = bmMask.bmHeight / 2; - data->icon_info.planes = 1; - data->icon_info.bpp = 1; - } - else + + if (iconinfo.hbmMask) { - data->icon_info.width = bmColour.bmWidth; - data->icon_info.height = bmColour.bmHeight; - data->icon_info.planes = bmColour.bmPlanes; - data->icon_info.bpp = bmColour.bmBitsPixel; - buffer += cbMaskBits; - GetBitmapBits(iconinfo.hbmColor, cbColourBits, buffer); - DeleteObject(iconinfo.hbmColor); + fill_icon_info(&bmMask, &bmColour, cbMaskBits, cbColourBits, &iconinfo, &data->icon_info, image_data_buffer); + image_data_buffer += cbMaskBits + cbColourBits; } - DeleteObject(iconinfo.hbmMask); + + if (ballooniconinfo.hbmMask) + fill_icon_info(&bmBalloonMask, &bmBalloonColour, balloonCbMaskBits, balloonCbColourBits, &ballooniconinfo, &data->balloon_icon_info, image_data_buffer); }
-noicon: data->hWnd = HandleToLong( nid->hWnd ); data->uID = nid->uID; data->uFlags = nid->uFlags; diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index 6337faad633..6ee59b527fc 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -63,6 +63,7 @@ struct notify_data /* platform-independent format for NOTIFYICONDATA */ WCHAR szInfoTitle[64]; DWORD dwInfoFlags; GUID guidItem; + struct notify_data_icon balloon_icon_info; /* balloon icon bitmap info */ BYTE icon_data[]; };
@@ -729,7 +730,17 @@ static BOOL modify_icon( struct icon *icon, NOTIFYICONDATAW *nid ) lstrcpynW( icon->info_title, nid->szInfoTitle, ARRAY_SIZE( icon->info_title )); icon->info_flags = nid->dwInfoFlags; icon->info_timeout = max(min(nid->uTimeout, BALLOON_SHOW_MAX_TIMEOUT), BALLOON_SHOW_MIN_TIMEOUT); - icon->info_icon = nid->hBalloonIcon; + + if (icon->info_icon) DestroyIcon(icon->info_icon); + if ((icon->info_flags & NIIF_ICONMASK) == NIIF_USER) + { + icon->info_icon = CopyIcon(nid->hBalloonIcon); + } + else + { + icon->info_icon = NULL; + } + update_balloon( icon ); } if (icon->state & NIS_HIDDEN) hide_icon( icon ); @@ -883,8 +894,26 @@ static BOOL handle_incoming(HWND hwndSource, COPYDATASTRUCT *cds) } nid.hIcon = CreateIcon(NULL, data->icon_info.width, data->icon_info.height, data->icon_info.planes, data->icon_info.bpp, icon_data, icon_data + cbMaskBits); + icon_data += cbMaskBits + cbColourBits; }
+ if ((nid.uFlags & NIF_INFO) && (nid.dwInfoFlags & NIIF_USER)) + { + /* Balloon icon */ + LONG cbMaskBits; + LONG cbColourBits; + + cbMaskBits = (data->balloon_icon_info.width * data->balloon_icon_info.height + 15) / 16 * 2; + cbColourBits = (data->balloon_icon_info.planes * data->balloon_icon_info.width * data->balloon_icon_info.height * data->balloon_icon_info.bpp + 15) / 16 * 2; + + if (cds->cbData < ((char*)icon_data - (char*)data) + cbMaskBits + cbColourBits) + { + ERR("buffer underflow\n"); + return FALSE; + } + nid.hBalloonIcon = CreateIcon(NULL, data->balloon_icon_info.width, data->balloon_icon_info.height, data->balloon_icon_info.planes, data->balloon_icon_info.bpp, + icon_data, icon_data + cbMaskBits); + } /* try forwarding to the display driver first */ if (cds->dwData == NIM_ADD || !(icon = get_icon( nid.hWnd, nid.uID ))) { @@ -919,6 +948,7 @@ static BOOL handle_incoming(HWND hwndSource, COPYDATASTRUCT *cds)
done: if (nid.hIcon) DestroyIcon( nid.hIcon ); + if (nid.hBalloonIcon) DestroyIcon( nid.hBalloonIcon ); sync_taskbar_buttons(); return ret; }