mirror of
https://github.com/gnustep/libs-back.git
synced 2025-05-30 00:40:55 +00:00
Add MSUserNotifications to Source/win32 directory to support notifications on windows
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/branches/gnustep_testplant_branch@39739 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
e67d8442e9
commit
58ca6b5c8e
35 changed files with 2974 additions and 0 deletions
|
@ -43,6 +43,7 @@ GNUSTEP_LOCAL_ADDITIONAL_MAKEFILES=back.make
|
|||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
|
||||
include ./Version
|
||||
-include ../back/Version
|
||||
|
||||
#
|
||||
# The list of subproject directories
|
||||
|
|
|
@ -33,6 +33,9 @@ include ../../config.make
|
|||
# The library to be compiled, as a library or as a bundle
|
||||
SUBPROJECT_NAME=win32
|
||||
|
||||
#win32_SUBPROJECTS = MSUserNotifications
|
||||
SUBPROJECTS = MSUserNotifications
|
||||
|
||||
win32_LOCALIZED_RESOURCE_FILES = \
|
||||
|
||||
# The C source files to be compiled
|
||||
|
@ -53,5 +56,6 @@ w32_GLcontext.m \
|
|||
-include GNUmakefile.preamble
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/subproject.make
|
||||
include $(GNUSTEP_MAKEFILES)/aggregate.make
|
||||
|
||||
-include GNUmakefile.postamble
|
||||
|
|
59
Source/win32/MSUserNotifications/GNUmakefile
Normal file
59
Source/win32/MSUserNotifications/GNUmakefile
Normal file
|
@ -0,0 +1,59 @@
|
|||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(warning )
|
||||
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
|
||||
$(warning Perhaps gnustep-make is not properly installed,)
|
||||
$(warning so gnustep-config is not in your PATH.)
|
||||
$(warning )
|
||||
$(warning Your PATH is currently $(PATH))
|
||||
$(warning )
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
|
||||
endif
|
||||
|
||||
BUNDLE_NAME = NSUserNotification
|
||||
|
||||
NSUserNotification_NEEDS_GUI = NO
|
||||
NSUserNotification_CFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
|
||||
NSUserNotification_OBJCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
|
||||
NSUserNotification_OBJCCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
|
||||
NSUserNotification_PRINCIPAL_CLASS = MSUserNotificationCenter
|
||||
NSUserNotification_OBJC_FILES =
|
||||
NSUserNotification_OBJCC_FILES = MSUserNotification.mm
|
||||
NSUserNotification_BUNDLE_LIBS = -lgnustep-gui -lgdi32
|
||||
#NSUserNotification_LIBS = stdc++
|
||||
#NSUserNotification_INCLUDE_DIRS += \
|
||||
# -I/mingw/lib/gcc/mingw32/4.8.1/include/c++/mingw32 \
|
||||
# -I/mingw/lib/gcc/mingw32/4.8.1/include/c++
|
||||
NSUserNotification_RESOURCE_FILES = \
|
||||
ToastNotifications/obj/ToastNotifications-0.dll \
|
||||
TaskbarNotifications/obj/TaskbarNotifications-0.dll
|
||||
#NSUserNotification_SUBPROJECTS = ToastNotifications TaskbarNotifications
|
||||
SUBPROJECTS = ToastNotifications TaskbarNotifications
|
||||
|
||||
# Set this to the ROOT DIRECTORY of your GNUstep build that contains the 'core' directory...
|
||||
BASE_VERSION_DEFINED =
|
||||
ifneq ($(MAJOR_VERSION),)
|
||||
ifneq ($(MINOR_VERSION),)
|
||||
BASE_VERSION_DEFINED=1
|
||||
endif
|
||||
endif
|
||||
ifdef ($(BASE_VERSION_DEFINED),)
|
||||
ifeq ($(GNUSTEP_BUILD_ROOT),)
|
||||
$(error You need to checkout core/base and/or set GNUSTEP_BUILD_ROOT to your gnustep root containing 'core/base' before compiling!)
|
||||
endif
|
||||
include ${GNUSTEP_BUILD_ROOT}/core/base/Version
|
||||
BASE_VERSION_DEFINED=1
|
||||
endif
|
||||
libgnustep-base_INTERFACE_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION)
|
||||
NSUserNotification_INSTALL_DIR = $(GNUSTEP_LIBRARY)/Libraries/gnustep-base/Versions/$(libgnustep-base_INTERFACE_VERSION)/Resources/
|
||||
|
||||
#include GNUmakefile.postamble
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
include $(GNUSTEP_MAKEFILES)/aggregate.make
|
||||
include $(GNUSTEP_MAKEFILES)/bundle.make
|
12
Source/win32/MSUserNotifications/GNUmakefile.postamble
Normal file
12
Source/win32/MSUserNotifications/GNUmakefile.postamble
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
#
|
||||
# Make list of class names for DLL exports. Uses the actual classes from
|
||||
# the .o files, so it should really have everything needed.
|
||||
#
|
||||
libNSUserNotifications.def: $(OBJ_FILES_TO_LINK)
|
||||
rm -f $@
|
||||
rm -f _tmp.def
|
||||
cat win32-def.top > $@
|
||||
nm $^ | grep '^........ [TR] _' | sed 's/[^_]*_//' > _tmp.def
|
||||
cat _tmp.def | grep "_class_name_" >> $@
|
||||
rm -rf _tmp.def
|
0
Source/win32/MSUserNotifications/Images/.gitignore
vendored
Normal file
0
Source/win32/MSUserNotifications/Images/.gitignore
vendored
Normal file
75
Source/win32/MSUserNotifications/MSUserNotification.h
Normal file
75
Source/win32/MSUserNotifications/MSUserNotification.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/* Interface for DKUserNotification for GNUstep
|
||||
Copyright (C) 2014 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
Date: 2014
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02111 USA.
|
||||
*/
|
||||
|
||||
// NOTE: for the time being, NSUserNotificationCenter needs this feature.
|
||||
// Whenever this restriction is lifted, we can get rid of it here as well.
|
||||
#if __has_feature(objc_default_synthesize_properties)
|
||||
|
||||
#include <windows.h>
|
||||
#include <GNUstepBase/GSConfig.h>
|
||||
|
||||
// C++ header includes...
|
||||
#include <string>
|
||||
|
||||
#include "MSUserNotificationAPI.h"
|
||||
|
||||
#import <GNUstepBase/GSVersionMacros.h>
|
||||
|
||||
#if OS_API_VERSION(MAC_OS_X_VERSION_10_8,GS_API_LATEST)
|
||||
#include <Foundation/NSUUID.h>
|
||||
|
||||
#import <Foundation/NSUserNotification.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// DLL API Function prototypes...
|
||||
typedef BOOL __cdecl (*SendNotificationFunctionPtr)(HWND, HICON, SEND_NOTE_INFO_PTR);
|
||||
typedef BOOL __cdecl (*RemoveNotificationFunctionPtr)(HICON, REMOVE_NOTE_INFO_PTR);
|
||||
typedef BOOL __cdecl (*ActivationCallbackFunctionPtr)(void);
|
||||
typedef BOOL __cdecl (*SetActivationCallbackPtr)(ActivationCallbackFunctionPtr);
|
||||
|
||||
@class NSConnection;
|
||||
@class NSArray;
|
||||
@class NSMutableDictionary;
|
||||
|
||||
@protocol Notifications;
|
||||
|
||||
@interface MSUserNotificationCenter : NSUserNotificationCenter
|
||||
{
|
||||
HICON appIcon;
|
||||
NSString *appIconPath;
|
||||
NSMutableDictionary *imageToIcon;
|
||||
NSArray *caps;
|
||||
NSUInteger uniqueID;
|
||||
NSMutableDictionary *idToNotes;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OS_API_VERSION(MAC_OS_X_VERSION_10_8,GS_API_LATEST) */
|
||||
#endif /* __has_feature(objc_default_synthesize_properties) */
|
633
Source/win32/MSUserNotifications/MSUserNotification.mm
Normal file
633
Source/win32/MSUserNotifications/MSUserNotification.mm
Normal file
|
@ -0,0 +1,633 @@
|
|||
/* Implementation for NSUserNotification for GNUstep/Windows
|
||||
Copyright (C) 2014 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
Date: 2014
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02111 USA.
|
||||
*/
|
||||
|
||||
// NOTE: for the time being, NSUserNotificationCenter needs this feature.
|
||||
// Whenever this restriction is lifted, we can get rid of it here as well.
|
||||
#if __has_feature(objc_default_synthesize_properties)
|
||||
|
||||
#define EXPOSE_NSUserNotification_IVARS 1
|
||||
#define EXPOSE_NSUserNotificationCenter_IVARS 1
|
||||
|
||||
#import "MSUserNotification.h"
|
||||
#import <GNUstepBase/GNUstep.h>
|
||||
#import "GNUstepBase/NSObject+GNUstepBase.h"
|
||||
#import "GNUstepBase/NSDebug+GNUstepBase.h"
|
||||
#import <AppKit/NSGraphics.h>
|
||||
#import <AppKit/NSImage.h>
|
||||
#import <AppKit/NSWindow.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <Foundation/NSBundle.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import "Foundation/NSException.h"
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
#import <Foundation/NSScanner.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <Foundation/NSUUID.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <ShellAPI.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static SendNotificationFunctionPtr pSendNotification = NULL;
|
||||
static RemoveNotificationFunctionPtr pRemoveNotification = NULL;
|
||||
static HMODULE hNotificationLib = NULL;
|
||||
|
||||
static NSString * const kButtonActionKey = @"show";
|
||||
|
||||
@interface NSImage (Private)
|
||||
- (NSString*)_filename;
|
||||
@end
|
||||
|
||||
@implementation NSImage (Private)
|
||||
- (NSString*)_filename
|
||||
{
|
||||
return _fileName;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface NSUserNotification ()
|
||||
@property (readwrite, retain) NSDate *actualDeliveryDate;
|
||||
@property (readwrite, getter=isPresented) BOOL presented;
|
||||
@property (readwrite) NSUserNotificationActivationType activationType;
|
||||
@end
|
||||
|
||||
@interface NSUserNotificationCenter (Private)
|
||||
- (NSUserNotification *) deliveredNotificationWithUniqueId: (id)uniqueId;
|
||||
@end
|
||||
|
||||
@interface MSUserNotificationCenter (Private)
|
||||
- (NSString *) cleanupTextIfNecessary: (NSString *)rawText;
|
||||
@end
|
||||
|
||||
@implementation MSUserNotificationCenter
|
||||
|
||||
- (id) init
|
||||
{
|
||||
NSDebugLLog(@"NSUserNotification", @"initializing...");
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
// Initialize instance variables...
|
||||
imageToIcon = [NSMutableDictionary new];
|
||||
uniqueID = 1;
|
||||
|
||||
NSBundle *classBundle = [NSBundle bundleForClass: [self class]];
|
||||
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||
NSDictionary *infoDict = [mainBundle infoDictionary];
|
||||
NSString *imageName = [[infoDict objectForKey:@"CFBundleIconFiles"] objectAtIndex:0];
|
||||
NSString *imageType = @"";
|
||||
NSString *path = nil;
|
||||
NSImage *image = [self _imageForBundleInfo:infoDict];
|
||||
|
||||
appIcon = [self _iconFromImage:image];
|
||||
appIconPath = [image _filename];
|
||||
NSLog(@"%s:bundle: %@ image: %@ icon: %p path: %@", __PRETTY_FUNCTION__, classBundle, image, appIcon, appIconPath);
|
||||
|
||||
if (appIcon == NULL)
|
||||
{
|
||||
NSLog(@"%s:unable to load icon for bundle: %@ GetLastError: %ld", __PRETTY_FUNCTION__, mainBundle, GetLastError());
|
||||
}
|
||||
|
||||
OSVERSIONINFO osvi = { 0 };
|
||||
WINBOOL bIsWindows81orLater = false;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
|
||||
if (GetVersionEx(&osvi)== 0)
|
||||
{
|
||||
NSLog(@"%s:failed getting OS version with error id: %ld", __PRETTY_FUNCTION__, GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:version number is: %d", __PRETTY_FUNCTION__, osvi.dwMajorVersion);
|
||||
#endif
|
||||
if (osvi.dwMajorVersion >= 6)
|
||||
{
|
||||
if (osvi.dwMinorVersion == 1)
|
||||
{
|
||||
bIsWindows81orLater = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bIsWindows81orLater = 1;
|
||||
}
|
||||
|
||||
if(bIsWindows81orLater)
|
||||
path = [classBundle pathForResource: @"ToastNotifications-0" ofType: @"dll"];
|
||||
else
|
||||
path = [classBundle pathForResource: @"TaskbarNotifications-0" ofType: @"dll"];
|
||||
|
||||
// Try loading the corresponding DLL required for notifications...
|
||||
hNotificationLib = LoadLibrary([path UTF8String]);
|
||||
|
||||
// For for not good conditions...
|
||||
if (hNotificationLib == NULL)
|
||||
{
|
||||
NSLog(@"%s:DLL load error: %ld", __PRETTY_FUNCTION__, GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
pSendNotification = (SendNotificationFunctionPtr)GetProcAddress(hNotificationLib, "sendNotification");
|
||||
pRemoveNotification = (RemoveNotificationFunctionPtr)GetProcAddress(hNotificationLib, "removeNotification");
|
||||
#if 1 //defined(DEBUG)
|
||||
NSLog(@"%s:DLL ptr: %p send notification ptr: %p remove ptr: %p", __PRETTY_FUNCTION__,
|
||||
hNotificationLib, pSendNotification, pRemoveNotification);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
// Cleanup any icons we generated...
|
||||
NSEnumerator *iter = [imageToIcon objectEnumerator];
|
||||
HICON icon = NULL;
|
||||
while ((icon = (HICON)[[iter nextObject] pointerValue]) != NULL)
|
||||
{
|
||||
DestroyIcon(icon);
|
||||
}
|
||||
|
||||
[imageToIcon release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSImage*) _imageForBundleInfo:(NSDictionary*)infoDict
|
||||
{
|
||||
NSImage *image = nil;
|
||||
NSString *appIconFile = [infoDict objectForKey: @"NSIcon"];
|
||||
|
||||
if (appIconFile && ![appIconFile isEqual: @""])
|
||||
{
|
||||
image = [NSImage imageNamed: appIconFile];
|
||||
}
|
||||
|
||||
// Try to look up the icns file.
|
||||
appIconFile = [infoDict objectForKey: @"CFBundleIconFile"];
|
||||
if (appIconFile && ![appIconFile isEqual: @""])
|
||||
{
|
||||
image = [NSImage imageNamed: appIconFile];
|
||||
}
|
||||
|
||||
if (image == nil)
|
||||
{
|
||||
image = [NSImage imageNamed: @"GNUstep"];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the new image to be named 'NSApplicationIcon' ... to do that we
|
||||
* must first check that any existing image of the same name has its
|
||||
* name removed.
|
||||
*/
|
||||
[(NSImage*)[NSImage imageNamed: @"NSApplicationIcon"] setName: nil];
|
||||
// We need to copy the image as we may have a proxy here
|
||||
image = AUTORELEASE([image copy]);
|
||||
[image setName: @"NSApplicationIcon"];
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
- (HICON) _iconFromRep: (NSBitmapImageRep*)rep
|
||||
{
|
||||
HICON result = NULL;
|
||||
|
||||
if (rep)
|
||||
{
|
||||
int w = [rep pixelsWide];
|
||||
int h = [rep pixelsHigh];
|
||||
|
||||
// Create a windows bitmap from the image representation's bitmap...
|
||||
if ((w > 0) && (h > 0))
|
||||
{
|
||||
BITMAP bm;
|
||||
HDC hDC = GetDC(NULL);
|
||||
HDC hMainDC = CreateCompatibleDC(hDC);
|
||||
HDC hAndMaskDC = CreateCompatibleDC(hDC);
|
||||
HDC hXorMaskDC = CreateCompatibleDC(hDC);
|
||||
HBITMAP hAndMaskBitmap = NULL;
|
||||
HBITMAP hXorMaskBitmap = NULL;
|
||||
|
||||
// Create the source bitmap...
|
||||
HBITMAP hSourceBitmap = CreateBitmap(w, h, [rep numberOfPlanes], [rep bitsPerPixel], [rep bitmapData]);
|
||||
|
||||
// Get the dimensions of the source bitmap
|
||||
GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
|
||||
|
||||
// Create compatible bitmaps for the device context...
|
||||
hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
|
||||
hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
|
||||
|
||||
// Select the bitmaps to DC
|
||||
HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
|
||||
HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
|
||||
HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
|
||||
|
||||
/* On windows, to calculate the color for a pixel, first an AND is done
|
||||
* with the background and the "and" bitmap, then an XOR with the "xor"
|
||||
* bitmap. This means that when the data in the "and" bitmap is 0, the
|
||||
* pixel will get the color as specified in the "xor" bitmap.
|
||||
* However, if the data in the "and" bitmap is 1, the result will be the
|
||||
* background XOR'ed with the value in the "xor" bitmap. In case the "xor"
|
||||
* data is completely black (0x000000) the pixel will become transparent,
|
||||
* in case it's white (0xffffff) the pixel will become the inverse of the
|
||||
* background color.
|
||||
*/
|
||||
|
||||
// Scan each pixel of the souce bitmap and create the masks
|
||||
int y;
|
||||
int *pixel = (int*)[rep bitmapData];
|
||||
for(y = 0; y < bm.bmHeight; ++y)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < bm.bmWidth; ++x)
|
||||
{
|
||||
if (*pixel++ == 0x00000000)
|
||||
{
|
||||
SetPixel(hAndMaskDC, x, y, RGB(255, 255, 255));
|
||||
SetPixel(hXorMaskDC, x, y, RGB(0, 0, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPixel(hAndMaskDC, x, y, RGB(0, 0, 0));
|
||||
SetPixel(hXorMaskDC, x, y, GetPixel(hMainDC, x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reselect the old bitmap objects...
|
||||
SelectObject(hMainDC, hOldMainBitmap);
|
||||
SelectObject(hAndMaskDC, hOldAndMaskBitmap);
|
||||
SelectObject(hXorMaskDC, hOldXorMaskBitmap);
|
||||
|
||||
// Create the cursor from the generated and/xor data...
|
||||
ICONINFO iconinfo = { 0 };
|
||||
iconinfo.fIcon = FALSE;
|
||||
iconinfo.xHotspot = 0;
|
||||
iconinfo.yHotspot = 0;
|
||||
iconinfo.hbmMask = hAndMaskBitmap;
|
||||
iconinfo.hbmColor = hXorMaskBitmap;
|
||||
|
||||
// Finally, try to create the cursor...
|
||||
result = CreateIconIndirect(&iconinfo);
|
||||
|
||||
// Cleanup the DC's...
|
||||
DeleteDC(hXorMaskDC);
|
||||
DeleteDC(hAndMaskDC);
|
||||
DeleteDC(hMainDC);
|
||||
|
||||
// Cleanup the bitmaps...
|
||||
DeleteObject(hXorMaskBitmap);
|
||||
DeleteObject(hAndMaskBitmap);
|
||||
DeleteObject(hSourceBitmap);
|
||||
|
||||
// Release the screen HDC...
|
||||
ReleaseDC(NULL,hDC);
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
- (NSBitmapImageRep*) _getStandardBitmap:(NSImage *)image
|
||||
{
|
||||
NSBitmapImageRep *rep;
|
||||
|
||||
if (image == nil)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
rep = (NSBitmapImageRep *)[image bestRepresentationForDevice: nil];
|
||||
if (!rep || ![rep respondsToSelector: @selector(samplesPerPixel)])
|
||||
{
|
||||
/* FIXME: We might create a blank cursor here? */
|
||||
//NSLog(@"%s:could not convert cursor bitmap data for image: %@", __PRETTY_FUNCTION__, image);
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert into something usable by the backend
|
||||
return [rep _convertToFormatBitsPerSample: 8
|
||||
samplesPerPixel: [rep hasAlpha] ? 4 : 3
|
||||
hasAlpha: [rep hasAlpha]
|
||||
isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bitmapFormat: 0
|
||||
bytesPerRow: 0
|
||||
bitsPerPixel: 0];
|
||||
}
|
||||
}
|
||||
|
||||
- (HICON) _iconFromImage: (NSImage *)image
|
||||
{
|
||||
// Default the return cursur ID to NULL...
|
||||
HICON result = NULL;
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:image: %@ imageToIcon dict: %@", __PRETTY_FUNCTION__, image, imageToIcon);
|
||||
#endif
|
||||
if ([image name] == nil)
|
||||
{
|
||||
NSLog(@"%s:cannot create/store image icon for NIL image names: %@", __PRETTY_FUNCTION__, result, [image name]);
|
||||
}
|
||||
else if ([imageToIcon objectForKey:[image name]])
|
||||
{
|
||||
result = (HICON)[[imageToIcon objectForKey: image] pointerValue];
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:reusing icon: %p for imageName: %@", __PRETTY_FUNCTION__, result, [image name]);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
NSBitmapImageRep *rep = [self _getStandardBitmap:image];
|
||||
|
||||
if (rep == NULL)
|
||||
{
|
||||
NSLog(@"%s:error creating standard bitmap for image: %@", __PRETTY_FUNCTION__, image);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to create the icon from the image...
|
||||
result = [self _iconFromRep:rep];
|
||||
|
||||
// Need to save these created cursors to remove later...
|
||||
if (result != NULL)
|
||||
{
|
||||
[imageToIcon setObject:[NSValue valueWithPointer:result] forKey:[image name]];
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:saving icon: %p for imageName: %@", __PRETTY_FUNCTION__, result, [image name]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return whatever we were able to generate...
|
||||
return(result);
|
||||
}
|
||||
|
||||
- (GUID)guidFromUUIDString:(NSString*)uuidString
|
||||
{
|
||||
NSLog(@"%s:UUIDString: %@", __PRETTY_FUNCTION__, uuidString);
|
||||
GUID theGUID = { 0 };
|
||||
NSUInteger value = 0;
|
||||
NSArray *components = [uuidString componentsSeparatedByString: @"-"];
|
||||
NSScanner *scanner1 = [NSScanner scannerWithString: [components objectAtIndex: 0]];
|
||||
NSScanner *scanner2 = [NSScanner scannerWithString: [components objectAtIndex: 1]];
|
||||
NSScanner *scanner3 = [NSScanner scannerWithString: [components objectAtIndex: 2]];
|
||||
NSString *data4 = [[components objectAtIndex: 3] stringByAppendingString: [components objectAtIndex: 4]];
|
||||
NSScanner *scanner4 = [NSScanner scannerWithString: data4];
|
||||
|
||||
[scanner1 scanHexInt: (NSUInteger*)&theGUID.Data1];
|
||||
[scanner2 scanHexInt: (NSUInteger*)&value];
|
||||
theGUID.Data2 = (WORD) value;
|
||||
[scanner3 scanHexInt: (NSUInteger*)&value];
|
||||
theGUID.Data3 = (WORD) value;
|
||||
|
||||
return theGUID;
|
||||
}
|
||||
|
||||
- (GUID)guidFromUUID:(NSUUID*)uuid
|
||||
{
|
||||
// Note: This is an example GUID only and should not be used.
|
||||
// Normally, you should use a GUID-generating tool to provide the value to
|
||||
// assign to guidItem.
|
||||
return([self guidFromUUIDString:[uuid UUIDString]]);
|
||||
}
|
||||
|
||||
- (NSUUID*)generateUUID
|
||||
{
|
||||
return [NSUUID UUID];
|
||||
}
|
||||
|
||||
- (GUID)generateGUID
|
||||
{
|
||||
return [self guidFromUUID:[self generateUUID]];
|
||||
}
|
||||
|
||||
- (NSNumber*)_showNotification:(NSUserNotification*)note forWindow:(NSWindow*)forWindow
|
||||
{
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:title: %@ informativeText: %@ contentImage: %@ (%@)", __PRETTY_FUNCTION__, note.title, note.informativeText, note.contentImage, [note.contentImage _filename]);
|
||||
#endif
|
||||
|
||||
NSNumber *result = nil;
|
||||
|
||||
|
||||
if (pSendNotification != NULL)
|
||||
{
|
||||
NSUUID *uuid = [self generateUUID];
|
||||
NSString *UUIDString = [uuid UUIDString];
|
||||
std::string uuidString = std::string([UUIDString UTF8String]);
|
||||
|
||||
// Try to send the notification...
|
||||
SEND_NOTE_INFO_T noteInfo = { 0 };
|
||||
noteInfo.uuidString = [UUIDString UTF8String];
|
||||
noteInfo.title = [note.title UTF8String];
|
||||
noteInfo.informativeText = [note.informativeText UTF8String];
|
||||
noteInfo.appIconPath = [appIconPath UTF8String];
|
||||
|
||||
// Content image is for displaying within the notification...
|
||||
// i.e. Shell notes - within the balloon somwhere...
|
||||
// Toast notes - with the toast window somewhere
|
||||
// Check for content image and generate a windows icon from it...
|
||||
if (note.contentImage != nil)
|
||||
{
|
||||
// Attempt to create a window icon from image...
|
||||
noteInfo.contentIcon = [self _iconFromImage:note.contentImage];
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:image: %@ icon: %p", __PRETTY_FUNCTION__, note.contentImage, noteInfo.contentIcon);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL status = pSendNotification((HWND)GetModuleHandle(NULL), appIcon, ¬eInfo);
|
||||
|
||||
if (status)
|
||||
{
|
||||
note.identifier = [[UUIDString copy] autorelease];
|
||||
note.presented = YES;
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:status: %d uniqueID: %d", __PRETTY_FUNCTION__, status, uniqueID);
|
||||
#endif
|
||||
|
||||
return [NSNumber numberWithBool:uniqueID++];
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup...
|
||||
[pool drain];
|
||||
|
||||
// TODO: Should we return something else here????
|
||||
return [NSNumber numberWithBool:0];
|
||||
}
|
||||
|
||||
- (void) _deliverNotification: (NSUserNotification *)un
|
||||
{
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
NSString *appName = nil;
|
||||
NSString *imageName = nil;
|
||||
NSURL *imageURL = nil;
|
||||
NSURL *soundFileURL = nil;
|
||||
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
if (bundle)
|
||||
{
|
||||
NSDictionary *info = [bundle localizedInfoDictionary];
|
||||
if (info)
|
||||
{
|
||||
appName = [info objectForKey: @"NSBundleName"];
|
||||
imageName = [info objectForKey: @"NSIcon"];
|
||||
if (imageName)
|
||||
imageURL = [bundle URLForResource: imageName withExtension: nil];
|
||||
}
|
||||
if (un.soundName && [caps containsObject:@"sound"])
|
||||
{
|
||||
soundFileURL = [bundle URLForResource: un.soundName
|
||||
withExtension: nil];
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
if (!appName)
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
NSMutableArray *actions = [NSMutableArray array];
|
||||
#if 0
|
||||
if ([un hasActionButton])
|
||||
{
|
||||
NSString *actionButtonTitle = un.actionButtonTitle;
|
||||
if (!actionButtonTitle)
|
||||
actionButtonTitle = _(@"Show");
|
||||
|
||||
// NOTE: don't use "default", as it's used by convention and seems
|
||||
// to remove the actionButton entirely
|
||||
// (tested with Notification Daemon (0.3.7))
|
||||
[actions addObject: kButtonActionKey];
|
||||
[actions addObject: [self cleanupTextIfNecessary: actionButtonTitle]];
|
||||
}
|
||||
#endif
|
||||
|
||||
NSDebugMLLog(@"NSUserNotification",
|
||||
@"appName: %@ imageName: %@ imageURL: %@ soundFileURL: %@",
|
||||
appName, imageName, imageURL, soundFileURL);
|
||||
|
||||
NSMutableDictionary *hints = [NSMutableDictionary dictionary];
|
||||
if (un.userInfo)
|
||||
[hints addEntriesFromDictionary: un.userInfo];
|
||||
|
||||
if (imageURL)
|
||||
[hints setObject: [imageURL absoluteString] forKey: @"image-path"];
|
||||
if (soundFileURL)
|
||||
[hints setObject: [soundFileURL path] forKey: @"sound-file"];
|
||||
|
||||
NSString *summary = [self cleanupTextIfNecessary: un.title];
|
||||
NSString *body = [self cleanupTextIfNecessary: un.informativeText];
|
||||
NSNumber *uniqueId = [self _showNotification:un forWindow: nil];
|
||||
|
||||
ASSIGN(un->_uniqueId, uniqueId);
|
||||
un.presented = ([uniqueId integerValue] != 0) ? YES : NO;
|
||||
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
- (void)_removeDeliveredNotification:(NSUserNotification *)theNote
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:note: %@ ID: %@", __PRETTY_FUNCTION__, theNote, theNote->_uniqueId);
|
||||
#endif
|
||||
|
||||
if (pRemoveNotification != NULL)
|
||||
{
|
||||
REMOVE_NOTE_INFO_T noteInfo = { 0 };
|
||||
noteInfo.uniqueID = [theNote->_uniqueId integerValue];
|
||||
pRemoveNotification(appIcon, ¬eInfo);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)cleanupTextIfNecessary:(NSString *)rawText
|
||||
{
|
||||
if (!rawText || ![caps containsObject:@"body-markup"])
|
||||
return nil;
|
||||
|
||||
NSMutableString *t = (NSMutableString *)[rawText mutableCopy];
|
||||
[t replaceOccurrencesOfString: @"&" withString: @"&" options: 0 range: NSMakeRange(0, [t length])]; // must be first!
|
||||
[t replaceOccurrencesOfString: @"<" withString: @"<" options: 0 range: NSMakeRange(0, [t length])];
|
||||
[t replaceOccurrencesOfString: @">" withString: @">" options: 0 range: NSMakeRange(0, [t length])];
|
||||
[t replaceOccurrencesOfString: @"\"" withString: @""" options: 0 range: NSMakeRange(0, [t length])];
|
||||
[t replaceOccurrencesOfString: @"'" withString: @"'" options: 0 range: NSMakeRange(0, [t length])];
|
||||
return t;
|
||||
}
|
||||
|
||||
// SIGNALS
|
||||
|
||||
- (void)receiveNotificationClosedNotification:(NSNotification *)n
|
||||
{
|
||||
id nId = [[n userInfo] objectForKey: @"arg0"];
|
||||
NSUserNotification *un = [self deliveredNotificationWithUniqueId: nId];
|
||||
NSDebugMLLog(@"NSUserNotification", @"%@", un);
|
||||
}
|
||||
|
||||
- (void)receiveActionInvokedNotification:(NSNotification *)n
|
||||
{
|
||||
id nId = [[n userInfo] objectForKey: @"arg0"];
|
||||
NSUserNotification *un = [self deliveredNotificationWithUniqueId: nId];
|
||||
NSString *action = [[n userInfo] objectForKey: @"arg1"];
|
||||
|
||||
NSDebugMLLog(@"NSUserNotification", @"%@ -- action: %@", un, action);
|
||||
if ([action isEqual:kButtonActionKey])
|
||||
un.activationType = NSUserNotificationActivationTypeActionButtonClicked;
|
||||
else
|
||||
un.activationType = NSUserNotificationActivationTypeContentsClicked;
|
||||
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(userNotificationCenter:didActivateNotification:)])
|
||||
[self.delegate userNotificationCenter: self didActivateNotification: un];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __has_feature(objc_default_synthesize_properties) */
|
25
Source/win32/MSUserNotifications/MSUserNotificationAPI.h
Normal file
25
Source/win32/MSUserNotifications/MSUserNotificationAPI.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" { // Only if you are using C++ rather than C
|
||||
#endif
|
||||
|
||||
typedef struct _SEND_NOTE_INFO
|
||||
{
|
||||
const char *uuidString;
|
||||
const char *title;
|
||||
const char *informativeText;
|
||||
HICON contentIcon;
|
||||
const char *appIconPath;
|
||||
} SEND_NOTE_INFO_T, *SEND_NOTE_INFO_PTR;
|
||||
|
||||
typedef struct _REMOVE_NOTE_INFO
|
||||
{
|
||||
UINT uniqueID;
|
||||
} REMOVE_NOTE_INFO_T, *REMOVE_NOTE_INFO_PTR;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(warning )
|
||||
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
|
||||
$(warning Perhaps gnustep-make is not properly installed,)
|
||||
$(warning so gnustep-config is not in your PATH.)
|
||||
$(warning )
|
||||
$(warning Your PATH is currently $(PATH))
|
||||
$(warning )
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
|
||||
endif
|
||||
|
||||
LIBRARY_NAME = TaskbarNotifications
|
||||
|
||||
TaskbarNotifications_NEEDS_GUI = NO
|
||||
TaskbarNotifications_CFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
|
||||
TaskbarNotifications_OBJCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
|
||||
TaskbarNotifications_OBJCCFLAGS += -DWINVER=0x0600 -D_WIN32_IE=0x0600 -DBUILD_DLL -fms-extensions
|
||||
TaskbarNotifications_OBJC_FILES =
|
||||
TaskbarNotifications_OBJCC_FILES = TaskbarNotifications.mm
|
||||
TaskbarNotifications_LIBRARIES_DEPEND_UPON += -lgnustep-gui -lgdi32
|
||||
TaskbarNotifications_RESOURCE_FILES +=
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
|
@ -0,0 +1,863 @@
|
|||
// Windows documentation - see: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx
|
||||
// Several members of this structure are only supported for Windows 2000 and later. To enable these members,
|
||||
// include one of the following lines in your header:
|
||||
// // Windows Vista and later:
|
||||
// #define NTDDI_VERSION NTDDI_WIN2K
|
||||
// #define NTDDI_VERSION NTDDI_WINXP
|
||||
// #define NTDDI_VERSION NTDDI_VISTA
|
||||
//
|
||||
// // Windows XP and earlier:
|
||||
// #define _WIN32_IE 0x0500
|
||||
//
|
||||
// We've defined these in the GNUmakefile!!!!
|
||||
|
||||
// MUST BE FIRST!!!
|
||||
#include <../MSUserNotification.h>
|
||||
#include <AppKit/NSGraphics.h>
|
||||
#include <AppKit/NSImage.h>
|
||||
#include <Foundation/NSAutoreleasePool.h>
|
||||
#include <Foundation/NSString.h>
|
||||
#include <Foundation/NSScanner.h>
|
||||
#include <Foundation/NSUserNotification.h>
|
||||
#include <Foundation/NSUUID.h>
|
||||
#include <Foundation/NSValue.h>
|
||||
|
||||
#include <ShellAPI.h>
|
||||
#include <shlwapi.h>
|
||||
//#include <Winuser.h>
|
||||
#include <rpcdce.h>
|
||||
|
||||
#if !defined(NIIF_USER)
|
||||
#define NIIF_USER 0x00000004
|
||||
#endif
|
||||
|
||||
#if !defined(NIN_SELECT)
|
||||
#define NIN_SELECT (WM_USER + 0)
|
||||
#endif
|
||||
#if !defined(NIN_BALLOONSHOW)
|
||||
#define NIN_BALLOONSHOW (WM_USER + 2)
|
||||
#endif
|
||||
#if !defined(NIN_BALLOONHIDE)
|
||||
#define NIN_BALLOONHIDE (WM_USER + 3)
|
||||
#endif
|
||||
#if !defined(NIN_BALLOONTIMEOUT)
|
||||
#define NIN_BALLOONTIMEOUT (WM_USER + 4)
|
||||
#endif
|
||||
#if !defined(NIN_BALLOONUSERCLICK)
|
||||
#define NIN_BALLOONUSERCLICK (WM_USER + 5)
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_DLL
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#define WIN_EXTRABYTES 0
|
||||
#define WINDOW_CLASS_NAME TEXT("NSUserNotificationTaskbar")
|
||||
#define WINDOW_TITLE_NAME TEXT("TaskBarNotifierWin")
|
||||
#define NOTIFY_MESSAGE_NAME TEXT("NSUserNotificationWindowsMessage")
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void _registerWindowsClass();
|
||||
void _unregisterWindowsClass();
|
||||
void _initWin32Context();
|
||||
void _destroyWin32Context();
|
||||
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
HICON _iconForBundle();
|
||||
void _setupNotifyDataIcon(NOTIFYICONDATA&, HICON);
|
||||
void _setupNotifyDataUUID(NOTIFYICONDATA &, const char *);
|
||||
void _setupNotifyData(NOTIFYICONDATA&);
|
||||
UINT _addApplicationIcon(DWORD, const char *, HICON);
|
||||
void _removeApplicationIcon(DWORD, HICON);
|
||||
void _removeProcessInfo();
|
||||
void _removeApplicationIconForID(UINT appIconID);
|
||||
|
||||
static HANDLE gHandleDLL = NULL;
|
||||
static HWND gHandleWin = NULL;
|
||||
static UINT gNotifyMsg = 0;
|
||||
static UINT gNotifyCnt = 0;
|
||||
|
||||
// Objective-C/GNUstep references...
|
||||
static NSString *gUuidString = nil;
|
||||
static NSMutableDictionary *gProcessInfo = nil;
|
||||
|
||||
// Need to capture the DLL instance handle....
|
||||
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
|
||||
{
|
||||
DWORD processID = GetCurrentProcessId();
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:hinstDLL: %p dwReason: %d lpvReserved: %p procID: %p", __PRETTY_FUNCTION__, hinstDLL, dwReason, lpvReserved, processID);
|
||||
#endif
|
||||
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH: // Do process attach specific stuff here...
|
||||
// TODO: DO we need to worry about OTHER PROCESSES ATTACHING???
|
||||
// Save the DLL instance handle...
|
||||
// FIXME: Need process specific code here to setup for capturing the generated icons
|
||||
gHandleDLL = hinstDLL;
|
||||
|
||||
#if 0 // DOES NOT WORK - error code 126 - "The specified module could not be found"
|
||||
// even though the instance handle is the same as the modile handle...
|
||||
// Disable thread attache/detach invocations...
|
||||
if (DisableThreadLibraryCalls((HMODULE)hinstDLL) == 0)
|
||||
{
|
||||
NSLog(@"%s:PROCESS_ATTACH:disable thread library calls error: %d", __PRETTY_FUNCTION__, GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
// General intialization...
|
||||
_initWin32Context();
|
||||
|
||||
// If no window...
|
||||
if (gHandleWin == NULL)
|
||||
return FALSE; // This is bad...
|
||||
|
||||
// Log our info...
|
||||
#if 0 //defined(DEBUG)
|
||||
NSLog(@"%s:PROCESS_ATTACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH: // Do process attach specific stuff here...
|
||||
// FIXME: Need process specific code here to remove any generated icons
|
||||
#if 0 //defined(DEBUG)
|
||||
NSLog(@"%s:PROCESS_DETACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
|
||||
#endif
|
||||
|
||||
// Cleanup window stuff...
|
||||
_destroyWin32Context();
|
||||
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH: // Do thread attach specific stuff here...
|
||||
// FIXME: Do we need thread specific code here...
|
||||
#if 0 //defined(DEBUG)
|
||||
NSLog(@"%s:THREAD_ATTACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case DLL_THREAD_DETACH: // Do thread detach specific stuff here...
|
||||
// FIXME: Do we need thread specific code here...
|
||||
#if 0 //defined(DEBUG)
|
||||
NSLog(@"%s:THREAD_DETACH:gHandleDLL: %p gHandleWin: %p", __PRETTY_FUNCTION__, gHandleDLL, gHandleWin);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void _initWin32Context()
|
||||
{
|
||||
NSString *gHandleString = nil;
|
||||
NSNumber *value = [NSNumber numberWithInteger:GetCurrentProcessId()];
|
||||
|
||||
// Register our message window type......
|
||||
_registerWindowsClass();
|
||||
|
||||
// Register the windows notify message we want...
|
||||
gNotifyMsg = RegisterWindowMessage(NOTIFY_MESSAGE_NAME);
|
||||
if (gNotifyMsg == 0)
|
||||
{
|
||||
NSLog(@"%s:error registering windos message - error: %d", __PRETTY_FUNCTION__, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a message only window...if it hasn't been created yet...
|
||||
gHandleWin = CreateWindowEx( 0, WINDOW_CLASS_NAME, WINDOW_TITLE_NAME, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );
|
||||
if (gHandleWin == NULL)
|
||||
{
|
||||
NSLog(@"%s:PROCESS_ATTACH:create window error: %d", __PRETTY_FUNCTION__, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize necessary data structures...
|
||||
gProcessInfo = [NSMutableDictionary new];
|
||||
[gProcessInfo setObject:[NSMutableDictionary dictionary] forKey:@"AppIcons"];
|
||||
[gProcessInfo setObject:[NSMutableDictionary dictionary] forKey:@"AppNotes"];
|
||||
[gProcessInfo setObject:value forKey:@"ProcessID"];
|
||||
}
|
||||
|
||||
void _destroyWin32Context()
|
||||
{
|
||||
// Remove the process' info dictionary...
|
||||
_removeProcessInfo();
|
||||
|
||||
// Destroy the message window...
|
||||
if (gHandleWin != NULL)
|
||||
DestroyWindow(gHandleWin);
|
||||
|
||||
// Unregister the window class...
|
||||
_unregisterWindowsClass();
|
||||
}
|
||||
|
||||
NSImage *_imageForBundleInfo(NSDictionary*infoDict)
|
||||
{
|
||||
NSImage *image = nil;
|
||||
NSString *appIconFile = [infoDict objectForKey: @"NSIcon"];
|
||||
|
||||
if (appIconFile && ![appIconFile isEqual: @""])
|
||||
{
|
||||
image = [NSImage imageNamed: appIconFile];
|
||||
}
|
||||
|
||||
// Try to look up the icns file.
|
||||
appIconFile = [infoDict objectForKey: @"CFBundleIconFile"];
|
||||
if (appIconFile && ![appIconFile isEqual: @""])
|
||||
{
|
||||
image = [NSImage imageNamed: appIconFile];
|
||||
}
|
||||
|
||||
if (image == nil)
|
||||
{
|
||||
image = [NSImage imageNamed: @"GNUstep"];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the new image to be named 'NSApplicationIcon' ... to do that we
|
||||
* must first check that any existing image of the same name has its
|
||||
* name removed.
|
||||
*/
|
||||
[(NSImage*)[NSImage imageNamed: @"NSApplicationIcon"] setName: nil];
|
||||
// We need to copy the image as we may have a proxy here
|
||||
image = AUTORELEASE([image copy]);
|
||||
[image setName: @"NSApplicationIcon"];
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
HICON _iconFromRep(NSBitmapImageRep* rep)
|
||||
{
|
||||
HICON result = NULL;
|
||||
|
||||
if (rep)
|
||||
{
|
||||
int w = [rep pixelsWide];
|
||||
int h = [rep pixelsHigh];
|
||||
|
||||
// Create a windows bitmap from the image representation's bitmap...
|
||||
if ((w > 0) && (h > 0))
|
||||
{
|
||||
BITMAP bm;
|
||||
HDC hDC = GetDC(NULL);
|
||||
HDC hMainDC = CreateCompatibleDC(hDC);
|
||||
HDC hAndMaskDC = CreateCompatibleDC(hDC);
|
||||
HDC hXorMaskDC = CreateCompatibleDC(hDC);
|
||||
HBITMAP hAndMaskBitmap = NULL;
|
||||
HBITMAP hXorMaskBitmap = NULL;
|
||||
|
||||
// Create the source bitmap...
|
||||
HBITMAP hSourceBitmap = CreateBitmap(w, h, [rep numberOfPlanes], [rep bitsPerPixel], [rep bitmapData]);
|
||||
|
||||
// Get the dimensions of the source bitmap
|
||||
GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
|
||||
|
||||
// Create compatible bitmaps for the device context...
|
||||
hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
|
||||
hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
|
||||
|
||||
// Select the bitmaps to DC
|
||||
HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
|
||||
HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
|
||||
HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
|
||||
|
||||
/* On windows, to calculate the color for a pixel, first an AND is done
|
||||
* with the background and the "and" bitmap, then an XOR with the "xor"
|
||||
* bitmap. This means that when the data in the "and" bitmap is 0, the
|
||||
* pixel will get the color as specified in the "xor" bitmap.
|
||||
* However, if the data in the "and" bitmap is 1, the result will be the
|
||||
* background XOR'ed with the value in the "xor" bitmap. In case the "xor"
|
||||
* data is completely black (0x000000) the pixel will become transparent,
|
||||
* in case it's white (0xffffff) the pixel will become the inverse of the
|
||||
* background color.
|
||||
*/
|
||||
|
||||
// Scan each pixel of the souce bitmap and create the masks
|
||||
int y;
|
||||
int *pixel = (int*)[rep bitmapData];
|
||||
for(y = 0; y < bm.bmHeight; ++y)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < bm.bmWidth; ++x)
|
||||
{
|
||||
if (*pixel++ == 0x00000000)
|
||||
{
|
||||
SetPixel(hAndMaskDC, x, y, RGB(255, 255, 255));
|
||||
SetPixel(hXorMaskDC, x, y, RGB(0, 0, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPixel(hAndMaskDC, x, y, RGB(0, 0, 0));
|
||||
SetPixel(hXorMaskDC, x, y, GetPixel(hMainDC, x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reselect the old bitmap objects...
|
||||
SelectObject(hMainDC, hOldMainBitmap);
|
||||
SelectObject(hAndMaskDC, hOldAndMaskBitmap);
|
||||
SelectObject(hXorMaskDC, hOldXorMaskBitmap);
|
||||
|
||||
// Create the cursor from the generated and/xor data...
|
||||
ICONINFO iconinfo = { 0 };
|
||||
iconinfo.fIcon = FALSE;
|
||||
iconinfo.xHotspot = 0;
|
||||
iconinfo.yHotspot = 0;
|
||||
iconinfo.hbmMask = hAndMaskBitmap;
|
||||
iconinfo.hbmColor = hXorMaskBitmap;
|
||||
|
||||
// Finally, try to create the cursor...
|
||||
result = CreateIconIndirect(&iconinfo);
|
||||
|
||||
// Cleanup the DC's...
|
||||
DeleteDC(hXorMaskDC);
|
||||
DeleteDC(hAndMaskDC);
|
||||
DeleteDC(hMainDC);
|
||||
|
||||
// Cleanup the bitmaps...
|
||||
DeleteObject(hXorMaskBitmap);
|
||||
DeleteObject(hAndMaskBitmap);
|
||||
DeleteObject(hSourceBitmap);
|
||||
|
||||
// Release the screen HDC...
|
||||
ReleaseDC(NULL,hDC);
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
NSBitmapImageRep *_getStandardBitmap(NSImage *image)
|
||||
{
|
||||
NSBitmapImageRep *rep;
|
||||
|
||||
if (image == nil)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
rep = (NSBitmapImageRep *)[image bestRepresentationForDevice: nil];
|
||||
if (!rep || ![rep respondsToSelector: @selector(samplesPerPixel)])
|
||||
{
|
||||
/* FIXME: We might create a blank cursor here? */
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:could not convert cursor bitmap data for image: %@", __PRETTY_FUNCTION__, image);
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert into something usable by the backend
|
||||
return [rep _convertToFormatBitsPerSample: 8
|
||||
samplesPerPixel: [rep hasAlpha] ? 4 : 3
|
||||
hasAlpha: [rep hasAlpha]
|
||||
isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bitmapFormat: 0
|
||||
bytesPerRow: 0
|
||||
bitsPerPixel: 0];
|
||||
}
|
||||
}
|
||||
|
||||
HICON _iconFromImage(NSImage *image)
|
||||
{
|
||||
// Default the return cursur ID to NULL...
|
||||
HICON result = NULL;
|
||||
NSBitmapImageRep *rep = _getStandardBitmap(image);
|
||||
|
||||
if (rep == NULL)
|
||||
{
|
||||
NSLog(@"%s:error creating standard bitmap for image: %@", __PRETTY_FUNCTION__, image);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to create the icon from the image...
|
||||
result = _iconFromRep(rep);
|
||||
}
|
||||
|
||||
// Return whatever we were able to generate...
|
||||
return(result);
|
||||
}
|
||||
|
||||
HICON _iconForBundle()
|
||||
{
|
||||
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||
NSDictionary *infoDict = [mainBundle infoDictionary];
|
||||
NSLog(@"%s:infoDict: %@", __PRETTY_FUNCTION__, infoDict);
|
||||
NSString *imageName = [[infoDict objectForKey:@"CFBundleIconFiles"] objectAtIndex:0];
|
||||
NSString *imageType = @"";
|
||||
NSString *path = [mainBundle pathForResource:imageName ofType:imageType];
|
||||
NSImage *image = _imageForBundleInfo(infoDict);
|
||||
return _iconFromImage(image);
|
||||
}
|
||||
|
||||
void _registerWindowsClass()
|
||||
{
|
||||
WNDCLASSEX wc = { 0 };
|
||||
|
||||
// Register the main window class.
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = (WNDPROC)MainWndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
// Keep extra space for each window, for OFF_LEVEL and OFF_ORDERED
|
||||
wc.cbWndExtra = WIN_EXTRABYTES;
|
||||
wc.hInstance = (HINSTANCE)gHandleDLL;
|
||||
wc.hIcon = NULL;
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = WINDOW_CLASS_NAME;
|
||||
wc.hIconSm = NULL;
|
||||
|
||||
if (!RegisterClassEx(&wc))
|
||||
{
|
||||
NSLog(@"%s:error registering windows class - error: %d", __PRETTY_FUNCTION__, GetLastError());
|
||||
return;
|
||||
}
|
||||
// FIXME We should use GetSysColor to get standard colours from MS Window and
|
||||
// use them in NSColor
|
||||
|
||||
// Should we create a message only window here, so we can get events, even when
|
||||
// no windows are created?
|
||||
}
|
||||
|
||||
void _unregisterWindowsClass()
|
||||
{
|
||||
UnregisterClass(WINDOW_CLASS_NAME, (HINSTANCE)gHandleDLL);
|
||||
}
|
||||
|
||||
/* Windows documentation.....
|
||||
|
||||
See: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx
|
||||
|
||||
An application-defined message identifier. The system uses this identifier to send
|
||||
notification messages to the window identified in hWnd. These notification messages
|
||||
are sent when a mouse event or hover occurs in the bounding rectangle of the icon,
|
||||
when the icon is selected or activated with the keyboard, or when those actions occur
|
||||
in the balloon notification.
|
||||
|
||||
When the uVersion member is either 0 or NOTIFYICON_VERSION, the wParam parameter of the
|
||||
message contains the identifier of the taskbar icon in which the event occurred. This
|
||||
identifier can be 32 bits in length. The lParam parameter holds the mouse or keyboard
|
||||
message associated with the event. For example, when the pointer moves over a taskbar
|
||||
icon, lParam is set to WM_MOUSEMOVE.
|
||||
|
||||
When the uVersion member is NOTIFYICON_VERSION_4, applications continue to receive
|
||||
notification events in the form of application-defined messages through the uCallbackMessage
|
||||
member, but the interpretation of the lParam and wParam parameters of that message is changed
|
||||
as follows:
|
||||
o LOWORD(lParam) contains notification events, such as NIN_BALLOONSHOW, NIN_POPUPOPEN, or
|
||||
WM_CONTEXTMENU.
|
||||
o HIWORD(lParam) contains the icon ID. Icon IDs are restricted to a length of 16 bits.
|
||||
o GET_X_LPARAM(wParam) returns the X anchor coordinate for notification events NIN_POPUPOPEN,
|
||||
NIN_SELECT, NIN_KEYSELECT, and all mouse messages between WM_MOUSEFIRST and WM_MOUSELAST.
|
||||
If any of those messages are generated by the keyboard, wParam is set to the upper-left corner
|
||||
of the target icon. For all other messages, wParam is undefined.
|
||||
o GET_Y_LPARAM(wParam) returns the Y anchor coordinate for notification events and messages
|
||||
as defined for the X anchor.
|
||||
|
||||
*/
|
||||
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
LRESULT status = 0;
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:hwnd: %p uMsg %d wParam: %p lParam: %p", __PRETTY_FUNCTION__, hwnd, uMsg, wParam, lParam);
|
||||
#endif
|
||||
|
||||
// If it's not our notify event message ID then...
|
||||
if (uMsg != gNotifyMsg)
|
||||
{
|
||||
// invoke default windows procedure to handle the message...
|
||||
status = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
else // Otherwise we'll process it...
|
||||
{
|
||||
UINT loword = LOWORD(lParam);
|
||||
|
||||
switch (loword)
|
||||
{
|
||||
case NIN_SELECT:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:NIN_SELECT:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NIN_BALLOONSHOW:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:NIN_BALLOONSHOW:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NIN_BALLOONHIDE:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:NIN_BALLOONHIDE:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NIN_BALLOONTIMEOUT:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:NIN_BALLOONTIMEOUT:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NIN_BALLOONUSERCLICK:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:NIN_BALLOONUSERCLICK:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NIN_POPUPOPEN:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:NIN_POPUPOPEN:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NIN_POPUPCLOSE:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:NIN_POPUPCLOSE:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_MOUSEMOVE:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_MOUSEMOVE:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_LBUTTONUP:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_LBUTTONDOWN:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_LBUTTONDBLCLK:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_LBUTTONDBLCLK:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_RBUTTONUP:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_RBUTTONUP:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_RBUTTONDOWN:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_RBUTTONDOWN:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_RBUTTONDBLCLK:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_RBUTTONDBLCLK:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_CONTEXTMENU:
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:WM_CONTEXTMENU:", __PRETTY_FUNCTION__);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
NSLog(@"%s:default - unhandled notification event: %d", __PRETTY_FUNCTION__, loword);
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup...
|
||||
[pool drain];
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NSMutableDictionary *appIconsForProcess()
|
||||
{
|
||||
return [gProcessInfo objectForKey:@"AppIcons"];
|
||||
}
|
||||
|
||||
NSMutableDictionary *appIconInfoForID(UINT appIconID)
|
||||
{
|
||||
NSNumber *appIDValue = [NSNumber numberWithInteger:appIconID];
|
||||
NSDictionary *appIcons = appIconsForProcess();
|
||||
NSDictionary *appIcon = [appIcons objectForKey:appIDValue];
|
||||
NSLog(@"Appicon for ID: %d value: %@", __PRETTY_FUNCTION__, appIconID, appIcon);
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
void removeAppIconInfoForID(UINT appIconID)
|
||||
{
|
||||
NSNumber *appIDValue = [NSNumber numberWithInteger:appIconID];
|
||||
NSMutableDictionary *appIcons = appIconsForProcess();
|
||||
[appIcons removeObjectForKey:appIDValue];
|
||||
}
|
||||
|
||||
void removeAppIconsForProcess()
|
||||
{
|
||||
// Clean up applications icons on task bar...
|
||||
NSDictionary *appicons = appIconsForProcess();
|
||||
NSEnumerator *iconsIter = [appicons objectEnumerator];
|
||||
NSDictionary *iconInfo = nil;
|
||||
|
||||
while ((iconInfo = [iconsIter nextObject]))
|
||||
{
|
||||
UINT appIconID = [[iconInfo objectForKey:@"AppIconID"] integerValue];
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:removing proc ID: %d iconID: %d", __PRETTY_FUNCTION__, GetCurrentProcessId(), appIconID);
|
||||
#endif
|
||||
|
||||
// Remove from task bar...
|
||||
_removeApplicationIconForID(appIconID);
|
||||
|
||||
// And destroy the icon memory...
|
||||
DestroyIcon((HICON)[[iconInfo objectForKey:@"AppIcon"] pointerValue]);
|
||||
}
|
||||
}
|
||||
|
||||
void _removeProcessInfo()
|
||||
{
|
||||
removeAppIconsForProcess();
|
||||
[gProcessInfo release];
|
||||
}
|
||||
|
||||
UINT _addApplicationIcon(DWORD processID, const char *uuidString, HICON icon)
|
||||
{
|
||||
NSValue *iconValue = [NSValue valueWithPointer:icon];
|
||||
NSMutableDictionary *appIcons = appIconsForProcess();
|
||||
UINT appID = -1;
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:icon: %p iconValue: %@ appIcons: %@", __PRETTY_FUNCTION__, icon, iconValue, appIcons);
|
||||
#endif
|
||||
|
||||
if ([appIcons objectForKey:iconValue] != nil)
|
||||
{
|
||||
NSDictionary *appIcon = [appIcons objectForKey:iconValue];
|
||||
appID = [[appIcon objectForKey:@"AppIconID"] integerValue];
|
||||
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:re-using icon: %p with uID: %d", __PRETTY_FUNCTION__, icon, appID);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:adding app icon for UUID: %p icon: %p", __PRETTY_FUNCTION__, uuidString, icon);
|
||||
#endif
|
||||
|
||||
NOTIFYICONDATA notifyData = { 0 };
|
||||
|
||||
// Initialize basic structure...
|
||||
_setupNotifyData(notifyData);
|
||||
_setupNotifyDataIcon(notifyData, icon);
|
||||
notifyData.uID = gNotifyCnt++;
|
||||
|
||||
// Adding...
|
||||
if (Shell_NotifyIcon(NIM_ADD, ¬ifyData) == 0)
|
||||
{
|
||||
NSLog(@"%s:adding windows notification icon failed - error: %ld", __PRETTY_FUNCTION__, GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Set version......
|
||||
if (Shell_NotifyIcon(NIM_SETVERSION, ¬ifyData) == 0)
|
||||
{
|
||||
NSLog(@"%s:setting windows notification version failed - error: %ld", __PRETTY_FUNCTION__, GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Add the application ID for this icon...
|
||||
appID = notifyData.uID;
|
||||
|
||||
// Remember this information...
|
||||
#if defined(DEBUG)
|
||||
NSLog(@"%s:setting up icon: %p with uID: %d", __PRETTY_FUNCTION__, icon, appID);
|
||||
#endif
|
||||
NSMutableDictionary *appIcon = [NSMutableDictionary dictionary];
|
||||
NSNumber *appIDValue = [NSNumber numberWithInteger:appID];
|
||||
[appIcon setObject:appIDValue forKey:@"AppIconID"];
|
||||
[appIcon setObject:iconValue forKey:@"AppIcon"];
|
||||
[appIcons setObject:appIcon forKey:iconValue];
|
||||
}
|
||||
|
||||
return appID;
|
||||
}
|
||||
|
||||
void _removeApplicationIcon(DWORD processID, HICON icon)
|
||||
{
|
||||
}
|
||||
|
||||
void _removeApplicationIconForID(UINT appIconID)
|
||||
{
|
||||
NOTIFYICONDATA notifyData = { 0 };
|
||||
|
||||
_setupNotifyData(notifyData);
|
||||
notifyData.uID = appIconID;
|
||||
|
||||
// Deleting...
|
||||
if (Shell_NotifyIcon(NIM_DELETE, ¬ifyData) == 0)
|
||||
{
|
||||
NSLog(@"%s:deleting windows notification icon failed - error: %ld", __PRETTY_FUNCTION__, GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
GUID guidFromUUIDString(NSString *uuidString)
|
||||
{
|
||||
GUID theGUID;
|
||||
NSArray *components = [uuidString componentsSeparatedByString: @"-"];
|
||||
NSScanner *scanner1 = [NSScanner scannerWithString: [components objectAtIndex: 0]];
|
||||
NSScanner *scanner2 = [NSScanner scannerWithString: [components objectAtIndex: 1]];
|
||||
NSScanner *scanner3 = [NSScanner scannerWithString: [components objectAtIndex: 2]];
|
||||
NSString *data4 = [[components objectAtIndex: 3] stringByAppendingString: [components objectAtIndex: 4]];
|
||||
NSScanner *scanner4 = [NSScanner scannerWithString: data4];
|
||||
|
||||
NSUInteger value;
|
||||
[scanner1 scanHexInt: (NSUInteger*)&theGUID.Data1];
|
||||
[scanner2 scanHexInt: (NSUInteger*)&value];
|
||||
theGUID.Data2 = (WORD) value;
|
||||
[scanner3 scanHexInt: (NSUInteger*)&value];
|
||||
theGUID.Data3 = (WORD) value;
|
||||
|
||||
return theGUID;
|
||||
}
|
||||
|
||||
void _setupNotifyDataIcon(NOTIFYICONDATA ¬ifyData, HICON icon)
|
||||
{
|
||||
// If we were not able to load the icon image...
|
||||
if (icon == NULL)
|
||||
{
|
||||
notifyData.dwInfoFlags |= NIIF_INFO;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise use it in the notification...
|
||||
notifyData.uFlags |= NIF_ICON;
|
||||
notifyData.hIcon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
void _setupNotifyDataBalloonIcon(NOTIFYICONDATA ¬ifyData, HICON contentIcon)
|
||||
{
|
||||
#if _WIN32_WINNT >= 0x600
|
||||
// If we were given a content icon image...
|
||||
if (contentIcon != NULL)
|
||||
{
|
||||
// otherwise use it in the notification...
|
||||
notifyData.dwInfoFlags |= NIIF_USER;
|
||||
notifyData.hBalloonIcon = contentIcon;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void _setupNotifyDataUUID(NOTIFYICONDATA ¬ifyData, const char *uuidString)
|
||||
{
|
||||
#if USE_GUID
|
||||
if (uuidString != NULL)
|
||||
{
|
||||
// Setup the flags and GUID...
|
||||
notifyData.uFlags |= NIF_GUID;
|
||||
notifyData.guidItem = guidFromUUIDString([NSString stringWithFormat:@"%s",uuidString]);
|
||||
}
|
||||
#else
|
||||
// Otherwise using uID field...
|
||||
notifyData.uID = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void _setupNotifyDataTextInfo(NOTIFYICONDATA ¬ifyData, const char *title, const char *informativeText)
|
||||
{
|
||||
// Setup the flags...
|
||||
notifyData.uFlags = NIF_TIP | NIF_INFO | NIF_MESSAGE;
|
||||
|
||||
// This text will be shown as the icon's tooltip.
|
||||
StrCpy(notifyData.szTip, informativeText);
|
||||
StrCpy(notifyData.szInfo, title);
|
||||
StrCpy(notifyData.szInfoTitle, informativeText);
|
||||
}
|
||||
|
||||
void _setupNotifyData(NOTIFYICONDATA ¬ifyData)
|
||||
{
|
||||
notifyData.cbSize = sizeof(NOTIFYICONDATA);
|
||||
notifyData.uCallbackMessage = gNotifyMsg;
|
||||
notifyData.hWnd = gHandleWin;
|
||||
notifyData.dwState |= NIS_SHAREDICON;
|
||||
|
||||
// Load the /timeout/version???
|
||||
notifyData.uVersion = NOTIFYICON_VERSION;
|
||||
}
|
||||
|
||||
EXPORT BOOL __cdecl sendNotification(HWND hWnd, HICON icon, SEND_NOTE_INFO_T *note)
|
||||
{
|
||||
#if 0 //defined(DEBUG)
|
||||
NSLog(@"%s:hWnd: %p icon: %p GUID: %p UUID: %s", __PRETTY_FUNCTION__, hWnd, icon, note->uuidString);
|
||||
NSLog(@"%s:note title: %s informativeText: %s", __PRETTY_FUNCTION__, note->title, note->informativeText);
|
||||
#endif
|
||||
|
||||
NOTIFYICONDATA notifyData = { 0 };
|
||||
_setupNotifyData(notifyData);
|
||||
_setupNotifyDataUUID(notifyData, note->uuidString);
|
||||
_setupNotifyDataIcon(notifyData, icon);
|
||||
#if 0
|
||||
_setupNotifyDataBalloonIcon(notifyData, note->contentIcon);
|
||||
#endif
|
||||
_setupNotifyDataTextInfo(notifyData, note->title, note->informativeText);
|
||||
|
||||
// Need the uID for this icon...
|
||||
notifyData.uID = _addApplicationIcon(GetCurrentProcessId(), note->uuidString, icon);
|
||||
|
||||
// Show the notification.
|
||||
// Modifying...
|
||||
if (Shell_NotifyIcon(NIM_MODIFY, ¬ifyData) == 0)
|
||||
{
|
||||
NSLog(@"%s:windows notification update failed for note title %s error: %ld", __PRETTY_FUNCTION__, note->title, GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// TODO: Should we instead return a NSString HERE instead???
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EXPORT BOOL __cdecl removeNotification(HICON icon, REMOVE_NOTE_INFO_T *noteinfo)
|
||||
{
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
NOTIFYICONDATA notifyData = { 0 };
|
||||
BOOL status = TRUE;
|
||||
NSLog(@"%s:%d:ID %d", __PRETTY_FUNCTION__, __LINE__, noteinfo->uniqueID);
|
||||
|
||||
_setupNotifyData(notifyData);
|
||||
notifyData.uID = _addApplicationIcon(GetCurrentProcessId(), NULL, icon);
|
||||
|
||||
// Show the notification.
|
||||
// Modifying...
|
||||
if (Shell_NotifyIcon(NIM_MODIFY, ¬ifyData) == 0)
|
||||
{
|
||||
NSLog(@"%s:windows notification update failed for note ID %d error: %ld", __PRETTY_FUNCTION__, noteinfo->uniqueID, GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
all: ToastNotifications-0.dll
|
||||
|
||||
clean:
|
||||
rm -rf obj
|
||||
(cd ToastNotifications/Debug && (find . -type f | grep -v '.dll' | xargs rm -rf))
|
||||
(cd ToastNotifications/Release && (find . -type f | grep -v '.dll' | xargs rm -rf))
|
||||
(rm -rf ToastNotifications/ToastNotifications/Debug)
|
||||
(rm -rf ToastNotifications/ToastNotifications/Release)
|
||||
(rm -rf ToastNotifications/ipch ToastNotifications/*.sdf)
|
||||
|
||||
obj:
|
||||
mkdir -p obj
|
||||
|
||||
install: ToastNotifications-0.dll
|
||||
|
||||
ifeq ($(debug),yes)
|
||||
ToastNotifications-0.dll: obj ToastNotifications/Debug/ToastNotifications.dll
|
||||
cp -p ToastNotifications/Debug/ToastNotifications.dll obj/ToastNotifications-0.dll
|
||||
else
|
||||
ToastNotifications-0.dll: obj ToastNotifications/Release/ToastNotifications.dll
|
||||
cp -p ToastNotifications/Release/ToastNotifications.dll obj/ToastNotifications-0.dll
|
||||
endif
|
|
@ -0,0 +1,31 @@
|
|||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(warning )
|
||||
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
|
||||
$(warning Perhaps gnustep-make is not properly installed,)
|
||||
$(warning so gnustep-config is not in your PATH.)
|
||||
$(warning )
|
||||
$(warning Your PATH is currently $(PATH))
|
||||
$(warning )
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
|
||||
endif
|
||||
|
||||
LIBRARY_NAME = ToastNotifications
|
||||
|
||||
ToastNotifications_NEEDS_GUI = NO
|
||||
ToastNotification_CFLAGS += -DBUILD_DLL
|
||||
ToastNotification_OBJCFLAGS += -DBUILD_DLL
|
||||
ToastNotifications_LDFLAGS +=
|
||||
ToastNotifications_OBJC_FILES =
|
||||
ToastNotifications_OBJCC_FILES = ToastNotifications.mm
|
||||
ToastNotifications_RESOURCE_FILES +=
|
||||
|
||||
ADDITIONAL_OBJCCFLAGS += -DBUILD_DLL
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
all: ToastNotifications-0.dll
|
||||
|
||||
clean:
|
||||
rm -rf obj
|
||||
|
||||
obj:
|
||||
mkdir -p obj
|
||||
|
||||
install: ToastNotifications-0.dll
|
||||
|
||||
ifeq ($(debug),yes)
|
||||
ToastNotifications-0.dll: obj ToastNotifications/Debug/ToastNotifications.dll
|
||||
cp -p ToastNotifications/Debug/ToastNotifications.dll obj/ToastNotifications-0.dll
|
||||
else
|
||||
ToastNotifications-0.dll: obj ToastNotifications/Release/ToastNotifications.dll
|
||||
cp -p ToastNotifications/Release/ToastNotifications.dll obj/ToastNotifications-0.dll
|
||||
endif
|
|
@ -0,0 +1,70 @@
|
|||
#include <Foundation/NSString.h>
|
||||
#include <Foundation/NSScanner.h>
|
||||
#include <Foundation/NSUserNotification.h>
|
||||
#include <Foundation/NSUUID.h>
|
||||
#include <Foundation/NSValue.h>
|
||||
#include <../MSUserNotification.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef BUILD_DLL
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" { // Only if you are using C++ rather than C
|
||||
#endif
|
||||
static HANDLE gHandleDLL = NULL;
|
||||
|
||||
// Need to capture the DLL instance handle....
|
||||
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
|
||||
{
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH: // Do process attach specific stuff here...
|
||||
// Save the DLL instance handle...
|
||||
gHandleDLL = hinstDLL;
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH: // Do process attach specific stuff here...
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH: // Do thread attach specific stuff here...
|
||||
break;
|
||||
|
||||
case DLL_THREAD_DETACH: // Do thread detach specific stuff here...
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EXPORT NSString * __cdecl sendNotification(HWND hWnd, NSMutableString *theGUID, HICON icon, NSUserNotification *note)
|
||||
{
|
||||
NSString *uuid = [[NSUUID UUID] UUIDString];
|
||||
NSLog(@"%s:UUID: %@", __PRETTY_FUNCTION__, uuid);
|
||||
NSArray *components = [uuid componentsSeparatedByString: @"-"];
|
||||
NSLog(@"%s:components: %@", __PRETTY_FUNCTION__, components);
|
||||
GUID myGUID;
|
||||
NSString *data4 = [[components objectAtIndex: 3] stringByAppendingString: [components objectAtIndex: 4]];
|
||||
myGUID.Data1 = [[components objectAtIndex: 0] longLongValue];
|
||||
myGUID.Data2 = [[components objectAtIndex: 1] integerValue];
|
||||
myGUID.Data3 = [[components objectAtIndex: 2] integerValue];
|
||||
int index;
|
||||
for (index = 0; index < 8; index++)
|
||||
myGUID.Data4[index] = [data4 characterAtIndex: index];
|
||||
|
||||
// TODO: Add your Toast code here...
|
||||
|
||||
// IF ERROR DO NOT RETURN UUID...
|
||||
if (true)
|
||||
return nil;
|
||||
|
||||
// TOD: Return status...
|
||||
return uuid;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.31101.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToastNotifications", "ToastNotifications\ToastNotifications.vcxproj", "{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
Binary file not shown.
|
@ -0,0 +1,67 @@
|
|||
========================================================================
|
||||
MICROSOFT FOUNDATION CLASS LIBRARY : ToastNotifications Project Overview
|
||||
========================================================================
|
||||
|
||||
|
||||
AppWizard has created this ToastNotifications DLL for you. This DLL not only
|
||||
demonstrates the basics of using the Microsoft Foundation classes but
|
||||
is also a starting point for writing your DLL.
|
||||
|
||||
This file contains a summary of what you will find in each of the files that
|
||||
make up your ToastNotifications DLL.
|
||||
|
||||
ToastNotifications.vcxproj
|
||||
This is the main project file for VC++ projects generated using an Application Wizard.
|
||||
It contains information about the version of Visual C++ that generated the file, and
|
||||
information about the platforms, configurations, and project features selected with the
|
||||
Application Wizard.
|
||||
|
||||
ToastNotifications.vcxproj.filters
|
||||
This is the filters file for VC++ projects generated using an Application Wizard.
|
||||
It contains information about the association between the files in your project
|
||||
and the filters. This association is used in the IDE to show grouping of files with
|
||||
similar extensions under a specific node (for e.g. ".cpp" files are associated with the
|
||||
"Source Files" filter).
|
||||
|
||||
ToastNotifications.h
|
||||
This is the main header file for the DLL. It declares the
|
||||
CToastNotificationsApp class.
|
||||
|
||||
ToastNotifications.cpp
|
||||
This is the main DLL source file. It contains the class CToastNotificationsApp.
|
||||
|
||||
ToastNotifications.rc
|
||||
This is a listing of all of the Microsoft Windows resources that the
|
||||
program uses. It includes the icons, bitmaps, and cursors that are stored
|
||||
in the RES subdirectory. This file can be directly edited in Microsoft
|
||||
Visual C++.
|
||||
|
||||
res\ToastNotifications.rc2
|
||||
This file contains resources that are not edited by Microsoft
|
||||
Visual C++. You should place all resources not editable by
|
||||
the resource editor in this file.
|
||||
|
||||
ToastNotifications.def
|
||||
This file contains information about the DLL that must be
|
||||
provided to run with Microsoft Windows. It defines parameters
|
||||
such as the name and description of the DLL. It also exports
|
||||
functions from the DLL.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Other standard files:
|
||||
|
||||
StdAfx.h, StdAfx.cpp
|
||||
These files are used to build a precompiled header (PCH) file
|
||||
named ToastNotifications.pch and a precompiled types file named StdAfx.obj.
|
||||
|
||||
Resource.h
|
||||
This is the standard header file, which defines new resource IDs.
|
||||
Microsoft Visual C++ reads and updates this file.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Other notes:
|
||||
|
||||
AppWizard uses "TODO:" to indicate parts of the source code you
|
||||
should add to or customize.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
|
@ -0,0 +1,16 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by ToastNotifications.rc
|
||||
//
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
#define _APS_NEXT_RESOURCE_VALUE 2000
|
||||
#define _APS_NEXT_CONTROL_VALUE 2000
|
||||
#define _APS_NEXT_SYMED_VALUE 2000
|
||||
#define _APS_NEXT_COMMAND_VALUE 32771
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
class StringReferenceWrapper
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructor which takes an existing string buffer and its length as the parameters.
|
||||
// It fills an HSTRING_HEADER struct with the parameter.
|
||||
// Warning: The caller must ensure the lifetime of the buffer outlives this
|
||||
// object as it does not make a copy of the wide string memory.
|
||||
|
||||
StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw()
|
||||
{
|
||||
HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
~StringReferenceWrapper()
|
||||
{
|
||||
WindowsDeleteString(_hstring);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw()
|
||||
{
|
||||
UINT32 length = N-1;
|
||||
HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t _>
|
||||
StringReferenceWrapper(_In_reads_(_) wchar_t (&stringRef)[_]) throw()
|
||||
{
|
||||
UINT32 length;
|
||||
HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
|
||||
}
|
||||
|
||||
WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
|
||||
}
|
||||
|
||||
HSTRING Get() const throw()
|
||||
{
|
||||
return _hstring;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
HSTRING _hstring;
|
||||
HSTRING_HEADER _header;
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
#include "stdafx.h"
|
||||
#include "ToastEventHandler.h"
|
||||
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
|
||||
ToastEventHandler::ToastEventHandler(_In_ HWND hToActivate, _In_ HWND hEdit) : _ref(1), _hToActivate(hToActivate), _hEdit(hEdit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ToastEventHandler::~ToastEventHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// DesktopToastActivatedEventHandler
|
||||
IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* sender, _In_ IInspectable* /* args */)
|
||||
{
|
||||
static char str[512];
|
||||
sprintf_s(str, "%s:%d: IToastNotePtr: %p msg: The user clicked on the toast", __FUNCTION__, __LINE__, sender);
|
||||
OutputDebugStringA(str);
|
||||
|
||||
BOOL succeeded = SetForegroundWindow(_hToActivate);
|
||||
if (succeeded)
|
||||
{
|
||||
LRESULT result = SendMessage(_hEdit, WM_SETTEXT, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(L"The user clicked on the toast."));
|
||||
succeeded = result ? TRUE : FALSE;
|
||||
}
|
||||
return succeeded ? S_OK : E_FAIL;
|
||||
}
|
||||
|
||||
// DesktopToastDismissedEventHandler
|
||||
IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* sender, _In_ IToastDismissedEventArgs* e)
|
||||
{
|
||||
ToastDismissalReason tdr;
|
||||
HRESULT hr = e->get_Reason(&tdr);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wchar_t *outputText;
|
||||
switch (tdr)
|
||||
{
|
||||
case ToastDismissalReason_ApplicationHidden:
|
||||
outputText = L"The application hid the toast using ToastNotifier.hide()";
|
||||
break;
|
||||
case ToastDismissalReason_UserCanceled:
|
||||
outputText = L"The user dismissed this toast";
|
||||
break;
|
||||
case ToastDismissalReason_TimedOut:
|
||||
outputText = L"The toast has timed out";
|
||||
break;
|
||||
default:
|
||||
outputText = L"Toast not activated";
|
||||
break;
|
||||
}
|
||||
|
||||
static wchar_t str[512];
|
||||
swprintf_s(str, L"%s:%d: IToastNotePtr: %p msg: %s", TEXT(__FUNCTION__), __LINE__, sender, outputText);
|
||||
OutputDebugStringW(str);
|
||||
|
||||
LRESULT succeeded = SendMessage(_hEdit, WM_SETTEXT, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(outputText));
|
||||
hr = succeeded ? S_OK : E_FAIL;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// DesktopToastFailedEventHandler
|
||||
IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* /* sender */, _In_ IToastFailedEventArgs* /* e */)
|
||||
{
|
||||
LRESULT succeeded = SendMessage(_hEdit, WM_SETTEXT, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(L"The toast encountered an error."));
|
||||
return succeeded ? S_OK : E_FAIL;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
typedef ABI::Windows::Foundation::ITypedEventHandler<ABI::Windows::UI::Notifications::ToastNotification *, ::IInspectable *> DesktopToastActivatedEventHandler;
|
||||
typedef ABI::Windows::Foundation::ITypedEventHandler<ABI::Windows::UI::Notifications::ToastNotification *, ABI::Windows::UI::Notifications::ToastDismissedEventArgs *> DesktopToastDismissedEventHandler;
|
||||
typedef ABI::Windows::Foundation::ITypedEventHandler<ABI::Windows::UI::Notifications::ToastNotification *, ABI::Windows::UI::Notifications::ToastFailedEventArgs *> DesktopToastFailedEventHandler;
|
||||
|
||||
class ToastEventHandler :
|
||||
public Microsoft::WRL::Implements<DesktopToastActivatedEventHandler, DesktopToastDismissedEventHandler, DesktopToastFailedEventHandler>
|
||||
{
|
||||
public:
|
||||
ToastEventHandler::ToastEventHandler(_In_ HWND hToActivate, _In_ HWND hEdit);
|
||||
~ToastEventHandler();
|
||||
|
||||
// DesktopToastActivatedEventHandler
|
||||
IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ IInspectable* args);
|
||||
|
||||
// DesktopToastDismissedEventHandler
|
||||
IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastDismissedEventArgs *e);
|
||||
|
||||
// DesktopToastFailedEventHandler
|
||||
IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastFailedEventArgs *e);
|
||||
|
||||
// IUnknown
|
||||
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_ref); }
|
||||
|
||||
IFACEMETHODIMP_(ULONG) Release() {
|
||||
ULONG l = InterlockedDecrement(&_ref);
|
||||
if (l == 0) delete this;
|
||||
return l;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) {
|
||||
if (IsEqualIID(riid, IID_IUnknown))
|
||||
*ppv = static_cast<IUnknown*>(static_cast<DesktopToastActivatedEventHandler*>(this));
|
||||
else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler)))
|
||||
*ppv = static_cast<DesktopToastActivatedEventHandler*>(this);
|
||||
else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler)))
|
||||
*ppv = static_cast<DesktopToastDismissedEventHandler*>(this);
|
||||
else if (IsEqualIID(riid, __uuidof(DesktopToastFailedEventHandler)))
|
||||
*ppv = static_cast<DesktopToastFailedEventHandler*>(this);
|
||||
else *ppv = nullptr;
|
||||
|
||||
if (*ppv) {
|
||||
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
private:
|
||||
ULONG _ref;
|
||||
HWND _hToActivate;
|
||||
HWND _hEdit;
|
||||
};
|
Binary file not shown.
|
@ -0,0 +1,489 @@
|
|||
// ToastNotifications.cpp : Defines the initialization routines for the DLL.
|
||||
//
|
||||
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <iostream>
|
||||
#include <Windows.Foundation.h>
|
||||
#include <wrl\implements.h>
|
||||
#include <wrl\client.h>
|
||||
#include <wrl\wrappers\corewrappers.h>
|
||||
#include <Windows.ui.notifications.h>
|
||||
#include <strsafe.h>
|
||||
#include "ToastNotifications.h"
|
||||
#include "ToastEventHandler.h"
|
||||
#include "../../../MSUserNotificationAPI.h"
|
||||
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <string>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
using namespace ABI::Windows::Data::Xml::Dom;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#endif
|
||||
|
||||
//
|
||||
//TODO: If this DLL is dynamically linked against the MFC DLLs,
|
||||
// any functions exported from this DLL which call into
|
||||
// MFC must have the AFX_MANAGE_STATE macro added at the
|
||||
// very beginning of the function.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
|
||||
// {
|
||||
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||||
// // normal function body here
|
||||
// }
|
||||
//
|
||||
// It is very important that this macro appear in each
|
||||
// function, prior to any calls into MFC. This means that
|
||||
// it must appear as the first statement within the
|
||||
// function, even before any object variable declarations
|
||||
// as their constructors may generate calls into the MFC
|
||||
// DLL.
|
||||
//
|
||||
// Please see MFC Technical Notes 33 and 58 for additional
|
||||
// details.
|
||||
//
|
||||
|
||||
// CToastNotificationsApp
|
||||
|
||||
BEGIN_MESSAGE_MAP(CToastNotificationsApp, CWinApp)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
// CToastNotificationsApp construction
|
||||
|
||||
CToastNotificationsApp::CToastNotificationsApp()
|
||||
{
|
||||
// TODO: add construction code here,
|
||||
// Place all significant initialization in InitInstance
|
||||
}
|
||||
|
||||
CToastNotificationsApp::~CToastNotificationsApp()
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
static char str[512];
|
||||
sprintf_s(str, "%s:%d: DONE", __FUNCTION__, __LINE__);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The one and only CToastNotificationsApp object
|
||||
|
||||
CToastNotificationsApp theApp;
|
||||
|
||||
|
||||
// CToastNotificationsApp initialization
|
||||
|
||||
BOOL CToastNotificationsApp::InitInstance()
|
||||
{
|
||||
CWinApp::InitInstance();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Create the toast XML from a template
|
||||
HRESULT CToastNotificationsApp::CreateToastXml(_In_ IToastNotificationManagerStatics *toastManager, _Outptr_ IXmlDocument** inputXml, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
int number = 600;
|
||||
char str[256];
|
||||
sprintf_s(str, "inside create toast xml and calling GetTemplateContent %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
HRESULT hr = toastManager->GetTemplateContent(ToastTemplateType_ToastImageAndText04, inputXml);
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "done with GetTemplateContent %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "OK inside\n");
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
//wchar_t *imagePath = _wfullpath(nullptr, L"toastImageAndText.png", MAX_PATH);
|
||||
|
||||
#if defined(DEBUG)
|
||||
static wchar_t wstr[512];
|
||||
swprintf_s(wstr, TEXT("%s:%d:imagePath: %s"), TEXT(__FUNCTION__), __LINE__, imagePath);
|
||||
OutputDebugStringW(wstr);
|
||||
#endif
|
||||
|
||||
hr = imagePath != nullptr ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "got hte image path and now setting its source");
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
hr = SetImageSrc(imagePath, *inputXml);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "done setting the source");
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
wchar_t* textValues[] = {
|
||||
notificationTitle,
|
||||
notificationDescription,
|
||||
L" "
|
||||
};
|
||||
|
||||
UINT32 textLengths[] = { wcslen(notificationTitle), wcslen(notificationDescription), 6 };
|
||||
hr = SetTextValues(textValues, 3, textLengths, *inputXml);
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "AHh!with imagepath %ld\n", GetLastError());
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "AHh! Shoot, done with GetTemplateContent %ld\n", GetLastError());
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CToastNotificationsApp::SetTextValues(_In_reads_(textValuesCount) wchar_t **textValues, _In_ UINT32 textValuesCount, _In_reads_(textValuesCount) UINT32 *textValuesLengths, _In_ IXmlDocument *toastXml)
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
int number = 600;
|
||||
char str[256];
|
||||
sprintf_s(str, "inside set text values %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
HRESULT hr = textValues != nullptr && textValuesCount > 0 ? S_OK : E_INVALIDARG;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IXmlNodeList> nodeList;
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "before calling get tag names %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"text").Get(), &nodeList);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "inside succeeded %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
UINT32 nodeListLength;
|
||||
hr = nodeList->get_Length(&nodeListLength);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "inside got length %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
hr = textValuesCount <= nodeListLength ? S_OK : E_INVALIDARG;
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "insidenode list length %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
for (UINT32 i = 0; i < textValuesCount; i++)
|
||||
{
|
||||
ComPtr<IXmlNode> textNode;
|
||||
hr = nodeList->Item(i, &textNode);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "before calling node value string %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
hr = SetNodeValueString(StringReferenceWrapper(textValues[i], textValuesLengths[i]).Get(), textNode.Get(), toastXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//int number = 600;
|
||||
//char str[256];
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "returning from set text values %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CToastNotificationsApp::SetImageSrc(_In_z_ wchar_t *imagePath, _In_ IXmlDocument *toastXml)
|
||||
{
|
||||
wchar_t imageSrc[MAX_PATH] = L"file:///";
|
||||
HRESULT hr = StringCchCat(imageSrc, ARRAYSIZE(imageSrc), imagePath);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IXmlNodeList> nodeList;
|
||||
hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"image").Get(), &nodeList);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IXmlNode> imageNode;
|
||||
hr = nodeList->Item(0, &imageNode);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IXmlNamedNodeMap> attributes;
|
||||
|
||||
hr = imageNode->get_Attributes(&attributes);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IXmlNode> srcAttribute;
|
||||
|
||||
hr = attributes->GetNamedItem(StringReferenceWrapper(L"src").Get(), &srcAttribute);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
static char str[256];
|
||||
sprintf_s(str, "setting image source %d", hr);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
hr = SetNodeValueString(StringReferenceWrapper(imageSrc).Get(), srcAttribute.Get(), toastXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
int number = 600;
|
||||
char str[256];
|
||||
sprintf_s(str, "returning from set image source %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CToastNotificationsApp::SetNodeValueString(_In_ HSTRING inputString, _In_ IXmlNode *node, _In_ IXmlDocument *xml)
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
int number = 600;
|
||||
char str[256];
|
||||
sprintf_s(str, "inside node value string %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
ComPtr<IXmlText> inputText;
|
||||
|
||||
HRESULT hr = xml->CreateTextNode(inputString, &inputText);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "done create node text %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
ComPtr<IXmlNode> inputTextNode;
|
||||
|
||||
hr = inputText.As(&inputTextNode);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IXmlNode> pAppendedChild;
|
||||
hr = node->AppendChild(inputTextNode.Get(), &pAppendedChild);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
number = 600;
|
||||
sprintf_s(str, "returning from set node value string %d\n", number);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CToastNotificationsApp::CreateToast(_In_ IToastNotificationManagerStatics *toastManager, _In_ IXmlDocument *xml, HWND hWnd)
|
||||
{
|
||||
_hwnd = hWnd;
|
||||
ComPtr<IToastNotifier> notifier;
|
||||
HRESULT hr = toastManager->CreateToastNotifierWithId(StringReferenceWrapper(AppId).Get(), ¬ifier);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IToastNotificationFactory> factory;
|
||||
hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IToastNotification> toast;
|
||||
hr = factory->CreateToastNotification(xml, &toast);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Register the event handlers
|
||||
EventRegistrationToken activatedToken, dismissedToken, failedToken;
|
||||
ComPtr<ToastEventHandler> eventHandler(new ToastEventHandler(_hwnd, _hwnd));
|
||||
|
||||
toast->add_Activated(eventHandler.Get(), &activatedToken);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = toast->add_Failed(eventHandler.Get(), &failedToken);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = notifier->Show(toast.Get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
static wchar_t str[256];
|
||||
swprintf_s(str, TEXT("returning from create toast"));
|
||||
OutputDebugString(str);
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CToastNotificationsApp::DisplayToast(HWND hWnd, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath)
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
static wchar_t str[512];
|
||||
swprintf_s(str, L"%s:%d: note title: %su infoText: %su", TEXT(__FUNCTION__), __LINE__, notificationTitle, notificationTitle, imagePath);
|
||||
OutputDebugString(str);
|
||||
#endif
|
||||
|
||||
ComPtr<IToastNotificationManagerStatics> toastStatics;
|
||||
|
||||
HRESULT hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastStatics);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IXmlDocument> toastXml;
|
||||
hr = CreateToastXml(toastStatics.Get(), &toastXml, notificationTitle, notificationDescription, imagePath);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
#if defined(DEBUG)
|
||||
char str[256];
|
||||
sprintf_s(str, "done with toast xml and calling the toast method %d\n");
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
hr = CreateToast(toastStatics.Get(), toastXml.Get(), hWnd);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
else
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" EXPORT BOOL __cdecl sendNotification(HWND hWnd, HICON icon, SEND_NOTE_INFO_T *noteInfo)
|
||||
{
|
||||
//NSLog(@"%s:hWnd: %p icon: %p GUID: %p note: %p", __PRETTY_FUNCTION__, hWnd, icon, note);
|
||||
|
||||
#if defined(DEBUG)
|
||||
static char str[512];
|
||||
sprintf_s(str, "%s:%d: note %p", __FUNCTION__, __LINE__, noteInfo);
|
||||
OutputDebugStringA(str);
|
||||
sprintf_s(str, "%s:%d: noteInfo ptrs: title: %p infoText: %p imagePath: %p", __FUNCTION__, __LINE__, noteInfo->title, noteInfo->informativeText, noteInfo->appIconPath);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "%s:%d: note title: %s", __FUNCTION__, __LINE__, noteInfo->title);
|
||||
OutputDebugStringA(str);
|
||||
sprintf_s(str, "%s:%d: note infoText: %s", __FUNCTION__, __LINE__, noteInfo->informativeText);
|
||||
OutputDebugStringA(str);
|
||||
sprintf_s(str, "%s:%d: note contentPath: %s", __FUNCTION__, __LINE__, noteInfo->appIconPath);
|
||||
OutputDebugStringA(str);
|
||||
sprintf_s(str, "%s:%d: note UUID: %s", __FUNCTION__, __LINE__, noteInfo->uuidString);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
std::wstring title = converter.from_bytes(noteInfo->title);
|
||||
std::wstring description = converter.from_bytes(noteInfo->informativeText);
|
||||
std::wstring imagePath = TEXT("");
|
||||
|
||||
// Convert content image path if available...
|
||||
if (noteInfo->appIconPath != NULL)
|
||||
imagePath = converter.from_bytes(noteInfo->appIconPath);
|
||||
|
||||
#if defined(DEBUG)
|
||||
static wchar_t wstr[512];
|
||||
swprintf_s(wstr, TEXT("%s:%d: note title: %s infoText: %s imagePath: %s"), TEXT(__FUNCTION__), __LINE__, title.c_str(), description.c_str(), imagePath.c_str());
|
||||
OutputDebugString(wstr);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
CToastNotificationsApp *app = new CToastNotificationsApp();
|
||||
HRESULT hr = app->DisplayToast(hWnd, const_cast<wchar_t*>(title.c_str()), const_cast<wchar_t*>(description.c_str()));
|
||||
#else
|
||||
HRESULT hr = theApp.DisplayToast(hWnd, const_cast<wchar_t*>(title.c_str()), const_cast<wchar_t*>(description.c_str()), const_cast<wchar_t*>(imagePath.c_str()));
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG)
|
||||
sprintf_s(str, "%s:%d: HR %d", __FUNCTION__, __LINE__, hr);
|
||||
OutputDebugStringA(str);
|
||||
#endif
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
EXPORT BOOL __cdecl removeNotification(HICON icon, REMOVE_NOTE_INFO_T *noteinfo)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
; ToastNotifications.def : Declares the module parameters for the DLL.
|
||||
|
||||
LIBRARY
|
||||
|
||||
EXPORTS
|
||||
; Explicit exports can go here
|
|
@ -0,0 +1,64 @@
|
|||
// ToastNotifications.h : main header file for the ToastNotifications DLL
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __AFXWIN_H__
|
||||
#error "include 'stdafx.h' before including this file for PCH"
|
||||
#endif
|
||||
|
||||
#include "resource.h" // main symbols
|
||||
|
||||
const wchar_t AppId[] = L"Microsoft.Samples.DesktopToasts";
|
||||
|
||||
|
||||
|
||||
|
||||
// CToastNotificationsApp
|
||||
// See ToastNotifications.cpp for the implementation of this class
|
||||
//
|
||||
|
||||
class CToastNotificationsApp : public CWinApp
|
||||
{
|
||||
public:
|
||||
CToastNotificationsApp();
|
||||
~CToastNotificationsApp();
|
||||
|
||||
// Overrides
|
||||
public:
|
||||
virtual BOOL InitInstance();
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
friend class ToastEventHandler;
|
||||
|
||||
public:
|
||||
//HRESULT DisplayToast(HWND hWnd, wchar_t* notificationTitle, wchar_t* notificationDescription);
|
||||
HRESULT DisplayToast(HWND hWnd, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath);
|
||||
HRESULT CreateToastXml(
|
||||
_In_ ABI::Windows::UI::Notifications::IToastNotificationManagerStatics *toastManager,
|
||||
_Outptr_ ABI::Windows::Data::Xml::Dom::IXmlDocument **xml, wchar_t* notificationTitle, wchar_t* notificationDescription, wchar_t* imagePath);
|
||||
HRESULT CreateToast(
|
||||
_In_ ABI::Windows::UI::Notifications::IToastNotificationManagerStatics *toastManager,
|
||||
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *xml, HWND hWnd
|
||||
);
|
||||
HRESULT SetImageSrc(
|
||||
_In_z_ wchar_t *imagePath,
|
||||
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *toastXml
|
||||
);
|
||||
HRESULT SetTextValues(
|
||||
_In_reads_(textValuesCount) wchar_t **textValues,
|
||||
_In_ UINT32 textValuesCount,
|
||||
_In_reads_(textValuesCount) UINT32 *textValuesLengths,
|
||||
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *toastXml
|
||||
);
|
||||
HRESULT SetNodeValueString(
|
||||
_In_ HSTRING onputString,
|
||||
_In_ ABI::Windows::Data::Xml::Dom::IXmlNode *node,
|
||||
_In_ ABI::Windows::Data::Xml::Dom::IXmlDocument *xml
|
||||
);
|
||||
|
||||
|
||||
private:
|
||||
HWND _hwnd;
|
||||
HWND _hEdit;
|
||||
};
|
Binary file not shown.
|
@ -0,0 +1,131 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{5158BA99-59BA-4F4C-8D1D-8E8886EDF0BA}</ProjectGuid>
|
||||
<RootNamespace>ToastNotifications</RootNamespace>
|
||||
<Keyword>MFCDLLProj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseOfMfc>Dynamic</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseOfMfc>Dynamic</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>BUILD_DLL;WIN32;_WINDOWS;_DEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>.\ToastNotifications.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>runtimeobject.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<Midl>
|
||||
<MkTypLibCompatible>false</MkTypLibCompatible>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<Culture>0x0409</Culture>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>BUILD_DLL;WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<ModuleDefinitionFile>.\ToastNotifications.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>runtimeobject.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<Midl>
|
||||
<MkTypLibCompatible>false</MkTypLibCompatible>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<Culture>0x0409</Culture>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ToastEventHandler.cpp" />
|
||||
<ClCompile Include="ToastNotifications.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Resource.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="StringReferenceWrapper.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="ToastEventHandler.h" />
|
||||
<ClInclude Include="ToastNotifications.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="res\ToastNotifications.rc2" />
|
||||
<None Include="ToastNotifications.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ToastNotifications.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ToastNotifications.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ToastEventHandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ToastNotifications.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StringReferenceWrapper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ToastEventHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ToastNotifications.def">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="res\ToastNotifications.rc2">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ToastNotifications.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// ToastNotifications.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
|
||||
#pragma once
|
||||
#ifdef BUILD_DLL
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#ifndef VC_EXTRALEAN
|
||||
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
|
||||
#endif
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
|
||||
|
||||
#include <afxwin.h> // MFC core and standard components
|
||||
#include <afxext.h> // MFC extensions
|
||||
|
||||
#ifndef _AFX_NO_OLE_SUPPORT
|
||||
#include <afxole.h> // MFC OLE classes
|
||||
#include <afxodlgs.h> // MFC OLE dialog classes
|
||||
#include <afxdisp.h> // MFC Automation classes
|
||||
#endif // _AFX_NO_OLE_SUPPORT
|
||||
|
||||
#ifndef _AFX_NO_DB_SUPPORT
|
||||
#include <afxdb.h> // MFC ODBC database classes
|
||||
#endif // _AFX_NO_DB_SUPPORT
|
||||
|
||||
#ifndef _AFX_NO_DAO_SUPPORT
|
||||
#include <afxdao.h> // MFC DAO database classes
|
||||
#endif // _AFX_NO_DAO_SUPPORT
|
||||
|
||||
#ifndef _AFX_NO_OLE_SUPPORT
|
||||
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
|
||||
#endif
|
||||
#ifndef _AFX_NO_AFXCMN_SUPPORT
|
||||
#include <afxcmn.h> // MFC support for Windows Common Controls
|
||||
#endif // _AFX_NO_AFXCMN_SUPPORT
|
||||
|
||||
#pragma once
|
||||
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
|
||||
// Windows Header Files:
|
||||
#include <Windows.h>
|
||||
#include <sal.h>
|
||||
#include <Psapi.h>
|
||||
#include <strsafe.h>
|
||||
#include <ObjBase.h>
|
||||
#include <ShObjIdl.h>
|
||||
#include <propvarutil.h>
|
||||
#include <functiondiscoverykeys.h>
|
||||
#include <intsafe.h>
|
||||
#include <guiddef.h>
|
||||
|
||||
#include <roapi.h>
|
||||
#include <wrl\client.h>
|
||||
#include <wrl\implements.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#include "StringReferenceWrapper.h"
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <SDKDDKVer.h>
|
Loading…
Add table
Add a link
Reference in a new issue