/* * Copyright 2010, Haiku, Inc. All rights reserved. * Copyright 2008, Pier Luigi Fiorini. * Distributed under the terms of the MIT License. * * Authors: * Pier Luigi Fiorini * Stephan Aßmus */ #include #include #include #include #include #include #include #include #include #include const char* kSignature = "application/x-vnd.Haiku-notify"; const char* kSmallIconAttribute = "BEOS:M:STD_ICON"; const char* kLargeIconAttribute = "BEOS:L:STD_ICON"; const char* kIconAttribute = "BEOS:ICON"; const char *kTypeNames[] = { "information", "important", "error", "progress", NULL }; const int32 kErrorInitFail = 127; const int32 kErrorArgumentsFail = 126; class NotifyApp : public BApplication { public: NotifyApp(); virtual ~NotifyApp(); virtual void ReadyToRun(); virtual void ArgvReceived(int32 argc, char** argv); bool HasGoodArguments() const { return fHasGoodArguments; } private: bool fHasGoodArguments; notification_type fType; BString fGroup; BString fTitle; BString fMsgId; float fProgress; bigtime_t fTimeout; BString fIconFile; entry_ref fFileRef; BString fContent; BString fOnClickApp; bool fHasFile; entry_ref fFile; BList* fRefs; BList* fArgv; void _Usage() const; BBitmap* _GetBitmap(const entry_ref* ref) const; }; NotifyApp::NotifyApp() : BApplication(kSignature), fHasGoodArguments(false), fType(B_INFORMATION_NOTIFICATION), fProgress(0.0f), fTimeout(-1), fHasFile(false) { fRefs = new BList(); fArgv = new BList(); } NotifyApp::~NotifyApp() { for (int32 i = 0; void* item = fRefs->ItemAt(i); i++) delete (BEntry*)item; delete fRefs; for (int32 i = 0; void* item = fArgv->ItemAt(i); i++) delete (BString*)item; delete fArgv; } void NotifyApp::ArgvReceived(int32 argc, char** argv) { const uint32 kArgCount = argc - 1; uint32 index = 1; // Look for valid options for (; index <= kArgCount; ++index) { if (argv[index][0] == '-' && argv[index][1] == '-') { const char* option = argv[index] + 2; if (++index > kArgCount) { // No argument to option fprintf(stderr, "Missing argument to option --%s\n\n", option); return; } const char* argument = argv[index]; if (strcmp(option, "type") == 0) { for (int32 i = 0; kTypeNames[i]; i++) { if (strncmp(kTypeNames[i], argument, strlen(argument)) == 0) fType = (notification_type)i; } } else if (strcmp(option, "group") == 0) fGroup = argument; else if (strcmp(option, "title") == 0) fTitle = argument; else if (strcmp(option, "messageID") == 0) fMsgId = argument; else if (strcmp(option, "progress") == 0) fProgress = atof(argument); else if (strcmp(option, "timeout") == 0) fTimeout = atol(argument) * 1000000; else if (strcmp(option, "icon") == 0) { fIconFile = argument; if (get_ref_for_path(fIconFile.String(), &fFileRef) < B_OK) { fprintf(stderr, "Bad icon path!\n\n"); return; } } else if (strcmp(option, "onClickApp") == 0) fOnClickApp = argument; else if (strcmp(option, "onClickFile") == 0) { if (get_ref_for_path(argument, &fFile) != B_OK) { fprintf(stderr, "Bad path for --onClickFile!\n\n"); return; } fHasFile = true; } else if (strcmp(option, "onClickRef") == 0) { entry_ref ref; if (get_ref_for_path(argument, &ref) != B_OK) { fprintf(stderr, "Bad path for --onClickRef!\n\n"); return; } fRefs->AddItem(new BEntry(&ref)); } else if (strcmp(option, "onClickArgv") == 0) fArgv->AddItem(new BString(argument)); else { // Unrecognized option fprintf(stderr, "Unrecognized option --%s\n\n", option); return; } } else { // Option doesn't start with '--' break; } if (index == kArgCount) { // No text argument provided, only '--' arguments fprintf(stderr, "Missing message argument!\n\n"); return; } } fContent = argv[index]; fHasGoodArguments = true; } void NotifyApp::_Usage() const { fprintf(stderr, "Usage: notify [OPTION]... [MESSAGE]\n" "Send notifications to notification_server.\n" " --type \tNotification type,\n" " \t - \"information\" is assumed by default: "); for (int32 i = 0; kTypeNames[i]; i++) fprintf(stderr, kTypeNames[i + 1] ? "%s|" : "%s\n", kTypeNames[i]); fprintf(stderr, " --group \tGroup\n" " --title \tMessage title\n" " --messageID <msg id>\tMessage ID\n" " --progress <float>\tProgress, value between 0.0 and 1.0 - if type is set to progress\n" " --timeout <secs>\tSpecify timeout\n" " --onClickApp <signature>\tApplication to open when notification is clicked\n" " --onClickFile <fullpath>\tFile to open when notification is clicked\n" " --onClickRef <fullpath>\tFile to open with the application when notification is clicked\n" " --onClickArgv <arg>\tArgument to the application when notification is clicked\n" " --icon <icon file> Icon\n"); } BBitmap* NotifyApp::_GetBitmap(const entry_ref* ref) const { BBitmap* bitmap = NULL; // First try by contents bitmap = BTranslationUtils::GetBitmap(ref); if (bitmap) return bitmap; // Then, try reading its attribute BNode node(BPath(ref).Path()); bitmap = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1, (float)B_LARGE_ICON - 1), B_RGBA32); if (BIconUtils::GetIcon(&node, kIconAttribute, kSmallIconAttribute, kLargeIconAttribute, B_LARGE_ICON, bitmap) != B_OK) { delete bitmap; bitmap = NULL; } return bitmap; } void NotifyApp::ReadyToRun() { if (HasGoodArguments()) { BNotification notification(fType); if (fGroup != "") notification.SetGroup(fGroup); if (fTitle != "") notification.SetTitle(fTitle); if (fContent != "") notification.SetContent(fContent); if (fMsgId != "") notification.SetMessageID(fMsgId); if (fType == B_PROGRESS_NOTIFICATION) notification.SetProgress(fProgress); if (fIconFile != "") { BBitmap* bitmap = _GetBitmap(&fFileRef); if (bitmap) { notification.SetIcon(bitmap); delete bitmap; } } if (fOnClickApp != "") notification.SetOnClickApp(fOnClickApp); if (fHasFile) notification.SetOnClickFile(&fFile); for (int32 i = 0; void* item = fRefs->ItemAt(i); i++) { BEntry* entry = (BEntry*)item; entry_ref ref; if (entry->GetRef(&ref) == B_OK) notification.AddOnClickRef(&ref); } for (int32 i = 0; void* item = fArgv->ItemAt(i); i++) { BString* arg = (BString*)item; notification.AddOnClickArg(arg->String()); } status_t ret = notification.Send(fTimeout); if (ret != B_OK) { fprintf(stderr, "Failed to deliver notification: %s\n", strerror(ret)); } } else _Usage(); Quit(); } int main(int argc, char** argv) { NotifyApp app; if (app.InitCheck() != B_OK) return kErrorInitFail; app.Run(); if (!app.HasGoodArguments()) return kErrorArgumentsFail; return 0; }