diff --git a/AUTHORS b/AUTHORS index 00c7322..5f37798 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,7 +1,3 @@ -Main developers: - Christian Hammond - Mike Hearn - -Patch contributors: - Richard Hughes - Duarte Henriques +Christian Hammond +Mike Hearn +John (J5) Palmieri diff --git a/ChangeLog b/ChangeLog index 0111c72..342b74f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,179 +1,101 @@ -Wed Nov 09 00:50:23 PST 2005 Christian Hammond +Tue Jan 10 23:33:47 PST 2006 Christian Hammond + * configure.ac: + - Require D-BUS 0.36+. + +Tue Jan 10 22:55:35 PST 2006 Christian Hammond + + * tests/test-default-action.c: * tools/notify-send.c: - - Call notify_icon_new_from_uri() and not notify_icon_new(), - since notify_icon_new() doesn't actually take parameters. - - Fix the help for --type. + - Fix some C99 usage. -Sun Nov 06 16:33:30 PST 2005 Christian Hammond +Tue Jan 10 16:20:27 MST 2006 David Trowbridge - * tests/Makefile.am: - - Add applet-critical.png to EXTRA_DIST. + * libnotify/Makefile.am: + - Make sure marshal files actually get built -==================== 0.2.2 ==================== +Mon Jan 09 11:13:15 PST 2006 Christian Hammond -Wed Aug 10 01:59:26 PDT 2005 Christian Hammond - - * NEWS: + * libnotify/Makefile.am: + * libnotify/Makefile.in: + A libnotify/notify-marshal.list: + D libnotify/notifymarshal.c: + D libnotify/notifymarshal.h: + * libnotify/notifynotification.c: * configure.ac: - - Bump to version 0.2.2 and prepare for release. + - Generate the marshal files automatically. We shouldn't be doing it + by hand. -Wed Aug 10 01:13:56 PDT 2005 Christian Hammond - - D tools/test-replace.c: - * tools/notify-send.c: - - Simplify notify-send a little, and nuke tools/test-replace. It - doesn't belong there. - -Wed Aug 10 01:12:55 PDT 2005 Christian Hammond - - * NEWS: - * configure.ac: - - Bump to version 0.2.1.90. - -Mon Jul 25 22:50:08 PDT 2005 Christian Hammond - - * NEWS: - * configure.ac: - - Bump to version 0.2.1. - -Mon Jul 25 22:47:02 PDT 2005 Christian Hammond +Mon Jan 09 10:15:57 PST 2006 Christian Hammond + * libnotify/Makefile.in: * libnotify/notify.c: - * tests/test-xy.c: - - Use a variant type for the hint dictionary values. This let's us - actually use various data types in D-BUS 0.3x. - -Thu Jul 28 01:54:27 PDT 2005 Christian Hammond - - * docs/notification-spec.xml: - - Bump to version 0.7, and add the x, y hint info. - - * NEWS: - * configure.ac: - - Bump to version 0.2.0. - -Thu Jul 28 01:52:03 PDT 2005 Christian Hammond - - * libnotify/notify.c: - * libnotify/notify.h: - - Don't send all hint values as strings. Send as integers, booleans, - or strings, depending on what the user set. - -Wed Jul 27 23:08:43 PDT 2005 Christian Hammond - - * libnotify/notify.c: - * libnotify/notify.h: - - Support setting boolean hints. - -Mon Jul 25 18:03:12 PDT 2005 Christian Hammond - - * libnotify/notify.c: - - Disable animation support on D-BUS 0.3x, since arrays of arrays - of a type seems broken. - -Mon Jul 25 18:00:12 PDT 2005 Christian Hammond - - * libnotify/notify.c: - * tests/test-animation.c: - - Flush the D-BUS connection queue on uninit so that messages go - through and so we don't trigger this ugly little warning on - D-BUS 0.3x. - - Fix a compiler warning on gcc4. - -Wed Jul 27 02:12:31 PDT 2005 Christian Hammond - - * libnotify/notify.c: - - Actually set the expires flag in the notification handle so we - don't just kill off all notifications as soon as we create them - and exit. - -Wed Jul 27 01:47:25 PDT 2005 Christian Hammond - - * libnotify/notify.c: - - Handle SIGINT and SIGTERM When raised, we call notify_uninit. - notify_uninit now checks if there are any non-expiring notifications - and closes them. This is a best-effort to preventing stale - notifications from a dead process from displaying on the screen. Of - course, we can't handle SIGKILL, so we're stuck there. - -Wed Jul 27 01:13:12 PDT 2005 Christian Hammond - - * libnotify/notify.c: - - Patch (bug #3050) by Duarte Henriques to prevent closing - notifications we don't own. - -Wed Jul 27 01:08:56 PDT 2005 Christian Hammond - - * libnotify/notify.c: - * libnotify/notify.h: + * libnotify/notifynotification.c: * tests/test-default-action.c: * tests/test-image.c: * tests/test-multi-actions.c: - - Patch (bug #3734) by Duarte Henriques to provide a convenient way - of integrating libnotify with the glib mainloop. Modifications by - me to make it even easier. - -Wed Jul 27 00:51:58 PDT 2005 Christian Hammond - - * tests/test-animation.c: - * tests/test-basic.c: - * tests/test-error.c: - * tests/test-image.c: - * tests/test-markup.c: - * tests/test-xy.c: + * tests/test-replace-widget.c: * tests/test-xy-stress.c: - - Actually support our own spec and use the number of seconds until - expiration instead of current time + number of seconds. That was - creating all kinds of problems. + - Clean up lots of warnings. -Wed Jul 27 00:31:47 PDT 2005 Christian Hammond +========================= 0.3.0 ========================= - * libnotify/dbus-compat.h: - - Compile under D-BUS 0.2x again. +Tue Dec 13 2005 John (J5) Palmieri + + * Release 0.3.0 -Wed Jul 27 00:18:16 PDT 2005 Christian Hammond + * tests/Makefile.am, tools/Makefile.am: patch from Rodney + Dawes fixing make distcheck - * libnotify/notify.c: - - If the handle given in ActionInvoked is not found in the local - table of handles, return from the ActionInvoked handler. Otherwise, - every program using libnotify will crash when a notification is - clicked. Oops! + * tools/notify-send.c: use show_and_forget -Sat Jul 16 02:53:06 PDT 2005 Christian Hammond +Fri Dec 09 2005 John (J5) Palmieri - * libnotify/notify.c: - - Fix the key type for the dictionary for hints on D-BUS 0.3x. The - notification-daemon side is still broken in this regard. + * libnotify/notifymarshal.[c|h]: new files where GLib callback + marshalers are added -Sat Jul 16 02:51:42 PDT 2005 Christian Hammond + * libnotify/notifynotification.c (notify_notifiaction_init): Add a + marshaller for signals with uint, string parameters + (_gslist_to_string_array): new internal method that coverts + a GSList to a NULL terminated array of strings + (_notify_notification_show_internal): send the actions list as an + array of strings, not a GSList which does not work with the bindings + + * libnotify/Makefile.am: notifymarshal.[c|h] added - * libnotify/dbus-compat.h: - * libnotify/notify.c: - - Fixed a couple of D-BUS 0.3x bugs. Several remain though. + * tests/test-multi-actions.c: working example of using actions + -Tue Jul 12 12:29:08 PDT 2005 Christian Hammond +Fri Dec 02 2005 John (J5) Palmieri - * AUTHORS: - - Update the AUTHORs file with our current contributors. + * libnotify/notifynotification.c (notify_notification_add_action): + implement adding actions + (_action_signal_handler): handle actions coming from the server + (notify_notifcation_clear_actions): new method for clearning out + the actions list and hash -Tue Jul 12 12:28:16 PDT 2005 Christian Hammond +Fri Dec 02 2005 John (J5) Palmieri - * tests/Makefile.am: - * tools/Makefile.am: - * libnotify.spec.in: - - Patch by Richard Hughes to fix make distcheck, and to provide a - Fedora Core 4 RPM spec file. + * libnotify/notifynotification.c (notify_notification_show_and_forget): + new method that shows and then unrefs the NotifyNotification object. + use this if you just want to fire off a quick notification. -Sun Jul 10 16:46:57 PDT 2005 Christian Hammond + * various compiler warning cleanups - * tests/test-replace.c: - - Decrease the wait time for the replacement notification. +Fri Dec 02 2005 John (J5) Palmieri -Sat Jul 9 18:30:41 2005 Duarte Henriques + * libnotify/notifynotification.c (SIGNAL_TYPE_CLOSED): "closed" glib + signal added + (notify_notification_clear_hints): New API for clearing out the + hints hash + (notify_notification_ref, notify_notification_unref): removed - use + g_object_ref/unref - * libnotify/notify.c: - - Fill in handle->user_data in notify_send_notification_varg + * tests/*: Various changes to the test binaries + +Thu Nov 10 14:00:00 EST 2005 John (J5) Palmieri + + * Complete rewrite and first import to libnotify-ng module in SVN Mon Jul 04 02:13:56 PDT 2005 Christian Hammond diff --git a/NEWS b/NEWS index 77b7e3a..1ce32fb 100644 --- a/NEWS +++ b/NEWS @@ -1,11 +1,4 @@ -version 0.2.3: - * Fix notify-send to work with icon parameters (bug #4308) +libnotify 0.3.0 +=== -version 0.2.2 (10-August-2005): - * Fixed many run-time issues with PPC and AMD64 (Martin Pitt) - -version 0.2.1 (28-July-2005): - * Fixed hint support for D-BUS 0.3x. - -version 0.2.0 (28-July-2005): - * Initial public release. +* First release of the new GObject based API and simpler wire protocol diff --git a/configure.ac b/configure.ac index acd13bf..8759927 100644 --- a/configure.ac +++ b/configure.ac @@ -3,18 +3,18 @@ dnl Process this file with autoconf to create configure. dnl ################################################################ dnl # Initialize autoconf dnl ################################################################ -AC_INIT(libnotify, 0.2.2, chipx86@chipx86.com) +AC_INIT(libnotify, 0.3.0, chipx86@gnupdate.org) AC_PREREQ(2.50) AC_CONFIG_SRCDIR(config.h.in) -AC_COPYRIGHT([Copyright 2004-2005 Christian Hammond]) +AC_COPYRIGHT([Copyright 2004 Christian Hammond]) dnl ################################################################ dnl # Version information dnl ################################################################ LIBGALAGO_MAJOR_VERSION=0 -LIBGALAGO_MINOR_VERSION=2 -LIBGALAGO_MICRO_VERSION=2 +LIBGALAGO_MINOR_VERSION=3 +LIBGALAGO_MICRO_VERSION=0 LIBGALAGO_DEVEL_VERSION=0 LIBGALAGO_VERSION=$LIBGALAGO_MAJOR_VERSION.$LIBGALAGO_MINOR_VERSION.$LIBGALAGO_MICRO_VERSION @@ -89,7 +89,8 @@ AM_PROG_LIBTOOL AC_CHECK_LIB([popt], [poptGetArg], , AC_MSG_ERROR([Popt is required])) -pkg_modules="glib-2.0 >= 2.2.2, dbus-1 >= 0.23, dbus-glib-1 >= 0.23" +REQ_DBUS_VERSION=0.36 +pkg_modules="gtk+-2.0 >= 2.2.2 glib-2.0 >= 2.2.2, dbus-1 >= $REQ_DBUS_VERSION, dbus-glib-1 >= $REQ_DBUS_VERSION" PKG_CHECK_MODULES(PACKAGE, [$pkg_modules]) AC_SUBST(PACKAGE_CFLAGS) AC_SUBST(PACKAGE_LIBS) @@ -107,6 +108,9 @@ AM_CONDITIONAL(HAVE_GDK, test "x$have_gdk" = "xyes") AC_SUBST(GDK_CFLAGS) AC_SUBST(GDK_LIBS) +GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0` +AC_SUBST(GLIB_GENMARSHAL) + dnl dnl Check the D-BUS version. dnl diff --git a/docs/ChangeLog b/docs/ChangeLog index 6a96326..3027087 100644 --- a/docs/ChangeLog +++ b/docs/ChangeLog @@ -1,10 +1,3 @@ -Mon Jul 25 22:48:54 PDT 2005 Christian Hammond - - * notification-spec.xml: - - Bump to version 0.7. - - Mention the x, y hints. - - Talk about variants as the dictionary values for hints. - Fri Apr 01 09:11:38 PST 2005 Christian Hammond * notification-spec.xml: diff --git a/docs/notification-spec-0.8.xml b/docs/notification-spec-0.8.xml new file mode 100644 index 0000000..1e48191 --- /dev/null +++ b/docs/notification-spec-0.8.xml @@ -0,0 +1,1187 @@ + + +
+ + Desktop Notifications Specification + Version 0.7 + 28 July 2005 + + + Mike + Hearn + +
+ mike@navi.cx +
+
+
+ + Christian + Hammond + +
+ chipx86@chipx86.com +
+
+
+
+ + + 0.8 + 23 September 2005 + J5 + + Major overhaul of spec to work with the newer D-Bus recursive type system. + Simplify protocol. + Changed the verbage notification type to category + + + + 0.7 + 28 July 2005 + cdh + + Added "x" and "y" hints. Talk about the variant type for hint values. + + + + 0.6 + 1 April 2005 + cdh + + Updated to work with D-BUS 0.31+. + + + + 0.5 + 2 October 2004 + cdh + + Added a "suppress-sound" hint. Added a "sound" capability. Renamed the + "soundfile" hint to sound-file". + + + + 0.4 + 29 September 2004 + cdh + + Added image support in markup, and made the restrictions on markup more + clear. Removed the High urgency. Added new notification types. Fixed + notification expiration. + + + + 0.3 + 15 September 2004 + cdh + Added hint and notification type sections + + + 0.2 + foo + mh + Added replaces field to protocol + + + 0.1 + foo + mh + Initial version + + +
+ + + Introduction + + This is a draft standard for a desktop notifications service, through + which applications can generate passive popups (sometimes known as + "poptarts") to notify the user in an asynchronous manner of events. + + + This specification explicitly does not include other types of + notification presentation such as modal message boxes, window manager + decorations or window list annotations. + + + Example use cases include: + + + + + Presence changes in IM programs: for instance, MSN Messenger on + Windows pioneered the use of passive popups to indicate presence + changes. + + + Scheduled alarm + Completed file transfer + New mail notification + Low disk space/battery warnings + + + + + Basic Design + + In order to ensure that multiple notifications can easily be + displayed at once, and to provide a convenient implementation, all + notifications are controlled by a single session-scoped service which + exposes a D-BUS interface. + + + On startup, a conforming implementation should take the + org.freedesktop.Notifications service on + the session bus. This service will be referred to as the "notification + server" or just "the server" in this document. It can optionally be + activated automatically by the bus process, however this is not required + and notification server clients must not assume that it is available. + + + The server should implement the + org.freedesktop.Notifications interface on + an object with the path "/org/freedesktop/Notifications". + This is the only interface required by this version of the specification. + + + A notification has the following components: + + + Notification Components + + + + Component + Description + + + + + Application Name + + This is the optional name of the application sending the notification. + This should be the application's formal name, rather than some sort + of ID. An example would be "FredApp E-Mail Client," rather than + "fredapp-email-client." + + + + Replaces ID + + An optional ID of an existing notification that this + notification is intended to replace. + + + + Application Icon + + The application icon. This is represented either as a URI + (file:// is the only location supported right now) + or a name in an icon theme. + + + + Summary + + This is a single line overview of the notification. For instance, + "You have mail" or "A friend has come online". It should generally + not be longer than 40 characters, though this is not a requirement, + and server implementations should word wrap if necessary. The summary + must be encoded using UTF-8. + + + + Body + + + This is a multi-line body of text. Each line is a paragraph, server + implementations are free to word wrap them as they see fit. + + + The body may contain simple markup as specified in + . It must be encoded using UTF-8. + + + If the body is omitted, just the summary is displayed. + + + + + Actions + + The actions send a request message back to the notification client + when invoked. This functionality may not be implemented by the + notification server, conforming clients should check if it is available + before using it (see the GetCapabilities message in + ). An implementation is free to ignore any + requested by the client. As an example one possible rendering of + actions would be as buttons in the notification popup. + + Actions are sent over as a list of pairs. Each even element in the list + (starting at index 0) represents the identifier for the action. Each odd + element in the list is the localized string that will be displayed to the user. + + + + Hints + See . + + Beyond the core protocol is the hints table. A couple of core elements have + been moved to hints mostly because in a huge number of cases their default values + would be sufficent. The elements moved to hints are: + + * Category ID - An optional ID representing the type of notification (the name has + been changed from Notification Type ID in pervious versions). See . + + * Urgency Level - The urgency of the notification. See . + (Defaults to 1 - Normal) + + * Icon Data - Instead of overloading the icon field we now have an icon_data field that + is used when icon is blank. + + + Expiration Timeout + + + The timeout time in milliseconds since the display of the notification at + which the notification should automatically close. + + + If -1, the notification's expiration time is dependent on the + notification server's settings, and may vary for the type of + notification. + + If 0, the notification never expires. + + + + + +
+ + Each notification displayed is allocated a unique ID by the server. + This is unique within the session. While the notification server is + running, the ID will not be recycled unless the capacity of a uint32 is + exceeded. + + + This can be used to hide the notification before the expiration timeout + is reached. It can also be used to atomically replace the notification + with another. This allows you to (for instance) modify the contents of + a notification while it's on-screen. + +
+ + + Backwards Compatibility + + Clients should try and avoid making assumptions about the presentation and + abilities of the notification server. The message content is the most + important thing. + + + Clients can check with the server what capabilities are supported + using the GetCapabilities message. See + . + + + If a client requires a response from a passive popup, it should be + coded such that a non-focus-stealing message box can be used in the + case that the notification server does not support this feature. + + + + + Markup + + Body text may contain markup. The markup is XML-based, and consists + of a small subset of HTML along with a few additional tags. + + + The following tags should be supported by the notification server. + Though it is optional, it is recommended. Notification servers that do + not support these tags should filter them out. + + + + + + + b ... + b + + Bold + + + + i ... + i + + Italic + + + + u ... + u + + Underline + + + + a href="..." ... + a + + Hyperlink + + + + img src="..." alt="..." + + Image + + + + + + A full-blown HTML implementation is not required of this spec, and + notifications should never take advantage of tags that are not listed + above. As notifications are not a substitute for web browsers or complex + dialogs, advanced layout is not necessary, and may in fact limit the + number of systems that notification services can run on, due to memory + usage and screen space. Such examples are PDAs, certain cell phones, and + slow PCs or laptops with little memory. + + + For the same reason, a full XML or XHTML implementation using XSLT or + CSS stylesheets is not part of this specification. Information that + must be presented in a more complex form should use an application-specific + dialog, a web browser, or some other display mechanism. + + + The tags specified above mark up the content in a way that allows them + to be stripped out on some implementations without impacting the actual + content. + + + + Hyperlinks + + Hyperlinks allow for linking one or more words to a URI. There is no + requirement to allow for images to be linked, and it is highly suggested + that implementations do not allow this, as there is no clean-looking, + standard visual indicator for a hyperlinked image. + + + Hyperlinked text should appear in the standard blue underline format. + + + Hyperlinks cannot function as a replacement for actions. They are + used to link to local directories or remote sites using standard URI + schemes. + + + Implementations are not required to support hyperlinks. + + + + + Images + + Images may be placed in the notification, but this should be done with + caution. The image should never exceed 200x100, but this should be thought + of as a maximum size. Images should always have alternative text + provided through the alt="..." attribute. + + + Image data cannot be embedded in the message itself. Images referenced + must always be local files. + + + Implementations are not required to support images. + + + + + + Icons + + A notification can optionaly have an icon specified by the Application Icon + field or by the icon_data hints field. + + + The icon_data field should be a raw image data structure of signature (iiibiiay) + which describes the width, height, rowstride, has alpha, bits per sample, channels + and image data respectively. + + + + + Categories + + Notifications can optionally have a type indicator. Although neither + client or nor server must support this, some may choose to. Those servers + implementing categories may use them to intelligently display + the notification in a certain way, or group notifications of similar + types. + + + Categories are in + class.specific form. + class specifies the generic type of notification, and + specific specifies the more specific type of + notification. + + + If a specific type of notification does not exist for your notification, + but the generic kind does, a notification of type + class is acceptable. + + + Third parties, when defining their own categories, should discuss + the possibility of standardizing on the hint with other parties, preferably + in a place such as the + xdg + mailing list at + freedesktop.org. If it + warrants a standard, it will be added to the table above. If no + consensus is reached, the category should be in the form of + "x-vendor.class.name." + + + The following table lists standard notifications as defined by this spec. + More will be added in time. + + + Categories + + + + Type + Description + + + + + "device" + + A generic device-related notification that doesn't fit into + any other category. + + + + "device.added" + A device, such as a USB device, was added to the system. + + + "device.error" + A device had some kind of error. + + + "device.removed" + + A device, such as a USB device, was removed from the system. + + + + "email" + + A generic e-mail-related notification that doesn't fit into any + other category. + + + + "email.arrived" + A new e-mail notification. + + + "email.bounced" + A notification stating that an e-mail has bounced. + + + "im" + + A generic instant message-related notification that doesn't fit + into any other category. + + + + "im.error" + An instant message error notification. + + + "im.received" + A received instant message notification. + + + "network" + + A generic network notification that doesn't fit into any other + category. + + + + "network.connected" + + A network connection notification, such as successful sign-on to a + network service. This should not be confused with + device.added for new network devices. + + + + "network.disconnected" + + A network disconnected notification. This should not be confused with + device.removed for disconnected network devices. + + + + "network.error" + + A network-related or connection-related error. + + + + "presence" + + A generic presence change notification that doesn't fit into + any other category, such as going away or idle. + + + + "presence.offline" + An offline presence change notification. + + + "presence.online" + An online presence change notification. + + + "transfer" + + A generic file transfer or download notification that doesn't fit + into any other category. + + + + "transfer.complete" + A file transfer or download complete notification. + + + "transfer.error" + A file transfer or download error. + + + +
+
+ + + Urgency Levels + + Notifications have an urgency level associated with them. This defines + the importance of the notification. For example, "Joe Bob signed on" + would be a low urgency. "You have new mail" or "A USB device was unplugged" + would be a normal urgency. "Your computer is on fire" would be a critical + urgency. + + Urgency levels are defined as follows: + + Urgency Levels + + + + Type + Description + + + + + 0 + Low + + + 1 + Normal + + + 2 + Critical + + + +
+ + Developers must use their own judgement when deciding the urgency of a + notification. Typically, if the majority of programs are using the same + level for a specific type of urgency, other applications should follow + them. + + + For low and normal urgencies, server implementations may display the + notifications how they choose. They should, however, have a sane + expiration timeout dependent on the urgency level. + + + Critical notifications should not automatically expire, as they are + things that the user will most likely want to know about. They should + only be closed when the user dismisses them, for example, by clicking on + the notification. + +
+ + + Hints + + Hints are a way to provide extra data to a notification server that + the server may be able to make use of. + + + Neither clients nor notification servers are required to support any + hints. Both sides should assume that hints are not passed, and should + ignore any hints they do not understand. + + + Third parties, when defining their own hints, should discuss the + possibility of standardizing on the hint with other parties, preferably + in a place such as the + xdg + mailing list at + freedesktop.org. If it + warrants a standard, it will be added to the table above. If no + consensus is reached, the hint name should be in the form of + "x-vendor-name." + + + The value type for the hint dictionary in D-BUS is of the + DBUS_TYPE_VARIANT container type. This allows different + data types (string, integer, boolean, etc.) to be used for hints. When + adding a dictionary of hints, this type must be used, rather than putting + the actual hint value in as the dictionary value. + + + The following table lists the standard hints as defined by this + specification. Future hints may be proposed and added to this list + over time. Once again, implementations are not required to support these. + + + Standard Hints + + + + Name + Value Type + Description + + + + + "urgency" + byte + + The urgency level. + + + + "category" + string + + The type of notification this is. + + + + "image_data" + (iiibiiay) + + This is a raw data image format which describes the width, height, rowstride, + has alpha, bits per sample, channels and image data respectively. We use this + value if the icon field is left blank. + + + + "sound-file" + string + + The path to a sound file to play when the notification pops up. + + + + "suppress-sound" + boolean + + Causes the server to suppress playing any sounds, if it has that + ability. This is usually set when the client itself is going to + play its own sound. + + + + "x" + int + + Specifies the X location on the screen that the notification should + point to. The "y" hint must also be specified. + + + + "y" + int + + Specifies the Y location on the screen that the notification should + point to. The "x" hint must also be specified. + + + + +
+
+ + + D-BUS Protocol + + The following messages must be supported by all + implementations. + + + + Message commands + + + <literal>org.freedesktop.Notifications.GetCapabilities</literal> + + + STRING_ARRAY + org.freedesktop.Notifications.GetCapabilities + + + + + + This message takes no parameters. + + + It returns an array of strings. Each string describes an optional + capability implemented by the server. The following values are + defined by this spec: + + + Server Capabilities + + + + "actions" + + The server will provide the specified actions to the user. Even if + this cap is missing, actions may still be specified by the client, + however the server is free to ignore them. + + + + "body" + + Supports body text. Some implementations may only show the + summary (for instance, onscreen displays, marquee/scrollers) + + + + "body-hyperlinks" + + The server supports hyperlinks in the notifications. + + + + "body-images" + + The server supports images in the notifications. + + + + "body-markup" + + Supports markup in the body text. If marked up text is sent + to a server that does not give this cap, the markup will show + through as regular text so must be stripped clientside. + + + + "icon-multi" + + The server will render an animation of all the frames in a given + image array. The client may still specify multiple frames even if + this cap and/or "icon-static" is missing, however + the server is free to ignore them and use only the primary frame. + + + + "icon-static" + + Supports display of exactly 1 frame of any given image array. + This value is mutually exclusive with + "icon-multi", it is a protocol error for the + server to specify both. + + + + "sound" + + The server supports sounds on notifications. If returned, the + server must support the "sound-file" and + "suppress-sound" hints. + + + + +
+ + New vendor-specific caps may be specified as long as they start with + "x-vendor". For instance, + "x-gnome-foo-cap". Capability names must not + contain spaces. They are limited to alpha-numeric characters and dashes + ("-"). + +
+ + + <literal>org.freedesktop.Notifications.Notify</literal> + + + UINT32 + org.freedesktop.Notifications.Notify + + STRING app_name + UINT32 replaces_id + STRING app_icon + STRING summary + STRING body + ARRAY actions + DICT hints + INT32 expire_timeout + + + + Sends a notification to the notification server. + + + Notify Parameters + + + + Name + Type + Description + + + + + app_name + STRING + + The optional name of the application sending the notification. + Can be blank. + + + + replaces_id + UINT32 + + The optional notification ID that this notification replaces. The + server must atomically (ie with no flicker or other visual cues) + replace the given notification with this one. This allows clients to + effectively modify the notification while it's active. A value of + value of 0 means that this notification won't replace any + existing notifications. + + + + app_icon + STRING + + The optional program icon of the calling application. See . + Can be an empty string, indicating no icon. + + + + summary + STRING + The summary text briefly describing the notification. + + + body + STRING + The optional detailed body text. Can be empty. + + + actions + ARRAY + + Actions are sent over as a list of pairs. Each even element in the list + (starting at index 0) represents the identifier for the action. Each odd + element in the list is the localized string that will be displayed to the user. + + + + hints + DICT + + Optional hints that can be passed to the server from the client + program. Although clients and servers should never assume each other + supports any specific hints, they can be used to pass along + information, such as the process PID or window ID, that the server + may be able to make use of. See . Can be + empty. + + + + expire_timeout + INT32 + + + The timeout time in milliseconds since the display of the notification at + which the notification should automatically close. + + + If -1, the notification's expiration time is dependent on the + notification server's settings, and may vary for the type of + notification. + + If 0, never expire. + + + + + +
+ + If replaces_id is 0, the return value is a + UINT32 that represent the notification. It is unique, and will not be + reused unless a MAXINT number of notifications + have been generated. An acceptable implementation may just use an + incrementing counter for the ID. The returned ID is always greater than + zero. Servers must make sure not to return zero as an ID. + + + If replaces_id is not 0, the returned value + is the same value as replaces_id. + +
+ + + <literal>org.freedesktop.Notifications.CloseNotification</literal> + + + void + org.freedesktop.Notifications.CloseNotification + + UINT32 id + + + + Causes a notification to be forcefully closed and removed from the user's + view. It can be used, for example, in the event that what the + notification pertains to is no longer relevant, or to cancel a + notification with no expiration time. + + + The NotificationClosed signal is emitted by this + method. + + + If the notification no longer exists, an empty D-BUS Error message is + sent back. + + + + + <literal>org.freedesktop.Notifications.GetServerInformation</literal> + + + + void + org.freedesktop.Notifications.GetServerInformation + + out STRING name + out STRING vendor + out STRING version + + + + This message returns the information on the server. Specifically, + the server name, vendor, and version number. + + + GetServerInformation Return Values + + + + Name + Type + Description + + + + + name + STRING + The product name of the server. + + + vendor + STRING + + The vendor name. For example, "KDE," "GNOME," + "freedesktop.org," or "Microsoft." + + + + version + STRING + The server's version number. + + + +
+
+
+ + + Signals + + + <literal>org.freedesktop.Notifications.NotificationClosed</literal> + + + + org.freedesktop.Notifications.NotificationClosed + + UINT32 id + UINT32 reason + + + + A completed notification is one that has timed out, or has been + dismissed by the user. + + + NotificationClosed Parameters + + + + Name + Type + Description + + + + + id + UINT32 + The ID of the notification that was closed. + + + reason + UINT32 + + The reason the notification was closed. + 1 - The notification expired. + 2 - The notification was dismissed by the user. + 3 - The notification was closed by a call to + CloseNotification. + 4 - Undefined/reserved reasons. + + + + +
+ + The ID specified in the signal is invalidated + before the signal is sent and may not be used + in any further communications with the server. + +
+ + + <literal>org.freedesktop.Notifications.ActionInvoked</literal> + + + + org.freedesktop.Notifications.ActionInvoked + + UINT32 id + + UINT32 action_id + + + + This signal is emitted when one of the following occurs: + + + + + The user performs some global "invoking" action upon a notification. + For instance, clicking somewhere on the notification itself. + + + + + The user invokes a specific action as specified in the original + Notify request. For example, clicking on an action button. + + + + + ActionInvoked Parameters + + + + Name + Type + Description + + + + + id + UINT32 + + The ID of the notification emitting the ActionInvoked signal. + + + + + action_id + UINT32 + + The ID of the action invoked. A value of 0 means that the default + action was invoked, i.e., clicking the notification itself. + IDs greater than zero are the action IDs as defined by the + calling application. + + + + + +
+ + + Clients should not assume the server will generate this signal. Some + servers may not support user interaction at all, or may not support + the concept of being able to "invoke" a notification. + + +
+
+
+
diff --git a/docs/notification-spec.xml b/docs/notification-spec.xml index ab92488..484a212 100644 --- a/docs/notification-spec.xml +++ b/docs/notification-spec.xml @@ -6,8 +6,8 @@
Desktop Notifications Specification - Version 0.7 - 28 July 2005 + Version 0.5 + 2 October 2004 Mike @@ -29,14 +29,6 @@ - - 0.7 - 28 July 2005 - cdh - - Added "x" and "y" hints. Talk about the variant type for hint values. - - 0.6 1 April 2005 @@ -702,13 +694,6 @@ consensus is reached, the hint name should be in the form of "x-vendor-name." - - The value type for the hint dictionary in D-BUS is of the - DBUS_TYPE_VARIANT container type. This allows different - data types (string, integer, boolean, etc.) to be used for hints. When - adding a dictionary of hints, this type must be used, rather than putting - the actual hint value in as the dictionary value. - The following table lists the standard hints as defined by this specification. Future hints may be proposed and added to this list @@ -741,22 +726,6 @@ play its own sound. - - "x" - int - - Specifies the X location on the screen that the notification should - point to. The "y" hint must also be specified. - - - - "y" - int - - Specifies the Y location on the screen that the notification should - point to. The "x" hint must also be specified. - - diff --git a/libnotify.pc.in b/libnotify.pc.in index 4da0bab..198d324 100644 --- a/libnotify.pc.in +++ b/libnotify.pc.in @@ -1,7 +1,7 @@ prefix=@prefix@ -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ Name: libnotify Description: Notifications Library diff --git a/libnotify.spec.in b/libnotify.spec.in index 154020b..e69de29 100644 --- a/libnotify.spec.in +++ b/libnotify.spec.in @@ -1,53 +0,0 @@ -Summary: Desktop notifications library -Name: libnotify -Version: @VERSION@ -Release: 1%{?dist} -License: GPL -Group: Applications/System -Source: http://www.galago.info/files/releases/source/libnotify-%{version}.tar.gz -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) -URL: http://svn.galago.info/trunk/libnotify -BuildPrereq: dbus-devel >= 0.30 -Requires: dbus >= 0.30 - -%description -A library that sends desktop notifications to a notification daemon, as -defined in the Desktop Notifications spec. These notifications can be -used to inform the user about an event or display some form of -information without getting in the user's way. - -%prep -%setup -q - -%build -%configure -make - -%install -rm -rf $RPM_BUILD_ROOT -%makeinstall - -%clean -rm -rf $RPM_BUILD_ROOT - -%post -/sbin/ldconfig - -%postun -/sbin/ldconfig - -%files -%defattr(-,root,root) -%doc AUTHORS ChangeLog COPYING README -%{_bindir}/* -%{_libdir}/lib*.a -%{_libdir}/lib*.so -%{_libdir}/lib*.la -%{_libdir}/lib*.so.* -%{_libdir}/pkgconfig/* -%{_includedir}/* - -%changelog -* Mon Jul 12 2005 Richard Hughes 0.0.1-1 -- initial packaging of 0.0.1 - diff --git a/libnotify/Makefile.am b/libnotify/Makefile.am index d552c07..0f4f2a2 100644 --- a/libnotify/Makefile.am +++ b/libnotify/Makefile.am @@ -1,13 +1,23 @@ notifyincdir = $(includedir)/libnotify +MARSHAL_PREFIX = notify_marshal +MARSHAL_FILE = notify-marshal + lib_LTLIBRARIES = libnotify.la -notifyinc_HEADERS = \ - notify.h +notifyinc_HEADERS = \ + notify.h \ + notifynotification.h \ + notifycommon.h -libnotify_la_SOURCES = \ - dbus-compat.h \ - notify.c +noinst_HEADERS = \ + notify-marshal.h + +libnotify_la_SOURCES = \ + dbus-compat.h \ + notify.c \ + notifynotification.c \ + notify-marshal.c libnotify_la_LIBADD = \ $(PACKAGE_LIBS) @@ -15,5 +25,24 @@ libnotify_la_LIBADD = \ libnotify_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +BUILT_SOURCES = \ + notify-marshal.c \ + notify-marshal.h + +notify-marshal.h: notify-marshal.list + $(GLIB_GENMARSHAL) --prefix=$(MARSHAL_PREFIX) notify-marshal.list \ + --header > notify-marshal.h + +notify-marshal.c: notify-marshal.list + $(GLIB_GENMARSHAL) --prefix=$(MARSHAL_PREFIX) notify-marshal.list \ + --body > notify-marshal.c + +EXTRA_DIST = \ + notify-marshal.list + +CLEANFILES = \ + notify-marshal.c \ + notify-marshal.h + INCLUDES = \ $(PACKAGE_CFLAGS) diff --git a/libnotify/dbus-compat.h b/libnotify/dbus-compat.h index 560b262..2a2d198 100644 --- a/libnotify/dbus-compat.h +++ b/libnotify/dbus-compat.h @@ -40,59 +40,20 @@ dbus_message_iter_append_basic((iter), DBUS_TYPE_DOUBLE, &(val)) # define _notify_dbus_message_iter_append_byte_array(iter, data, len) \ - { \ - DBusMessageIter array_iter; \ - dbus_message_iter_open_container((iter), DBUS_TYPE_ARRAY, \ - DBUS_TYPE_BYTE_AS_STRING, \ - &array_iter); \ - dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, \ - &(data), (len)); \ - dbus_message_iter_close_container((iter), &array_iter); \ - } + dbus_message_iter_append_fixed_array((iter), DBUS_TYPE_BYTE, &(data), \ + (len)) # define _notify_dbus_message_iter_append_boolean_array(iter, data, len) \ - { \ - DBusMessageIter array_iter; \ - dbus_message_iter_open_container((iter), DBUS_TYPE_ARRAY, \ - DBUS_TYPE_BOOLEAN_AS_STRING, \ - &array_iter); \ - dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BOOLEAN, \ - &(data), (len)); \ - dbus_message_iter_close_container((iter), &array_iter); \ - } + dbus_message_iter_append_fixed_array((iter), DBUS_TYPE_BOOLEAN, &(data), \ + (len)) # define _notify_dbus_message_iter_append_int32_array(iter, data, len) \ - { \ - DBusMessageIter array_iter; \ - dbus_message_iter_open_container((iter), DBUS_TYPE_ARRAY, \ - DBUS_TYPE_INT32_AS_STRING, \ - &array_iter); \ - dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_INT32, \ - &(data), (len)); \ - dbus_message_iter_close_container((iter), &array_iter); \ - } + dbus_message_iter_append_fixed_array((iter), DBUS_TYPE_INT32, &(data), \ + (len)) # define _notify_dbus_message_iter_append_uint32_array(iter, data, len) \ - { \ - DBusMessageIter array_iter; \ - dbus_message_iter_open_container((iter), DBUS_TYPE_ARRAY, \ - DBUS_TYPE_UINT32_AS_STRING, \ - &array_iter); \ - dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_UINT32, \ - &(data), (len)); \ - dbus_message_iter_close_container((iter), &array_iter); \ - } + dbus_message_iter_append_fixed_array((iter), DBUS_TYPE_UINT32, &(data), \ + (len)) # define _notify_dbus_message_iter_append_string_array(iter, data, len) \ - { \ - DBusMessageIter array_iter; \ - int i; \ - dbus_message_iter_open_container((iter), DBUS_TYPE_ARRAY, \ - DBUS_TYPE_STRING_AS_STRING, \ - &array_iter); \ - for (i = 0; i < len; i++) \ - { \ - dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, \ - &((char **)data)[i]); \ - } \ - dbus_message_iter_close_container((iter), &array_iter); \ - } + dbus_message_iter_append_fixed_array((iter), DBUS_TYPE_STRING, \ + &(data), (len)) # define _notify_dbus_message_iter_get_byte(iter, retvar) \ dbus_message_iter_get_basic((iter), &(retvar)) @@ -107,21 +68,16 @@ # define _notify_dbus_message_iter_get_double(iter, retvar) \ dbus_message_iter_get_basic((iter), &(retvar)) -# define _notify_dbus_message_iter_get_fixed_array(iter, data, len) \ - { \ - DBusMessageIter array_iter; \ - dbus_message_iter_recurse((iter), &array_iter); \ - dbus_message_iter_get_fixed_array(&array_iter, (data), (len)); \ - } - -# define _notify_dbus_message_iter_get_byte_array(iter, data, len) \ - _notify_dbus_message_iter_get_fixed_array((iter), (data), (len)) -# define _notify_dbus_message_iter_get_boolean_array(iter, data, len) \ - _notify_dbus_message_iter_get_fixed_array((iter), (data), (len)) -# define _notify_dbus_message_iter_get_int32_array(iter, data, len) \ - _notify_dbus_message_iter_get_fixed_array((iter), (data), (len)) -# define _notify_dbus_message_iter_get_uint32_array(iter, data, len) \ - _notify_dbus_message_iter_get_fixed_array((iter), (data), (len)) +# define _notify_dbus_message_iter_get_byte_array \ + dbus_message_iter_get_fixed_array +# define _notify_dbus_message_iter_get_boolean_array \ + dbus_message_iter_get_fixed_array +# define _notify_dbus_message_iter_get_int32_array \ + dbus_message_iter_get_fixed_array +# define _notify_dbus_message_iter_get_uint32_array \ + dbus_message_iter_get_fixed_array +# define _notify_dbus_message_iter_get_string_array(iter, array, num_items) \ + dbus_message_iter_get_fixed_array((iter), (array), (num_items)) #else /* D-BUS < 0.30 */ # define DBUS_INTERFACE_DBUS DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS @@ -129,14 +85,13 @@ # define DBUS_PATH_DBUS DBUS_PATH_ORG_FREEDESKTOP_DBUS # define DBUS_ERROR_SERVICE_UNKNOWN DBUS_ERROR_SERVICE_DOES_NOT_EXIST -# define dbus_message_iter_init_append(msg, iter) \ - dbus_message_iter_init(msg, iter) - -# define dbus_bus_start_service_by_name(conn, service, flags, result, error) \ - dbus_bus_activate_service((conn), (service), (flags), (result), (error)) +# define dbus_bus_start_service_by_name dbus_bus_activate_service # define dbus_message_iter_close_container(iter, container_iter) +# define dbus_message_iter_init_append(msg, iter) \ + dbus_message_iter_init(msg, iter) + # define _notify_dbus_message_iter_append_byte(iter, val) \ dbus_message_iter_append_byte((iter), (val)) # define _notify_dbus_message_iter_append_boolean(iter, val) \ @@ -182,6 +137,8 @@ dbus_message_iter_get_int32_array((iter), (data), (len)) # define _notify_dbus_message_iter_get_uint32_array(iter, data, len) \ dbus_message_iter_get_uint32_array((iter), (data), (len)) +# define _notify_dbus_message_iter_get_string_array(iter, data, len) \ + dbus_message_iter_get_string_array((iter), (data), (len)) #endif #endif /* NOTIFY_DBUS_COMPAT_H_ */ diff --git a/libnotify/notify-marshal.list b/libnotify/notify-marshal.list new file mode 100644 index 0000000..5ab45bf --- /dev/null +++ b/libnotify/notify-marshal.list @@ -0,0 +1 @@ +VOID:UINT,STRING diff --git a/libnotify/notify.c b/libnotify/notify.c index 3eb4682..5efc785 100644 --- a/libnotify/notify.c +++ b/libnotify/notify.c @@ -33,1060 +33,78 @@ #include "dbus-compat.h" #include #include -#include -#include #include #include #include #include -#include -#define NOTIFY_DBUS_SERVICE "org.freedesktop.Notifications" -#define NOTIFY_DBUS_CORE_INTERFACE "org.freedesktop.Notifications" -#define NOTIFY_DBUS_CORE_OBJECT "/org/freedesktop/Notifications" - -struct _NotifyHandle -{ - guint32 id; - - guint32 replaces; - gboolean expires; - - gpointer user_data; - - guint32 action_count; - GHashTable *actions_table; -}; - -struct _NotifyIcon -{ - size_t frames; - - char **uri; - - size_t *raw_len; - guchar **raw_data; -}; - -struct _NotifyHints -{ - GHashTable *data; -}; - -typedef struct -{ - guint32 id; - char *text; - NotifyCallback cb; - -} NotifyAction; - -static DBusConnection *_dbus_conn = NULL; static gboolean _initted = FALSE; -static gboolean _filters_added = FALSE; -static guint32 _init_ref_count = 0; -static char *_app_name = NULL; -static GHashTable *_handles = NULL; - +static gchar *_app_name = NULL; #ifdef __GNUC__ # define format_func __attribute__((format(printf, 1, 2))) #else /* no format string checking with this compiler */ # define format_func #endif +#if 0 static void format_func -print_error(char *message, ...) +print_error (char *message, ...) { - char buf[1024]; - va_list args; + char buf[1024]; + va_list args; - va_start(args, message); - vsnprintf(buf, sizeof(buf), message, args); - va_end(args); + va_start (args, message); + vsnprintf (buf, sizeof (buf), message, args); + va_end (args); - fprintf(stderr, "%s(%d): libnotify: %s", - (getenv("_") ? getenv("_") : ""), getpid(), buf); + fprintf (stderr, "%s(%d): libnotify: %s", + (getenv ("_") ? getenv ("_") : ""), getpid (), buf); } - -static NotifyHandle * -_notify_handle_new(guint32 id) -{ - NotifyHandle *handle; - - handle = g_new0(NotifyHandle, 1); - - handle->id = id; - - handle->replaces = 0; - - g_hash_table_insert(_handles, &handle->id, handle); - - return handle; -} - -static void -_notify_handle_destroy(NotifyHandle *handle) -{ - g_return_if_fail(handle != NULL); - - if (handle->actions_table != NULL) - g_hash_table_destroy(handle->actions_table); - - g_free(handle); -} - -static void -_notify_action_destroy(NotifyAction *action) -{ - g_return_if_fail(action != NULL); - - if (action->text != NULL) - g_free(action->text); - - g_free(action); -} - -static DBusMessage * -_notify_dbus_message_new(const char *name, DBusMessageIter *iter) -{ - DBusMessage *message; - - g_return_val_if_fail(name != NULL, NULL); - g_return_val_if_fail(*name != '\0', NULL); - - message = dbus_message_new_method_call(NOTIFY_DBUS_SERVICE, - NOTIFY_DBUS_CORE_OBJECT, - NOTIFY_DBUS_CORE_INTERFACE, - name); - - g_return_val_if_fail(message != NULL, NULL); - - if (iter != NULL) - dbus_message_iter_init_append(message, iter); - - return message; -} - -static void -_notify_dbus_message_iter_append_string_or_empty(DBusMessageIter *iter, - const char *str) -{ - g_return_if_fail(iter != NULL); - - if (str == NULL) - str = ""; - - _notify_dbus_message_iter_append_string(iter, str); -} - -static DBusHandlerResult -_filter_func(DBusConnection *dbus_conn, DBusMessage *message, void *user_data) -{ - DBusMessageIter iter; - - if (dbus_message_is_signal(message, NOTIFY_DBUS_CORE_INTERFACE, - "NotificationClosed")) - { - guint32 id, reason; - - dbus_message_iter_init(message, &iter); - - _notify_dbus_message_iter_get_uint32(&iter, id); - dbus_message_iter_next(&iter); - - _notify_dbus_message_iter_get_uint32(&iter, reason); - - g_hash_table_remove(_handles, &id); - } - else if (dbus_message_is_signal(message, NOTIFY_DBUS_CORE_INTERFACE, - "ActionInvoked")) - { - guint32 id, action_id; - NotifyHandle *handle; - - dbus_message_iter_init(message, &iter); - - _notify_dbus_message_iter_get_uint32(&iter, id); - dbus_message_iter_next(&iter); - - _notify_dbus_message_iter_get_uint32(&iter, action_id); - - handle = g_hash_table_lookup(_handles, &id); - - if (handle == NULL) - goto exit; - - if (handle->actions_table == NULL) - { - print_error("An action (%d) was invoked for a notification (%d) " - "with no actions listed!\n", - action_id, id); - } - else - { - NotifyAction *action; - - action = g_hash_table_lookup(handle->actions_table, &action_id); - - if (action == NULL) - { - print_error("An invalid action (%d) was invoked for " - "notification %d\n", action_id, id); - } - else if (action->cb != NULL) - { - action->cb(handle, action_id, handle->user_data); - } - } - } - else - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - -exit: - return DBUS_HANDLER_RESULT_HANDLED; -} - -static gboolean -_notify_connect(void) -{ - DBusError error; - - dbus_error_init(&error); - - _dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &error); - - if (_dbus_conn == NULL) - { - print_error("Error connecting to session bus: %s\n", error.message); - - dbus_error_free(&error); - - return FALSE; - } - - dbus_connection_set_exit_on_disconnect(_dbus_conn, FALSE); - - if (!dbus_bus_start_service_by_name(_dbus_conn, NOTIFY_DBUS_SERVICE, - 0, NULL, &error)) - { - print_error("Error activating %s service: %s\n", - NOTIFY_DBUS_SERVICE, error.message); - - dbus_error_free(&error); - - return FALSE; - } - - if (!dbus_connection_add_filter(_dbus_conn, _filter_func, NULL, NULL)) - { - print_error("Error creating D-BUS message filter.\n"); - - dbus_error_free(&error); - - return FALSE; - } - - dbus_bus_add_match(_dbus_conn, - "type=signal," - "interface=" DBUS_INTERFACE_DBUS "," - "sender=" DBUS_SERVICE_DBUS , - &error); - - dbus_bus_add_match(_dbus_conn, - "type=signal," - "interface=" NOTIFY_DBUS_CORE_INTERFACE "," - "path=" NOTIFY_DBUS_CORE_OBJECT , - &error); - - if (dbus_error_is_set(&error)) - { - print_error("Error subscribing to signals: %s\n", error.message); - - dbus_error_free(&error); - - return FALSE; - } - - _filters_added = TRUE; - - dbus_error_free(&error); - - return TRUE; -} - -static void -_notify_disconnect(void) -{ - if (_dbus_conn == NULL) - return; - - if (_filters_added) - { - dbus_connection_remove_filter(_dbus_conn, _filter_func, NULL); - - _filters_added = FALSE; - } - - dbus_connection_disconnect(_dbus_conn); - dbus_connection_dispatch(_dbus_conn); - dbus_connection_unref(_dbus_conn); - - _dbus_conn = NULL; -} - -static void -sig_handler(int sig) -{ - static volatile sig_atomic_t in_progress = 0; - - if (in_progress) - raise(sig); - - in_progress = 1; - - notify_uninit(); - - signal(sig, SIG_DFL); - raise(sig); -} - -gboolean -notify_init(const char *app_name) -{ - g_return_val_if_fail(app_name != NULL, FALSE); - g_return_val_if_fail(*app_name != '\0', FALSE); - - _init_ref_count++; - - if (_initted) - return TRUE; - - if (!_notify_connect()) - { - _notify_disconnect(); - - return FALSE; - } - - _app_name = g_strdup(app_name); - - _handles = g_hash_table_new_full(g_int_hash, g_int_equal, - NULL, (GFreeFunc)_notify_handle_destroy); - - g_atexit(notify_uninit); - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); - - _initted = TRUE; - - return TRUE; -} - -gboolean -notify_glib_init(const char *app_name, GMainContext *context) -{ - if (!notify_init(app_name)) - return FALSE; - - notify_setup_with_g_main(context); - - return TRUE; -} - -static void -foreach_handle(int *id, NotifyHandle *handle, gpointer user_data) -{ - if (!handle->expires) - notify_close(handle); -} - -void -notify_uninit(void) -{ - _init_ref_count--; - - if (_init_ref_count != 0) - return; - - if (_app_name != NULL) - { - g_free(_app_name); - _app_name = NULL; - } - - if (_handles != NULL) - { - g_hash_table_foreach(_handles, (GHFunc)foreach_handle, NULL); - g_hash_table_destroy(_handles); - _handles = NULL; - } - - _notify_disconnect(); -} - -gboolean -notify_is_initted(void) -{ - return _initted; -} - -void -notify_setup_with_g_main(GMainContext *context) -{ - dbus_connection_setup_with_g_main(_dbus_conn, context); -} - -void -notify_close(NotifyHandle *handle) -{ - DBusMessage *message; - DBusMessageIter iter; - - g_return_if_fail(handle != NULL); - - /* Don't close other applications' notifications! */ - g_return_if_fail(g_hash_table_lookup(_handles, &handle->id) != NULL); - - message = _notify_dbus_message_new("CloseNotification", &iter); - - g_return_if_fail(message != NULL); - - _notify_dbus_message_iter_append_uint32(&iter, handle->id); - - dbus_connection_send_with_reply_and_block(_dbus_conn, message, -1, NULL); - dbus_message_unref(message); -} - -gboolean -notify_get_server_info(char **ret_name, char **ret_vendor, char **ret_version) -{ - DBusMessage *message, *reply; - DBusMessageIter iter; - DBusError error; - char *name, *vendor, *version; - - message = _notify_dbus_message_new("GetServerInformation", NULL); - - g_return_val_if_fail(message != NULL, FALSE); - - dbus_error_init(&error); - - reply = dbus_connection_send_with_reply_and_block(_dbus_conn, message, - -1, &error); - - dbus_message_unref(message); - - if (dbus_error_is_set(&error)) - { - print_error("Error sending GetServerInformation: %s\n", error.message); - - dbus_error_free(&error); - - return FALSE; - } - - dbus_error_free(&error); - - dbus_message_iter_init(reply, &iter); - - _notify_dbus_message_iter_get_string(&iter, name); - dbus_message_iter_next(&iter); - - _notify_dbus_message_iter_get_string(&iter, vendor); - dbus_message_iter_next(&iter); - - _notify_dbus_message_iter_get_string(&iter, version); - dbus_message_iter_next(&iter); - - dbus_message_unref(reply); - - if (ret_name != NULL) - *ret_name = g_strdup(name); - - if (ret_vendor != NULL) - *ret_vendor = g_strdup(vendor); - - if (ret_version != NULL) - *ret_version = g_strdup(version); - -#if !NOTIFY_CHECK_DBUS_VERSION(0, 30) - dbus_free(name); - dbus_free(vendor); - dbus_free(version); #endif - return TRUE; -} - -GList * -notify_get_server_caps(void) -{ - DBusMessage *message, *reply; - DBusMessageIter iter; - DBusError error; - GList *caps = NULL; -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - DBusMessageIter array_iter; -#else - char **temp_array; - int num_items, i; -#endif - - message = _notify_dbus_message_new("GetCapabilities", NULL); - - g_return_val_if_fail(message != NULL, FALSE); - - dbus_error_init(&error); - - reply = dbus_connection_send_with_reply_and_block(_dbus_conn, message, - -1, &error); - - dbus_message_unref(message); - - if (dbus_error_is_set(&error)) - { - print_error("Error sending GetCapabilities: %s\n", error.message); - - dbus_error_free(&error); - - return FALSE; - } - - dbus_error_free(&error); - - dbus_message_iter_init(reply, &iter); - -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - dbus_message_iter_recurse(&iter, &array_iter); - - while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRING) - { - const char *value; - - dbus_message_iter_get_basic(&array_iter, &value); - - caps = g_list_append(caps, g_strdup(value)); - - dbus_message_iter_next(&array_iter); - } -#else /* D-BUS < 0.30 */ - dbus_message_iter_get_string_array(&iter, &temp_array, &num_items); - - for (i = 0; i < num_items; i++) - caps = g_list_append(caps, g_strdup(temp_array[i])); - - dbus_free_string_array(temp_array); -#endif /* D-BUS < 0.30 */ - - dbus_message_unref(reply); - - return caps; -} - - -/************************************************************************** - * Notify Hints API - **************************************************************************/ -typedef enum -{ - HINT_TYPE_STRING, - HINT_TYPE_INT, - HINT_TYPE_BOOL - -} NotifyHintType; - -typedef struct -{ - NotifyHintType type; - - union - { - char *string; - int integer; - gboolean boolean; - } u; - -} NotifyHintData; - -static void -destroy_hint(NotifyHintData *hint_data) -{ - if (hint_data->type == HINT_TYPE_STRING) - g_free(hint_data->u.string); - - g_free(hint_data); -} - -NotifyHints * -notify_hints_new(void) -{ - NotifyHints *hints = g_new0(NotifyHints, 1); - - hints->data = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GFreeFunc)destroy_hint); - - return hints; -} - -void -notify_hints_destroy(NotifyHints *hints) -{ - g_return_if_fail(hints != NULL); - - g_hash_table_destroy(hints->data); - g_free(hints); -} - -void -notify_hints_set_string(NotifyHints *hints, const char *key, - const char *value) -{ - NotifyHintData *hint_data; - - g_return_if_fail(hints != NULL); - g_return_if_fail(key != NULL && *key != '\0'); - g_return_if_fail(value != NULL && *value != '\0'); - - hint_data = g_new0(NotifyHintData, 1); - hint_data->type = HINT_TYPE_STRING; - hint_data->u.string = g_strdup(value); - - g_hash_table_replace(hints->data, g_strdup(key), hint_data); -} - -void -notify_hints_set_int(NotifyHints *hints, const char *key, int value) -{ - NotifyHintData *hint_data; - - g_return_if_fail(hints != NULL); - g_return_if_fail(key != NULL && *key != '\0'); - - hint_data = g_new0(NotifyHintData, 1); - hint_data->type = HINT_TYPE_INT; - hint_data->u.integer = value; - - g_hash_table_replace(hints->data, g_strdup(key), hint_data); -} - -void -notify_hints_set_bool(NotifyHints *hints, const char *key, gboolean value) -{ - NotifyHintData *hint_data; - - g_return_if_fail(hints != NULL); - g_return_if_fail(key != NULL && *key != '\0'); - - hint_data = g_new0(NotifyHintData, 1); - hint_data->type = HINT_TYPE_BOOL; - hint_data->u.boolean = value; - - g_hash_table_replace(hints->data, g_strdup(key), hint_data); -} - - -/************************************************************************** - * Icon API - **************************************************************************/ - -NotifyIcon * -notify_icon_new() -{ - NotifyIcon *icon; - - icon = g_new0(NotifyIcon, 1); - - return icon; -} - -NotifyIcon * -notify_icon_new_from_uri(const char *icon_uri) -{ - NotifyIcon *icon; - - g_return_val_if_fail(icon_uri != NULL, NULL); - g_return_val_if_fail(*icon_uri != '\0', NULL); - - icon = g_new0(NotifyIcon, 1); - - icon->frames = 1; - icon->uri = g_malloc(sizeof(char *)); - icon->uri[0] = g_strdup(icon_uri); - - return icon; -} - -NotifyIcon * -notify_icon_new_from_data(size_t icon_len, const guchar *icon_data) -{ - NotifyIcon *icon; - - g_return_val_if_fail(icon_len > 0, NULL); - g_return_val_if_fail(icon_data != NULL, NULL); - - icon = g_new0(NotifyIcon, 1); - - icon->frames = 1; - icon->raw_len = g_malloc(sizeof(icon->raw_len)); - icon->raw_len[0] = icon_len; - icon->raw_data = g_malloc(sizeof(guchar *)); - icon->raw_data[0] = g_memdup(icon_data, icon_len); - - return icon; -} - gboolean -notify_icon_add_frame_from_data(NotifyIcon *icon, size_t icon_len, const guchar *icon_data) +notify_init (const char *app_name) { - g_return_val_if_fail(icon != NULL, FALSE); - g_return_val_if_fail(icon_data != NULL, FALSE); - g_return_val_if_fail(icon_len != 0, FALSE); + g_return_val_if_fail (app_name != NULL, FALSE); + g_return_val_if_fail (*app_name != '\0', FALSE); - /* check for integer overflow */ - g_return_val_if_fail(icon->frames + 1 < INT_MAX, FALSE); + if (_initted) + return TRUE; - if (icon->frames) g_return_val_if_fail(icon->raw_len != NULL, FALSE); + _app_name = g_strdup (app_name); - icon->frames++; - icon->raw_len = g_realloc(icon->raw_len, sizeof(size_t) * icon->frames); - icon->raw_len[icon->frames - 1] = icon_len; - icon->raw_data = g_realloc(icon->raw_data, sizeof(guchar *) * icon->frames); - icon->raw_data[icon->frames - 1] = g_memdup(icon_data, icon_len); + g_type_init (); - return TRUE; +#ifdef HAVE_ATEXIT + atexit (notify_uninit); +#endif /* HAVE_ATEXIT */ + + _initted = TRUE; + + return TRUE; } -gboolean -notify_icon_add_frame_from_uri(NotifyIcon *icon, const char *uri) +const gchar * +_notify_get_app_name (void) { - g_return_val_if_fail(icon != NULL, FALSE); - g_return_val_if_fail(uri != NULL, FALSE); - - if (icon->frames) - { - g_return_val_if_fail(icon->uri != NULL, FALSE); - } - - /* check for integer overflow */ - g_return_val_if_fail(icon->frames + 1 < INT_MAX, FALSE); - - icon->frames++; - icon->uri = g_realloc(icon->uri, sizeof(char *) * icon->frames); - icon->uri[icon->frames - 1] = g_strdup(uri); - - return TRUE; + return _app_name; } void -notify_icon_destroy(NotifyIcon *icon) +notify_uninit (void) { - g_return_if_fail(icon != NULL); - if (icon->uri != NULL) - g_free(icon->uri); - - if (icon->raw_data != NULL) - g_free(icon->raw_data); - - g_free(icon); -} - -/************************************************************************** - * Notifications API - **************************************************************************/ -NotifyHandle * -notify_send_notification(NotifyHandle *replaces, const char *type, - NotifyUrgency urgency, const char *summary, - const char *body, const NotifyIcon *icon, - gboolean expires, time_t timeout, - NotifyHints *hints, gpointer user_data, - size_t action_count, ...) -{ - va_list actions; - NotifyHandle *handle; - - g_return_val_if_fail(summary != NULL, 0); - - va_start(actions, action_count); - handle = notify_send_notification_varg(replaces, type, urgency, summary, - body, icon, expires, - timeout, hints, user_data, - action_count, actions); - va_end(actions); - - return handle; -} - -static void -hint_foreach_func(const gchar *key, NotifyHintData *hint, - DBusMessageIter *iter) -{ -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - DBusMessageIter entry_iter, value_iter; - - dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, - &entry_iter); - dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); - - switch (hint->type) + if (_app_name != NULL) { - case HINT_TYPE_STRING: - dbus_message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, - DBUS_TYPE_STRING_AS_STRING, - &value_iter); - dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, - &hint->u.string); - dbus_message_iter_close_container(&entry_iter, &value_iter); - break; - - case HINT_TYPE_INT: - dbus_message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, - DBUS_TYPE_INT32_AS_STRING, - &value_iter); - dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_INT32, - &hint->u.integer); - dbus_message_iter_close_container(&entry_iter, &value_iter); - break; - - case HINT_TYPE_BOOL: - dbus_message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, - DBUS_TYPE_BOOLEAN_AS_STRING, - &value_iter); - dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_BOOLEAN, - &hint->u.boolean); - dbus_message_iter_close_container(&entry_iter, &value_iter); - break; - - default: - { - /* Better than nothing... */ - char *empty = ""; - dbus_message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, - DBUS_TYPE_STRING_AS_STRING, - &value_iter); - dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, - &empty); - dbus_message_iter_close_container(&entry_iter, &value_iter); - break; - } + g_free (_app_name); + _app_name = NULL; } - dbus_message_iter_close_container(iter, &entry_iter); -#else - dbus_message_iter_append_dict_key(iter, key); - - switch (hint->type) - { - case HINT_TYPE_STRING: - dbus_message_iter_append_string(iter, hint->u.string); - break; - - case HINT_TYPE_INT: - dbus_message_iter_append_int32(iter, hint->u.integer); - break; - - case HINT_TYPE_BOOL: - dbus_message_iter_append_boolean(iter, hint->u.boolean); - break; - - default: - { - /* Better than nothing... */ - dbus_message_iter_append_string(iter, ""); - break; - } - } -#endif + /* TODO: keep track of all notifications and destroy them here? */ } -NotifyHandle * -notify_send_notification_varg(NotifyHandle *replaces, const char *type, - NotifyUrgency urgency, const char *summary, - const char *body, const NotifyIcon *icon, - gboolean expires, time_t timeout, - NotifyHints *hints, gpointer user_data, - size_t action_count, va_list actions) +gboolean +notify_is_initted (void) { - DBusMessage *message, *reply; - DBusMessageIter iter, array_iter, dict_iter; - DBusError error; - GHashTable *table; - guint32 id; - guint32 i; - guint32 replaces_id; - guint32 timeout_time; - NotifyHandle *handle; - - g_return_val_if_fail(notify_is_initted(), NULL); - - message = _notify_dbus_message_new("Notify", &iter); - - g_return_val_if_fail(message != NULL, NULL); - - replaces_id = (replaces != NULL ? replaces->id : 0); - - _notify_dbus_message_iter_append_string_or_empty(&iter, _app_name); - _notify_dbus_message_iter_append_string_or_empty(&iter, NULL); - _notify_dbus_message_iter_append_uint32(&iter, replaces_id); - _notify_dbus_message_iter_append_string_or_empty(&iter, type); - _notify_dbus_message_iter_append_byte(&iter, urgency); - _notify_dbus_message_iter_append_string(&iter, summary); - _notify_dbus_message_iter_append_string_or_empty(&iter, body); - - if (icon == NULL) - { - _notify_dbus_message_iter_append_string_or_empty(&iter, NULL); - } - else if (icon->raw_data) - { -#if !NOTIFY_CHECK_DBUS_VERSION(0, 30) - int i; - dbus_message_iter_append_array(&iter, &array_iter, DBUS_TYPE_ARRAY); - - for (i = 0; i < icon->frames; i++) - { - _notify_dbus_message_iter_append_byte_array(&array_iter, - icon->raw_data[i], - icon->raw_len[i]); - } -#else - /* - * I can't figure this out for D-BUS 0.3x, so we just won't - * support it - */ - _notify_dbus_message_iter_append_string_or_empty(&iter, NULL); -#endif - } - else - { - int i; - - g_assert(icon->uri != NULL); /* can be either raw data OR uri */ - -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, - &array_iter); -#else - dbus_message_iter_append_array(&iter, &array_iter, DBUS_TYPE_STRING); -#endif - - for (i = 0; i < icon->frames; i++) - { - _notify_dbus_message_iter_append_string(&array_iter, - icon->uri[i]); - } - - dbus_message_iter_close_container(&iter, &array_iter); - } - - /* Actions */ -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_UINT32_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &dict_iter); -#else - dbus_message_iter_append_dict(&iter, &dict_iter); -#endif - - table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, - (GFreeFunc)_notify_action_destroy); - - for (i = 0; i < action_count; i++) - { - NotifyAction *action; -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - DBusMessageIter entry_iter; -#endif - - action = g_new0(NotifyAction, 1); - - action->id = va_arg(actions, guint32); - action->text = g_strdup((va_arg(actions, char *))); - action->cb = va_arg(actions, NotifyCallback); - -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, - NULL, &entry_iter); - dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, - &action->text); - dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_UINT32, - &action->id); - dbus_message_iter_close_container(&dict_iter, &entry_iter); -#else - dbus_message_iter_append_dict_key(&dict_iter, action->text); - dbus_message_iter_append_uint32(&dict_iter, action->id); -#endif - - g_hash_table_insert(table, &action->id, action); - } - - dbus_message_iter_close_container(&iter, &dict_iter); - - /* Hints */ -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &dict_iter); -#else - dbus_message_iter_append_dict(&iter, &dict_iter); -#endif - - if (hints != NULL) - { - g_hash_table_foreach(hints->data, - (GHFunc)hint_foreach_func, &dict_iter); - } - -#if NOTIFY_CHECK_DBUS_VERSION(0, 30) - dbus_message_iter_close_container(&iter, &dict_iter); -#endif - - /* Expires */ - _notify_dbus_message_iter_append_boolean(&iter, expires); - - /* Expire Timeout */ - timeout_time = (expires ? timeout : 0); - _notify_dbus_message_iter_append_uint32(&iter, timeout_time); - - dbus_error_init(&error); - - reply = dbus_connection_send_with_reply_and_block(_dbus_conn, message, - -1, &error); - - dbus_message_unref(message); - - if (dbus_error_is_set(&error)) - { - print_error("error sending notification: %s\n", error.message); - - dbus_error_free(&error); - - g_hash_table_destroy(table); - - return 0; - } - - dbus_message_iter_init(reply, &iter); - _notify_dbus_message_iter_get_uint32(&iter, id); - - dbus_message_unref(reply); - dbus_error_free(&error); - - if (hints != NULL) - notify_hints_destroy(hints); - - handle = _notify_handle_new(id); - handle->actions_table = table; - handle->action_count = action_count; - handle->user_data = user_data; - handle->expires = expires; - - return handle; + return _initted; } diff --git a/libnotify/notify.h b/libnotify/notify.h index 0ddfeba..017cb19 100644 --- a/libnotify/notify.h +++ b/libnotify/notify.h @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. * * @todo We talk about URIs, but they are actually file paths not URIs + * @todo Un-glibify? */ #ifndef _LIBNOTIFY_NOTIFY_H_ @@ -28,22 +29,8 @@ #include #include -/** - * Notification urgency levels. - */ -typedef enum -{ - NOTIFY_URGENCY_LOW, /**< Low urgency. */ - NOTIFY_URGENCY_NORMAL, /**< Normal urgency. */ - NOTIFY_URGENCY_CRITICAL, /**< Critical urgency. */ - -} NotifyUrgency; - -typedef struct _NotifyHandle NotifyHandle; -typedef struct _NotifyIcon NotifyIcon; -typedef struct _NotifyHints NotifyHints; - -typedef void (*NotifyCallback)(NotifyHandle *, guint32, gpointer); +#include "notifycommon.h" +#include "notifynotification.h" /**************************************************************************/ /** @name libnotify Base API */ @@ -60,18 +47,6 @@ typedef void (*NotifyCallback)(NotifyHandle *, guint32, gpointer); */ gboolean notify_init(const char *app_name); -/** - * Initializes the notifications library and sets it up with the - * glib mainloop. - * - * @param app_name The application name. - * @param context The mainloop context, or NULL for the default. - * - * @return TRUE if the library initialized properly and a connection to a - * notification server was made. - */ -gboolean notify_glib_init(const char *app_name, GMainContext *context); - /** * Uninitializes the notifications library. * @@ -86,248 +61,8 @@ void notify_uninit(void); */ gboolean notify_is_initted(void); -/** - * Sets up libnotify with the glib mainloop. - * - * This is usually best done by simply calling notify_glib_init(). However, - * there's no harm in calling this yourself. - * - * @param context the #GMainContext or NULL for default context - */ -void notify_setup_with_g_main(GMainContext *context); - -/** - * Manually closes a notification. - * - * @param handle The notification handle. - */ -void notify_close(NotifyHandle *handle); - - -/** - * Returns the server information. - * - * The strings returned must be freed. - * - * @param ret_name The returned product name of the server. - * @param ret_vendor The returned vendor. - * @param ret_version The returned specification version supported. - * - * @return TRUE if the call succeeded, or FALSE if there were errors. - */ -gboolean notify_get_server_info(char **ret_name, char **ret_vendor, - char **ret_version); - -/** - * Returns the server's capabilities. - * - * The returned list and the strings inside must all be freed. - * - * @return The list of capabilities, or NULL on error. - */ -GList *notify_get_server_caps(void); - +const gchar *_notify_get_app_name(void); /*@}*/ -/**************************************************************************/ -/** @name Hints API */ -/**************************************************************************/ -/*@{*/ - -/** - * Creates a hints table. - * - * @return A hints table. - */ -NotifyHints *notify_hints_new(void); - -/** - * Adds a string value to the hints table. - * - * @param hints The hints table. - * @param key The key. - * @param value The value. - */ -void notify_hints_set_string(NotifyHints *hints, const char *key, - const char *value); - -/** - * Adds an integer value to the hints table. - * - * @param hints The hints table. - * @param key The key. - * @param value The value. - */ -void notify_hints_set_int(NotifyHints *hints, const char *key, int value); - -/** - * Adds a boolean value to the hints table. - * - * @param hints The hints table. - * @param key The key. - * @param value The value. - */ -void notify_hints_set_bool(NotifyHints *hints, const char *key, gboolean value); - -/*@}*/ - - -/**************************************************************************/ -/** @name NotifyIcon API */ -/**************************************************************************/ -/*@{*/ - -/** - * Creates an empty (invalid) icon. You must add at least one frame, - * otherwise the icon will be rejected. The first add_frame function - * you call determines if this is a raw data or URI based icon. - * - * This function is useful when adding data from a loop. - * - * @return A new invalid icon. - */ -NotifyIcon *notify_icon_new(); - -/** - * Creates an icon with the specified icon URI as the first frame. - * You can then add more frames by calling notify_icon_add_frame_from_uri. - * Note that you can't mix raw data and file URIs in the same icon. - * - * @param icon_uri The icon URI. - * - * @return The icon. - */ -NotifyIcon *notify_icon_new_from_uri(const char *icon_uri); - -/** - * Creates an icon with the specified icon data as the first frame. - * You can then add more frames by calling notify_icon_add_frame_from_data. - * Note that you can't mix raw data and file URIs in the same icon. - * - * @param icon_len The icon data length. - * @param icon_data The icon data. - * - * @return The icon. - */ -NotifyIcon *notify_icon_new_from_data(size_t icon_len, - const guchar *icon_data); - -/** - * Adds a frame from raw data (a png file) to the icons animation. - * - * @param icon The icon to add the frame to - * @param icon_len The frame data length - * @param icon_data The frame data - * - * @return TRUE if was successful, FALSE if this icon isn't a raw data - * icon or there was a bad parameter. - */ -gboolean notify_icon_add_frame_from_data(NotifyIcon *icon, - size_t icon_len, - const guchar *icon_data); - -/** - * Adds a frame from a URI to the icons animation. - * - * @param icon The icon to add the frame to - * @param icon_uri The URI of the icon file for the frame - * - * @return TRUE if was successful, FALSE if this icon isn't a file URI - * icon or there was a bad parameter. - */ -gboolean notify_icon_add_frame_from_uri(NotifyIcon *icon, - const char *icon_uri); - -/** - * Destroys an icon. - * - * @param icon The icon to destroy. - */ -void notify_icon_destroy(NotifyIcon *icon); - -/*@}*/ - -/**************************************************************************/ -/** @name Notifications API */ -/**************************************************************************/ -/*@{*/ - -/** - * Sends a notification. - * - * A callback has the following prototype: - * - * @code - * void callback(NotifyHandle *handle, guint32 action_id, void *user_data); - * @endcode - * - * @param replaces The ID of the notification to atomically replace - * @param type The optional notification type. - * @param urgency The urgency level. - * @param summary The summary of the notification. - * @param body The optional body. - * @param icon The optional icon. - * @param expires TRUE if the notification should automatically expire,, - * or FALSE to keep it open until manually closed. - * @param timeout The optional timeout to automatically close the - * notification, or 0 for the daemon's default. - * @param hints A hashtable of hints. - * @param user_data User-specified data to send to a callback. - * @param action_count The number of actions. - * @param ... The actions in uint32/string/callback sets. - * - * @return A unique ID for the notification. - */ -NotifyHandle *notify_send_notification(NotifyHandle *replaces, - const char *type, - NotifyUrgency urgency, - const char *summary, - const char *body, - const NotifyIcon *icon, - gboolean expires, time_t timeout, - NotifyHints *hints, - gpointer user_data, - size_t action_count, ...); - -/** - * Sends a notification, taking a va_list for the actions. - * - * A callback has the following prototype: - * - * @code - * void callback(NotifyHandle *handle, guint32 action, void *user_data); - * @endcode - * - * @param replaces The handle of the notification to atomically replace - * @param type The optional notification type. - * @param urgency The urgency level. - * @param summary The summary of the notification. - * @param detailed The optional body. - * @param icon The optional icon. - * @param expires TRUE if the notification should automatically expire, - * or FALSE to keep it open until manually closed. - * @param timeout The optional timeout to automatically close the - * notification, or 0 for the daemon's default. - * @param hints A hashtable of hints. - * @param user_data User-specified data to send to a callback. - * @param action_count The number of actions. - * @param actions The actions in string/callback pairs. - * - * @return A unique ID for the notification. - */ -NotifyHandle *notify_send_notification_varg(NotifyHandle *replaces, - const char *type, - NotifyUrgency urgency, - const char *summary, - const char *detailed, - const NotifyIcon *icon, - gboolean expires, - time_t timeout, - NotifyHints *hints, - gpointer user_data, - size_t action_count, - va_list actions); - -/*@}*/ #endif /* _LIBNOTIFY_NOTIFY_H_ */ diff --git a/libnotify/notifycommon.h b/libnotify/notifycommon.h new file mode 100644 index 0000000..b2ad943 --- /dev/null +++ b/libnotify/notifycommon.h @@ -0,0 +1,23 @@ +#ifndef _LIBNOTIFY_NOTIFY_COMMON_H_ +#define _LIBNOTIFY_NOTIFY_COMMON_H_ +#include + +#define NOTIFY_TIMEOUT_DEFAULT -1 +#define NOTIFY_TIMEOUT_NEVER 0 + +#define NOTIFY_DBUS_NAME "org.freedesktop.Notifications" +#define NOTIFY_DBUS_CORE_INTERFACE "org.freedesktop.Notifications" +#define NOTIFY_DBUS_CORE_OBJECT "/org/freedesktop/Notifications" + +/** + * Notification urgency levels. + */ +typedef enum +{ + NOTIFY_URGENCY_LOW, /**< Low urgency. */ + NOTIFY_URGENCY_NORMAL, /**< Normal urgency. */ + NOTIFY_URGENCY_CRITICAL, /**< Critical urgency. */ + +} NotifyUrgency; + +#endif /* _LIBNOTIFY_NOTIFY_H_ */ diff --git a/libnotify/notifynotification.c b/libnotify/notifynotification.c new file mode 100644 index 0000000..08ac75e --- /dev/null +++ b/libnotify/notifynotification.c @@ -0,0 +1,934 @@ +/* + * This program 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.1 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with main.c; if not, write to: + * The Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "config.h" +#include +#include + +#include "notify.h" +#include "notifynotification.h" +#include "notify-marshal.h" + +static void notify_notification_class_init (NotifyNotificationClass * klass); +static void notify_notification_init (NotifyNotification * sp); +static void notify_notification_finalize (GObject * object); +static void _close_signal_handler (DBusGProxy *proxy, + guint32 id, + NotifyNotification *notification); + +static void _action_signal_handler (DBusGProxy *proxy, + guint32 id, + gchar *action, + NotifyNotification *notification); + +struct NotifyNotificationPrivate +{ + guint32 id; + gchar *summary; + gchar *message; + + /*NULL to use icon data + anything else to have server lookup icon */ + gchar *icon_name; + + /*-1 = use server default + 0 = never timeout + >0 = Number of milliseconds before we timeout + */ + gint timeout; + + GSList *actions; + GHashTable *action_map; + GHashTable *hints; + + GtkWidget *attached_widget; + gint widget_old_x; + gint widget_old_y; + + gpointer user_data; + GDestroyNotify user_data_free_func; + + gboolean updates_pending; + + DBusGProxy *proxy; +}; + +typedef enum +{ + SIGNAL_TYPE_CLOSED, + LAST_SIGNAL +} NotifyNotificationSignalType; + +typedef struct +{ + NotifyNotification *object; +} NotifyNotificationSignal; + +static guint notify_notification_signals[LAST_SIGNAL] = { 0 }; +static GObjectClass *parent_class = NULL; + +GType +notify_notification_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo our_info = { + sizeof (NotifyNotificationClass), + NULL, + NULL, + (GClassInitFunc) notify_notification_class_init, + NULL, + NULL, + sizeof (NotifyNotification), + 0, + (GInstanceInitFunc) notify_notification_init, + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "NotifyNotification", &our_info, 0); + } + + return type; +} + +static void +notify_notification_class_init (NotifyNotificationClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + object_class->finalize = notify_notification_finalize; + + /* Create signals here: */ + notify_notification_signals[SIGNAL_TYPE_CLOSED] = + g_signal_new ("closed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NotifyNotificationClass, closed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + dbus_g_object_register_marshaller (notify_marshal_VOID__UINT_STRING, + G_TYPE_NONE, + G_TYPE_UINT, + G_TYPE_STRING, + G_TYPE_INVALID); + +} + +static void +_g_value_free (GValue * value) +{ + g_value_unset (value); + g_free (value); +} + +static void +notify_notification_init (NotifyNotification * obj) +{ + obj->priv = g_new0 (NotifyNotificationPrivate, 1); + + obj->priv->id = 0; + + obj->priv->summary = NULL; + obj->priv->message = NULL; + obj->priv->icon_name = NULL; + obj->priv->timeout = NOTIFY_TIMEOUT_DEFAULT; + + obj->priv->actions = NULL; + obj->priv->hints = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) _g_value_free); + + + obj->priv->action_map = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + obj->priv->attached_widget = NULL; + obj->priv->user_data = NULL; + obj->priv->user_data_free_func = NULL; + + obj->priv->updates_pending = FALSE; + + obj->priv->widget_old_x = 0; + obj->priv->widget_old_y = 0; + + obj->priv->proxy = NULL; + +} + +static void +notify_notification_finalize (GObject * object) +{ + NotifyNotification *obj; + NotifyNotificationPrivate *priv; + + obj = NOTIFY_NOTIFICATION (object); + priv = obj->priv; + + g_free (priv->summary); + g_free (priv->message); + g_free (priv->icon_name); + + + if (priv->actions != NULL) + { + g_slist_foreach (priv->actions, (GFunc) g_free, NULL); + g_slist_free (priv->actions); + } + + if (priv->action_map != NULL) + g_hash_table_destroy (priv->action_map); + + if (priv->hints != NULL) + g_hash_table_destroy (priv->hints); + + if (priv->attached_widget != NULL) + gtk_widget_unref (priv->attached_widget); + + if (priv->user_data_free_func != NULL) + priv->user_data_free_func (priv->user_data); + + dbus_g_proxy_disconnect_signal (priv->proxy, "NotificationClosed", + (GCallback) _close_signal_handler, + object); + + dbus_g_proxy_disconnect_signal (priv->proxy, "ActionInvoked", + (GCallback) _action_signal_handler, + object); + + g_free (obj->priv); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +_notify_notification_update_applet_hints (NotifyNotification * n) +{ + NotifyNotificationPrivate *priv; + gboolean update_pending; + + update_pending = FALSE; + priv = n->priv; + + if (priv->attached_widget != NULL) + { + gint x, y, h, w; + GtkWidget *widget; + GtkRequisition requisition; + + widget = priv->attached_widget; + + gtk_widget_size_request (widget, &requisition); + w = requisition.width; + h = requisition.height; + + gdk_window_get_origin (widget->window, &x, &y); + if (GTK_WIDGET_NO_WINDOW (widget)) + { + x += widget->allocation.x; + y += widget->allocation.y; + } + + x += widget->allocation.width / 2; + y += widget->allocation.height / 2; + + if (x != priv->widget_old_x) + { + notify_notification_set_hint_int32 (n, "x", x); + priv->widget_old_x = x; + + update_pending = TRUE; + } + + if (y != priv->widget_old_y) + { + notify_notification_set_hint_int32 (n, "y", y); + priv->widget_old_y = y; + + update_pending = TRUE; + } + } + + return update_pending; +} + +#if 0 + +/* This is left here just incase we revisit autoupdating + One thought would be to check for updates every time the icon + is redrawn but we still have to deal with the race conditions + that could occure between the server and the client so we will + leave this alone for now. +*/ +static gboolean +_idle_check_updates (void *user_data) +{ + NotifyNotification *n; + NotifyNotificationPrivate *priv; + + n = NOTIFY_NOTIFICATION (user_data); + priv = n->priv; + + if (priv->is_visible) + { + priv->updates_pending = _notify_notification_update_applet_hints (n); + if (priv->updates_pending) + { + /* Try again if we fail on next idle */ + priv->updates_pending = !notify_notification_show (n, NULL); + } + } + else + { + priv->updates_pending = FALSE; + } + + n = NOTIFY_NOTIFICATION (user_data); + priv = n->priv; + + return TRUE; +} +#endif + +GdkFilterReturn +_catch (GdkXEvent * xevent, GdkEvent * event, gpointer data) +{ + static int i = 1; + printf ("here, %i\n", i); + i++; + return GDK_FILTER_CONTINUE; +} + +NotifyNotification * +notify_notification_new (const gchar * summary, + const gchar * message, + const gchar * icon, GtkWidget * attach) +{ + NotifyNotification *obj; + + g_assert (summary != NULL); + g_assert (message != NULL); + + obj = NOTIFY_NOTIFICATION (g_object_new (NOTIFY_TYPE_NOTIFICATION, NULL)); + + obj->priv->summary = g_strdup (summary); + obj->priv->message = g_strdup (message); + obj->priv->icon_name = g_strdup (icon); + + if (attach != NULL) + { + gtk_widget_ref (attach); + obj->priv->attached_widget = attach; + } + + return obj; +} + +gboolean +notify_notification_update (NotifyNotification * notification, + const gchar * summary, + const gchar * message, const gchar * icon) +{ + NotifyNotificationPrivate *priv; + + priv = notification->priv; + g_free (priv->summary); + g_free (priv->message); + g_free (priv->icon_name); + + priv->summary = g_strdup (summary); + priv->message = g_strdup (message); + priv->icon_name = g_strdup (icon); + + priv->updates_pending = TRUE; + + /*TODO: return false on OOM */ + return TRUE; +} + +void +notify_notification_attach_to_widget (NotifyNotification * notification, + GtkWidget * attach) +{ + NotifyNotificationPrivate *priv; + + priv = notification->priv; + + if (priv->attached_widget != NULL) + gtk_widget_unref (priv->attached_widget); + + if (attach != NULL) + priv->attached_widget = gtk_widget_ref (attach); + else + priv->attached_widget = NULL; + +} + +gboolean +notify_notification_set_user_data (NotifyNotification * notification, + void *user_data, GFreeFunc free_func) +{ + NotifyNotificationPrivate *priv; + + priv = notification->priv; + + if (priv->user_data) + if (priv->user_data_free_func) + priv->user_data_free_func (priv->user_data); + + priv->user_data = user_data; + priv->user_data_free_func = priv->user_data; + + /* TODO: return FALSE on OOM */ + return TRUE; +} + +static void +_close_signal_handler (DBusGProxy *proxy, + guint32 id, + NotifyNotification *notification) +{ + printf ("Got the NotificationClosed signal (id = %i, notification->id = %i)\n" +, id, notification->priv->id); + + if (id == notification->priv->id) + g_signal_emit (notification, + notify_notification_signals[SIGNAL_TYPE_CLOSED], + 0); +} + +static void +_action_signal_handler (DBusGProxy *proxy, + guint32 id, + gchar *action, + NotifyNotification *notification) +{ + g_assert (NOTIFY_IS_NOTIFICATION (notification)); + + if (id == notification->priv->id) + { + NotifyActionCallback callback; + + callback = (NotifyActionCallback) g_hash_table_lookup (notification->priv->action_map, + action); + + if (callback == NULL) + g_warning ("Recieved unknown action %s", action); + else + callback (notification, action); + + } +} + +static gchar ** +_gslist_to_string_array (GSList *list) +{ + GSList *element; + GArray *a; + gsize len; + gchar **result; + + len = g_slist_length (list); + + a = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), len); + + element = list; + while (element != NULL) + { + g_array_append_val (a, element->data); + + element = g_slist_next (element); + } + + result = (gchar **)g_array_free (a, FALSE); + + return result; +} + +static gboolean +_notify_notification_show_internal (NotifyNotification *notification, + GError **error, + gboolean ignore_reply) +{ + NotifyNotificationPrivate *priv; + GError *tmp_error; + gchar **action_array; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + priv = notification->priv; + + tmp_error = NULL; + + if (priv->proxy == NULL) + { + DBusGConnection *bus; + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &tmp_error); + if (tmp_error != NULL) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + + priv->proxy = dbus_g_proxy_new_for_name (bus, + NOTIFY_DBUS_NAME, + NOTIFY_DBUS_CORE_OBJECT, + NOTIFY_DBUS_CORE_INTERFACE); + + dbus_g_proxy_add_signal (priv->proxy, "NotificationClosed", + G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "NotificationClosed", + (GCallback) _close_signal_handler, + notification, NULL); + + dbus_g_proxy_add_signal (priv->proxy, "ActionInvoked", + G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "ActionInvoked", + (GCallback) _action_signal_handler, + notification, NULL); + + + dbus_g_connection_unref (bus); + } + + /*if attached to a widget modify x and y in hints */ + _notify_notification_update_applet_hints (notification); + + action_array = _gslist_to_string_array (priv->actions); + + /*TODO: make this nonblocking */ + if (!ignore_reply) + dbus_g_proxy_call (priv->proxy, "Notify", &tmp_error, + G_TYPE_STRING, _notify_get_app_name (), + G_TYPE_STRING, + (priv->icon_name != NULL) ? priv->icon_name : "", + G_TYPE_UINT, priv->id, G_TYPE_STRING, priv->summary, + G_TYPE_STRING, priv->message, + G_TYPE_STRV, + action_array, dbus_g_type_get_map ("GHashTable", + G_TYPE_STRING, + G_TYPE_VALUE), + priv->hints, G_TYPE_INT, priv->timeout, G_TYPE_INVALID, + G_TYPE_UINT, &priv->id, G_TYPE_INVALID); + else + dbus_g_proxy_call_no_reply (priv->proxy, "Notify", + G_TYPE_STRING, _notify_get_app_name (), + G_TYPE_STRING, + (priv->icon_name != NULL) ? priv->icon_name : "", + G_TYPE_UINT, priv->id, G_TYPE_STRING, priv->summary, + G_TYPE_STRING, priv->message, + G_TYPE_STRV, + action_array, dbus_g_type_get_map ("GHashTable", + G_TYPE_STRING, + G_TYPE_VALUE), + priv->hints, G_TYPE_INT, priv->timeout, G_TYPE_INVALID); + + + + /*don't free the elements because they are owned by priv->actions */ + g_free (action_array); + + if (tmp_error != NULL) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + + return TRUE; +} + + + +gboolean +notify_notification_show (NotifyNotification *notification, GError **error) +{ + return _notify_notification_show_internal (notification, error, FALSE); +} + +gboolean +notify_notification_show_and_forget (NotifyNotification *notification, GError **error) +{ + gboolean result; + + result = _notify_notification_show_internal (notification, error, TRUE); + g_object_unref (G_OBJECT (notification)); + + return result; +} + +void +notify_notification_set_timeout (NotifyNotification * notification, + gint timeout) +{ + notification->priv->timeout = timeout; +} + +gboolean +notify_notification_set_category (NotifyNotification * notification, + const char *category) +{ + return notify_notification_set_hint_string (notification, + "category", category); +} + +gboolean +notify_notification_set_urgency (NotifyNotification * notification, + NotifyUrgency l) +{ + return notify_notification_set_hint_byte (notification, + "urgency", (guchar) l); +} + +static gboolean +_gvalue_array_append_int (GValueArray * array, gint i) +{ + GValue *value; + + value = g_new0 (GValue, 1); + if (!value) + return FALSE; + + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, i); + + g_value_array_append (array, value); + + return TRUE; +} + +static gboolean +_gvalue_array_append_bool (GValueArray * array, gboolean b) +{ + GValue *value; + + value = g_new0 (GValue, 1); + if (!value) + return FALSE; + + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, b); + + g_value_array_append (array, value); + + return TRUE; +} + +static gboolean +_gvalue_array_append_byte_array (GValueArray * array, + guchar * bytes, gsize len) +{ + GArray *byte_array; + GValue *value; + + byte_array = g_array_sized_new (FALSE, FALSE, sizeof (guchar), len); + if (!byte_array) + return FALSE; + + byte_array = g_array_append_vals (byte_array, bytes, len); + + value = g_new0 (GValue, 1); + if (!value) + { + g_array_free (byte_array, TRUE); + return FALSE; + } + + g_value_init (value, dbus_g_type_get_collection ("GArray", G_TYPE_UCHAR)); + g_value_set_boxed_take_ownership (value, byte_array); + + g_value_array_append (array, value); + + return TRUE; +} + + + +gboolean +notify_notification_set_icon_data_from_pixbuf (NotifyNotification * + notification, GdkPixbuf * icon) +{ + gint width; + gint height; + gint rowstride; + gboolean alpha; + gint bits_per_sample; + gint n_channels; + guchar *image; + gsize image_len; + gchar *key_dup; + + GValueArray *image_struct; + GValue *value; + NotifyNotificationPrivate *priv; + + priv = notification->priv; + + width = gdk_pixbuf_get_width (icon); + height = gdk_pixbuf_get_height (icon); + rowstride = gdk_pixbuf_get_rowstride (icon); + n_channels = gdk_pixbuf_get_n_channels (icon); + bits_per_sample = gdk_pixbuf_get_bits_per_sample (icon); + alpha = gdk_pixbuf_get_has_alpha (icon); + image_len = + (height - 1) * rowstride + + width * ((n_channels * bits_per_sample + 7) / 8); + + image = gdk_pixbuf_get_pixels (icon); + + image_struct = g_value_array_new (8); + + if (!image_struct) + goto fail; + + _gvalue_array_append_int (image_struct, width); + _gvalue_array_append_int (image_struct, height); + _gvalue_array_append_int (image_struct, rowstride); + _gvalue_array_append_bool (image_struct, alpha); + _gvalue_array_append_int (image_struct, bits_per_sample); + _gvalue_array_append_int (image_struct, n_channels); + _gvalue_array_append_byte_array (image_struct, image, image_len); + + value = g_new0 (GValue, 1); + if (!value) + goto fail; + + g_value_init (value, G_TYPE_VALUE_ARRAY); + g_value_set_boxed (value, image_struct); + + key_dup = g_strdup ("icon_data"); + if (!key_dup) + goto fail; + + g_hash_table_insert (priv->hints, key_dup, value); + + return TRUE; + +fail: + if (image_struct != NULL) + g_value_array_free (image_struct); + return FALSE; +} + +gboolean +notify_notification_set_hint_int32 (NotifyNotification * notification, + const gchar * key, gint value) +{ + NotifyNotificationPrivate *priv; + GValue *hint_value; + gchar *key_dup; + + priv = notification->priv; + + hint_value = g_new0 (GValue, 1); + g_value_init (hint_value, G_TYPE_INT); + g_value_set_int (hint_value, value); + + key_dup = g_strdup (key); + + g_hash_table_insert (priv->hints, key_dup, hint_value); + + /* TODO: return FALSE on OOM */ + return TRUE; +} + +gboolean +notify_notification_set_hint_double (NotifyNotification * notification, + const gchar * key, gdouble value) +{ + NotifyNotificationPrivate *priv; + GValue *hint_value; + gchar *key_dup; + + priv = notification->priv; + + hint_value = g_new0 (GValue, 1); + g_value_init (hint_value, G_TYPE_FLOAT); + g_value_set_float (hint_value, value); + + key_dup = g_strdup (key); + + g_hash_table_insert (priv->hints, key_dup, hint_value); + + /* TODO: return FALSE on OOM */ + return TRUE; +} + +gboolean +notify_notification_set_hint_byte (NotifyNotification * notification, + const gchar * key, guchar value) +{ + NotifyNotificationPrivate *priv; + GValue *hint_value; + gchar *key_dup; + + priv = notification->priv; + + hint_value = g_new0 (GValue, 1); + g_value_init (hint_value, G_TYPE_UCHAR); + g_value_set_uchar (hint_value, value); + + key_dup = g_strdup (key); + + g_hash_table_insert (priv->hints, key_dup, hint_value); + + /* TODO: return FALSE on OOM */ + return TRUE; +} + +gboolean +notify_notification_set_hint_byte_array (NotifyNotification * notification, + const gchar * key, + const guchar * value, gsize len) +{ + NotifyNotificationPrivate *priv; + GValue *hint_value; + gchar *key_dup; + GArray *byte_array; + + priv = notification->priv; + + byte_array = g_array_sized_new (FALSE, FALSE, sizeof (guchar), len); + byte_array = g_array_append_vals (byte_array, value, len); + + hint_value = g_new0 (GValue, 1); + g_value_init (hint_value, + dbus_g_type_get_collection ("GArray", G_TYPE_UCHAR)); + g_value_set_boxed_take_ownership (hint_value, byte_array); + key_dup = g_strdup (key); + + g_hash_table_insert (priv->hints, key_dup, hint_value); + + /* TODO: return FALSE on OOM */ + return TRUE; +} + + +gboolean +notify_notification_set_hint_string (NotifyNotification * notification, + const gchar * key, const gchar * value) +{ + NotifyNotificationPrivate *priv; + GValue *hint_value; + gchar *key_dup; + + priv = notification->priv; + + hint_value = g_new0 (GValue, 1); + g_value_init (hint_value, G_TYPE_STRING); + g_value_set_string (hint_value, value); + + key_dup = g_strdup (key); + + g_hash_table_insert (priv->hints, key_dup, hint_value); + + /* TODO: return FALSE on OOM */ + return TRUE; +} + +static gboolean +_remove_all (void) +{ + return TRUE; +} + +void +notify_notification_clear_hints (NotifyNotification *notification) +{ + g_hash_table_foreach_remove (notification->priv->hints, + (GHRFunc) _remove_all, NULL); +} + +void +notify_notification_clear_actions (NotifyNotification *notification) +{ + g_hash_table_foreach_remove (notification->priv->action_map, (GHRFunc) _remove_all, NULL); + + if (notification->priv->actions != NULL) + { + g_slist_foreach (notification->priv->actions, (GFunc) g_free, NULL); + g_slist_free (notification->priv->actions); + } + + notification->priv->actions = NULL; +} + +gboolean +notify_notification_add_action (NotifyNotification *notification, + const char *action, + const char *label, + NotifyActionCallback callback) +{ + NotifyNotificationPrivate *priv; + + priv = notification->priv; + + priv->actions = g_slist_append (priv->actions, g_strdup (action)); + priv->actions = g_slist_append (priv->actions, g_strdup (label)); + + g_hash_table_insert (priv->action_map, g_strdup (action), callback); + + return FALSE; +} + +gboolean +notify_notification_close (NotifyNotification * notification, GError ** error) +{ + NotifyNotificationPrivate *priv; + GError *tmp_error; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + priv = notification->priv; + + tmp_error = NULL; + + if (priv->proxy == NULL) + { + DBusGConnection *bus; + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &tmp_error); + if (tmp_error != NULL) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + + priv->proxy = dbus_g_proxy_new_for_name (bus, + NOTIFY_DBUS_NAME, + NOTIFY_DBUS_CORE_OBJECT, + NOTIFY_DBUS_CORE_INTERFACE); + dbus_g_connection_unref (bus); + } + + dbus_g_proxy_call (priv->proxy, "CloseNotification", &tmp_error, + G_TYPE_UINT, priv->id, G_TYPE_INVALID, G_TYPE_INVALID); + + if (tmp_error != NULL) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + + return TRUE; +} diff --git a/libnotify/notifynotification.h b/libnotify/notifynotification.h new file mode 100644 index 0000000..c0da83f --- /dev/null +++ b/libnotify/notifynotification.h @@ -0,0 +1,125 @@ +/* + * This program 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.1 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with main.c; if not, write to: + * The Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef NOTIFY_NOTIFICATION_H +#define NOTIFY_NOTIFICATION_H +#include "config.h" + +#include +#include +#include + +#include "notifycommon.h" + +G_BEGIN_DECLS + +#define NOTIFY_TYPE_NOTIFICATION (notify_notification_get_type ()) +#define NOTIFY_NOTIFICATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NOTIFY_TYPE_NOTIFICATION, NotifyNotification)) +#define NOTIFY_NOTIFICATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NOTIFY_TYPE_NOTIFICATION, NotifyNotificationClass)) +#define NOTIFY_IS_NOTIFICATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NOTIFY_TYPE_NOTIFICATION)) +#define NOTIFY_IS_NOTIFICATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NOTIFY_TYPE_NOTIFICATION)) +#define NOTIFY_NOTIFICATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NOTIFY_TYPE_NOTIFICATION, NotifyNotificationClass)) + +typedef struct NotifyNotificationPrivate NotifyNotificationPrivate; + +typedef struct { + GObject parent; + NotifyNotificationPrivate *priv; +} NotifyNotification; + +typedef struct { + GObjectClass parent_class; + /* Add Signal Functions Here */ + void (*closed) (void); +} NotifyNotificationClass; + +typedef void (*NotifyActionCallback )(NotifyNotification *, gchar *); + +GType notify_notification_get_type(); +NotifyNotification *notify_notification_new (const gchar *summary, + const gchar *message, + const gchar *icon, + GtkWidget *attach); + +gboolean notify_notification_update (NotifyNotification *notification, + const gchar *summary, + const gchar *message, + const gchar *icon); + +void notify_notification_attach_to_widget (NotifyNotification *notification, + GtkWidget *attach); + +gboolean notify_notification_set_user_data (NotifyNotification *notification, + void *user_data, + GFreeFunc free_func); + +gboolean notify_notification_show (NotifyNotification *notification, + GError **error); + +gboolean notify_notification_show_and_forget (NotifyNotification *notification, + GError **error); + +void notify_notification_set_timeout (NotifyNotification *notification, + gint timeout); + +gboolean notify_notification_set_category (NotifyNotification *notification, + const char *category); + +gboolean notify_notification_set_urgency (NotifyNotification *notification, + NotifyUrgency l); + +gboolean notify_notification_set_icon_data_from_pixbuf (NotifyNotification *notification, + GdkPixbuf *icon); + + +gboolean notify_notification_set_hint_int32 (NotifyNotification *notification, + const gchar *key, + gint value); + +gboolean notify_notification_set_hint_double (NotifyNotification *notification, + const gchar *key, + gdouble value); + +gboolean notify_notification_set_hint_string (NotifyNotification *notification, + const gchar *key, + const gchar *value); + +gboolean notify_notification_set_hint_byte (NotifyNotification *notification, + const gchar *key, + guchar value); + +gboolean notify_notification_set_hint_byte_array ( + NotifyNotification *notification, + const gchar *key, + const guchar *value, + gsize len); + +void notify_notification_clear_hints (NotifyNotification *notification); + +gboolean notify_notification_add_action (NotifyNotification *notification, + const char *action, + const char *label, + NotifyActionCallback callback); + +void notify_notification_clear_actions (NotifyNotification *notification); +gboolean notify_notification_close (NotifyNotification *notification, + GError **error); + +NotifyNotification *notify_notification_ref (NotifyNotification *notification); +void notify_notification_unref (NotifyNotification *notification); +#endif /* NOTIFY_NOTIFICATION_H */ diff --git a/libnotify/notifynotification.xml b/libnotify/notifynotification.xml new file mode 100644 index 0000000..cba9db3 --- /dev/null +++ b/libnotify/notifynotification.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Makefile.am b/tests/Makefile.am index 7c9ca3a..9f4195a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,9 +1,7 @@ if HAVE_GDK gdk_tests = \ - test-animation \ test-xy-stress -test_animation_SOURCES = test-animation.c test_xy_stress_SOURCES = test-xy-stress.c else gdk_tests = @@ -11,6 +9,7 @@ endif noinst_PROGRAMS = \ test-replace \ + test-replace-widget \ test-default-action \ test-multi-actions \ test-image \ @@ -27,6 +26,8 @@ common_ldflags = \ test_replace_SOURCES = test-replace.c test_replace_LDADD = $(common_ldflags) +test_replace_widget_SOURCES = test-replace-widget.c +test_replace_widget_LDADD = $(common_ldflags) test_default_action_SOURCES = test-default-action.c test_default_action_LDADD = $(common_ldflags) test_multi_actions_SOURCES = test-multi-actions.c @@ -38,8 +39,6 @@ test_basic_LDADD = $(common_ldflags) test_error_SOURCES = test-error.c test_error_LDADD = $(common_ldflags) -test_animation_LDADD = $(common_ldflags) - test_markup_SOURCES = test-markup.c test_markup_LDADD = $(common_ldflags) @@ -48,6 +47,6 @@ test_xy_LDADD = $(common_ldflags) test_xy_stress_LDADD = $(common_ldflags) -INCLUDES = $(PACKAGE_CFLAGS) $(GDK_CFLAGS) -I$(top_srcdir) - -EXTRA_DIST = applet-critical.png +INCLUDES = $(PACKAGE_CFLAGS) \ + $(GDK_CFLAGS) \ + -I$(top_srcdir) diff --git a/tests/test-animation.c b/tests/test-animation.c deleted file mode 100644 index ae544a8..0000000 --- a/tests/test-animation.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * @file tests/test-animation.c Unit test: animated images - * - * @Copyright (C) 2004 Mike Hearn - * - * 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.1 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 - * Lesser 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include - -#include - - -/// WRITE ME! /// - -const int frames = 10; - -// returns array of pixbufs for a pulsing animation -GdkPixbuf **generate_animation() -{ - int i; - GdkPixbuf *file = gdk_pixbuf_new_from_file("applet-critical.png", NULL); - double alpha = 1.0; - - GdkPixbuf **array = g_malloc(sizeof(GdkPixbuf *) * frames); - - for (i = 0; i < frames; i++) - { - GdkPixbuf *buf = gdk_pixbuf_copy(file); - - alpha = sin(M_PI + ((M_PI / frames) * i)) + 1.0; - - gdk_pixbuf_composite(file, buf, 0, 0, - gdk_pixbuf_get_width(buf), - gdk_pixbuf_get_height(buf), - 0, 0, 1.0, 1.0, GDK_INTERP_NEAREST, - alpha); - - array[i] = buf; - } - - return array; -} - - -int main() -{ - int i; - GdkPixbuf **buffers; - NotifyIcon *icon; - - notify_init("Animations"); - - g_type_init(); - - buffers = generate_animation(); - icon = notify_icon_new(); - - for (i = 0; i < frames; i++) - { - gchar *pngdata; - gsize size; - GError *error = NULL; - - gdk_pixbuf_save_to_buffer(buffers[i], &pngdata, &size, "png", - &error, NULL); - - g_assert(error == NULL); - - notify_icon_add_frame_from_data(icon, size, (guchar *)pngdata); - } - - NotifyHandle *n = notify_send_notification(NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - "Summary", "Content", - icon, - TRUE, 0, - NULL, // no hints - NULL, // no user data - 0); // no actions - - if (!n) { - fprintf(stderr, "failed to send notification\n"); - return 1; - } - - return 0; -} diff --git a/tests/test-basic.c b/tests/test-basic.c index 21dad80..4599b08 100644 --- a/tests/test-basic.c +++ b/tests/test-basic.c @@ -24,19 +24,16 @@ #include int main() { + NotifyNotification *n; + notify_init("Basics"); - NotifyHandle *n = notify_send_notification(NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - "Summary", "Content", - NULL, // no icon - TRUE, 0, - NULL, // no hints - NULL, // no user data - 0); // no actions + n = notify_notification_new ("Summary", + "Content that is very long 8374983278r32j4 rhjjfh dw8f 43jhf 8ds7 ur2389f jdbjkt h8924yf jkdbjkt 892hjfiHER98HEJIF BDSJHF hjdhF JKLH 890YRHEJHFU 89HRJKSHdd dddd ddddd dddd ddddd dddd ddddd dddd dddd ddd ddd dddd Fdd d ddddd dddddddd ddddddddhjkewdkjsjfjk sdhkjf hdkj dadasdadsa adsd asd sd saasd fadskfkhsjf hsdkhfkshfjkhsd kjfhsjdkhfj ksdhfkjshkjfsd sadhfjkhaskd jfhsdajkfhkjs dhfkjsdhfkjs adhjkfhasdkj fhdsakjhfjk asdhkjkfhd akfjshjfsk afhjkasdhf jkhsdaj hf kjsdfahkfh sakjhfksdah kfdashkjf ksdahfj shdjdh", + NULL, NULL); + notify_notification_set_timeout (n, 3000); //3 seconds - if (!n) { + if (!notify_notification_show_and_forget (n, NULL)) { fprintf(stderr, "failed to send notification\n"); return 1; } diff --git a/tests/test-default-action.c b/tests/test-default-action.c index db53fdd..d452aa0 100644 --- a/tests/test-default-action.c +++ b/tests/test-default-action.c @@ -24,8 +24,9 @@ #include #include #include +#include -#define DBUS_API_SUBJECT_TO_CHANGE 1 +#define DBUS_API_SUBJECT_TO_CHANGE #include #include @@ -33,13 +34,13 @@ #include static GMainLoop *loop; -static NotifyHandle *n; -static void callback(NotifyHandle *handle, guint32 uid, void *user_data) +static void callback(NotifyNotification *n, const char *action, void *user_data) { - assert( uid == 0 ); + assert (action != NULL); + assert (strcmp ("default", action) == 0); - notify_close(n); + notify_notification_close (n, NULL); g_main_loop_quit(loop); } @@ -47,23 +48,23 @@ static void callback(NotifyHandle *handle, guint32 uid, void *user_data) int main() { + NotifyNotification *n; + DBusConnection *conn; + + if (!notify_init("Default Action Test")) exit(1); + + conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); loop = g_main_loop_new(NULL, FALSE); - if (!notify_glib_init("Default Action Test", NULL)) - exit(1); + dbus_connection_setup_with_g_main(conn, NULL); - n = notify_send_notification(NULL, // replaces nothing - "presence.online", - NOTIFY_URGENCY_NORMAL, - "Matt is online", NULL, - NULL, // no icon - FALSE, 0, // does not expire - NULL, // no hints - NULL, // no user data - 1, - 0, "default", callback); // 1 action + n = notify_notification_new ("Matt is online", "", NULL, NULL); + notify_notification_set_timeout (n, NOTIFY_TIMEOUT_NEVER); + notify_notification_add_action (n, "default", "Do Default Action", + (NotifyActionCallback)callback); + notify_notification_set_category (n, "presence.online"); - if (!n) { + if (!notify_notification_show (n, NULL)) { fprintf(stderr, "failed to send notification\n"); return 1; } diff --git a/tests/test-error.c b/tests/test-error.c index fd34b37..c1ef835 100644 --- a/tests/test-error.c +++ b/tests/test-error.c @@ -24,26 +24,25 @@ #include int main() { + NotifyNotification *n; + + g_type_init (); + notify_init("Error Handling"); - NotifyIcon *icon = notify_icon_new("/no-such"); + n = notify_notification_new ("Summary", + "Content", + NULL, NULL); + notify_notification_set_timeout (n, 3000); //3 seconds - NotifyHandle *n = notify_send_notification(NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - "Summary", "Content", - icon, // no icon - TRUE, 0, - NULL, // no hints - NULL, // no user data - 0); + /* TODO: Create an error condition */ - notify_icon_destroy(icon); - if (n) { - fprintf(stderr, "failed to get an error ??\n"); + if (!notify_notification_show (n, NULL)) { + fprintf(stderr, "failed to send notification\n"); return 1; } + return 0; } diff --git a/tests/test-image.c b/tests/test-image.c index e012f90..e7b845e 100644 --- a/tests/test-image.c +++ b/tests/test-image.c @@ -1,12 +1,12 @@ /* * @file tests/test-image.c Unit test: images * - * @Copyright (C) 2004 Mike Hearn + * @Copyright(C) 2004 Mike Hearn * * 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.1 of the License, or (at your option) any later version. + * version 2.1 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 @@ -31,91 +31,89 @@ #include #include -#define DBUS_API_SUBJECT_TO_CHANGE 1 +#define DBUS_API_SUBJECT_TO_CHANGE #include +#include #include #include #include GMainLoop *loop; -NotifyHandle *n; - -static void send(char *i, size_t rawlen, char *s, char *b) -{ - NotifyIcon *icon; - - if (rawlen > 0) - icon = notify_icon_new_from_data(rawlen, i); - else - icon = notify_icon_new_from_uri(i); - - n = notify_send_notification(NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - s, b, - icon, - TRUE, 0, - NULL, // no hints - NULL, // no user data - 0); - - if (!n) { - fprintf(stderr, "failed to send notification\n"); - exit(1); - } - - notify_icon_destroy(icon); -} +NotifyNotification *n; int -main(int argc, char **argv) +main(int argc, char *argv[]) { - loop = g_main_loop_new(NULL, FALSE); + char file[PATH_MAX]; + int size; + char *uri; + GdkPixbuf *icon; + GtkWidget *helper; - if (!notify_glib_init("Images Test", NULL)) + gtk_init(&argc, &argv); + + if (!notify_init("Images Test")) exit(1); - /* - * These images exist on fedora core 2 workstation profile. - * Might not on yours - */ + n = notify_notification_new("Icon Test", "Testing stock icon", + "stock_help", NULL); - send("gnome-starthere", - 0, - "Welcome to Linux!", - "This is your first login. To begin exploring the system, click on 'Start Here', 'Computer' or 'Applications'"); + notify_notification_set_hint_int32(n, "x", 300); + notify_notification_set_hint_int32(n, "y", 24); + notify_notification_set_timeout(n, NOTIFY_TIMEOUT_NEVER); - char file[1024]; - readlink("/proc/self/exe", file, sizeof(file)); - *strrchr(file, '/') = '\0'; - strcat(file, "/../applet-critical.png"); - - printf("sending %s\n", file); - - send(file, - 0, - "Alert!", - "Warning!"); - - - struct stat buf; - if (stat(file, &buf) == -1) + if (!notify_notification_show(n, NULL)) { - fprintf(stderr, "could not stat %s: %s", file, strerror(errno)); - exit(1); + fprintf(stderr, "failed to send notification\n"); + return 1; } - int fd = open(file, O_RDONLY); + g_usleep(5000000); // 5 seconds - void *pngbase = mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + size = readlink("/proc/self/cwd", file, PATH_MAX - 1); + file[size] = '\0'; + uri = g_strdup_printf("file://%s/%s", file, "applet-critical.png"); - close(fd); + printf("sending %s\n", uri); - send(pngbase, - buf.st_size, - "Raw image test", - "This is an image marshalling test"); + notify_notification_update(n, "Alert!", "Testing URI icons", uri); + + if (!notify_notification_show(n, NULL)) + { + fprintf(stderr, "failed to send notification\n"); + return 1; + } + + g_usleep(5000000); // 5 seconds + + notify_notification_update(n, "Raw image test", + "Testing sending raw pixbufs", NULL); + + /* + * This is just a hack to get a stock icon's pixbuf in a realworld app + * if you were sending bitmapped data you would know the file location + * and open it up with a method that could generate a bmp for you + */ + helper = gtk_button_new(); + icon = gtk_widget_render_icon(helper, + GTK_STOCK_DIALOG_QUESTION, + GTK_ICON_SIZE_DIALOG, + ""); + + notify_notification_set_icon_data_from_pixbuf(n, icon); + + if (!notify_notification_show(n, NULL)) + { + fprintf(stderr, "failed to send notification\n"); + return 1; + } + + + gtk_widget_destroy(helper); + g_object_unref(G_OBJECT(icon)); + + g_usleep(5000000); // 5 seconds return 0; } diff --git a/tests/test-markup.c b/tests/test-markup.c index bd9aee6..1471bde 100644 --- a/tests/test-markup.c +++ b/tests/test-markup.c @@ -24,22 +24,17 @@ #include int main() { + NotifyNotification *n; + notify_init("Markup"); - NotifyHandle *n = notify_send_notification( - NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - "Summary", - "Some bold, underlined, italic, " - "linked text", - NULL, // no icon - TRUE, 0, - NULL, // no hints - NULL, // no user data - 0); // no actions + n = notify_notification_new ("Summary", + "Some bold, underlined, italic, " + "linked text", + NULL, NULL); + notify_notification_set_timeout (n, 3000); //3 seconds - if (!n) { + if (!notify_notification_show (n, NULL)) { fprintf(stderr, "failed to send notification\n"); return 1; } diff --git a/tests/test-multi-actions.c b/tests/test-multi-actions.c index dd82543..c5da12f 100644 --- a/tests/test-multi-actions.c +++ b/tests/test-multi-actions.c @@ -1,12 +1,12 @@ /* * @file tests/test-multi-actions.c Unit test: multiple actions * - * @Copyright (C) 2004 Mike Hearn + * @Copyright(C) 2004 Mike Hearn * * 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.1 of the License, or (at your option) any later version. + * version 2.1 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 @@ -23,62 +23,86 @@ #include #include #include -#include +#include -#define DBUS_API_SUBJECT_TO_CHANGE 1 +#define DBUS_API_SUBJECT_TO_CHANGE #include #include #include #include -GMainLoop *loop; -NotifyHandle *n; +static GMainLoop *loop; -static void callback(NotifyHandle *handle, guint32 uid, void *user_data) +static void +help_callback(NotifyNotification *n, const char *action) { - char *s = NULL; + g_assert(action != NULL); + g_assert(strcmp(action, "help") == 0); - assert( uid >= 0 && uid <= 2 ); + printf("You clicked Help\n"); - switch (uid) - { - case 0: s = "the notification"; break; - case 1: s = "Empty Trash"; break; - case 2: s = "Help Me"; break; - } - - printf("You clicked %s\n", s); - - notify_close(n); + notify_notification_close(n, NULL); g_main_loop_quit(loop); } +static void +ignore_callback(NotifyNotification *n, const char *action) +{ + g_assert(action != NULL); + g_assert(strcmp(action, "ignore") == 0); + + printf("You clicked Ignore\n"); + + notify_notification_close(n, NULL); + + g_main_loop_quit(loop); +} + +static void +empty_callback(NotifyNotification *n, const char *action) +{ + g_assert(action != NULL); + g_assert(strcmp(action, "empty") == 0); + + printf("You clicked Empty Trash\n"); + + notify_notification_close(n, NULL); + + g_main_loop_quit(loop); +} + + int main(int argc, char **argv) { - loop = g_main_loop_new(NULL, FALSE); + NotifyNotification *n; + DBusConnection *conn; - if (!notify_glib_init("Multi Action Test", NULL)) + if (!notify_init("Multi Action Test")) exit(1); - n = notify_send_notification(NULL, // replaces nothing - "device", - NOTIFY_URGENCY_NORMAL, - "Low disk space", - "You can free up some disk space by " - "emptying the trash can.", - NULL, // no icon - FALSE, 0, // does not expire - NULL, // no hints - NULL, // no user data - 3, // 3 actions - 0, "default", callback, - 1, "Empty Trash", callback, - 2, "Help Me", callback ); + conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); + loop = g_main_loop_new(NULL, FALSE); - if (!n) { + dbus_connection_setup_with_g_main(conn, NULL); + + n = notify_notification_new("Low disk space", + "You can free up some disk space by " + "emptying the trash can.", + NULL, NULL); + notify_notification_set_timeout(n, NOTIFY_TIMEOUT_NEVER); + notify_notification_add_action(n, "help", "Help", + (NotifyActionCallback)help_callback); + notify_notification_add_action(n, "ignore", "Ignore", + (NotifyActionCallback)ignore_callback); + notify_notification_add_action(n, "empty", "Empty Trash", + (NotifyActionCallback)empty_callback); + notify_notification_set_category(n, "device"); + + if (!notify_notification_show(n, NULL)) + { fprintf(stderr, "failed to send notification\n"); return 1; } diff --git a/tests/test-replace-widget.c b/tests/test-replace-widget.c new file mode 100644 index 0000000..f041afe --- /dev/null +++ b/tests/test-replace-widget.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +static int count = 0; + +static void +on_exposed(GtkWidget *widget, GdkEventExpose *ev, void *user_data) +{ + NotifyNotification *n = NOTIFY_NOTIFICATION(user_data); + + g_signal_handlers_disconnect_by_func(widget, on_exposed, user_data); + + notify_notification_show(n, NULL); +} + +static void +on_clicked(GtkButton *button, void *user_data) +{ + gchar *buf; + NotifyNotification *n = NOTIFY_NOTIFICATION(user_data); + + count++; + buf = g_strdup_printf("You clicked the button %i times", count); + notify_notification_update(n, "Widget Attachment Test", buf, NULL); + g_free(buf); + + notify_notification_show(n, NULL); +} + +int +main(int argc, char *argv[]) +{ + NotifyNotification *n; + GtkWidget *window; + GtkWidget *button; + + gtk_init(&argc, &argv); + notify_init("Replace Test"); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + button = gtk_button_new_with_label("click here to change notification"); + gtk_container_add(GTK_CONTAINER(window), button); + + gtk_widget_show_all(window); + + n = notify_notification_new("Widget Attachment Test", + "Button has not been clicked yet", + NULL, //no icon + button); //attach to button + + notify_notification_set_timeout(n, 0); //don't timeout + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(on_clicked), n); + g_signal_connect_after(G_OBJECT(button), "expose-event", + G_CALLBACK(on_exposed), n); + + gtk_main(); + + return 0; +} diff --git a/tests/test-replace.c b/tests/test-replace.c index dc47f3d..ec8cbe5 100644 --- a/tests/test-replace.c +++ b/tests/test-replace.c @@ -1,52 +1,40 @@ -/* - * @file tests/test-default-action.c Unit test: atomic replacements - * - * @Copyright (C) 2004 Mike Hearn - * - * 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.1 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 - * Lesser 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - #include #include #include +#include int main() { - notify_init("Replace Test"); + NotifyNotification *n; + GError *error; + error = NULL; - NotifyHandle *n = notify_send_notification(NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - "Summary", "Content", - NULL, // no icon - FALSE, 0, // does not expire - NULL, // no hints - NULL, // no user data - 0); // no actions + g_type_init (); - if (!n) { - fprintf(stderr, "failed to send notification\n"); - return 1; - } + notify_init("Replace Test"); + + n = notify_notification_new ("Summary", "First message", + NULL, //no icon + NULL); //don't attach to widget + + notify_notification_set_timeout (n, 0); //don't timeout + + if (!notify_notification_show (n, &error)) { + fprintf(stderr, "failed to send notification: %s\n", error->message); + g_error_free (error); + return 1; + } - sleep(5); + sleep(3); - notify_send_notification(n, NULL, NOTIFY_URGENCY_NORMAL, - "Second Summary", "Second Content", - NULL, TRUE, 5, NULL, NULL, 0); + notify_notification_update (n, "Second Summary", + "First mesage was replaced", NULL); - return 0; + if (!notify_notification_show (n, &error)) { + fprintf(stderr, "failed to send notification: %s\n", error->message); + g_error_free (error); + return 1; + } + + return 0; } diff --git a/tests/test-xy-stress.c b/tests/test-xy-stress.c index 718c315..333e908 100644 --- a/tests/test-xy-stress.c +++ b/tests/test-xy-stress.c @@ -1,12 +1,12 @@ /* * @file tests/test-xy.c Unit test: X, Y hints * - * @Copyright (C) 2005 Christian Hammond + * @Copyright(C) 2005 Christian Hammond * * 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.1 of the License, or (at your option) any later version. + * version 2.1 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 @@ -24,62 +24,77 @@ #include #include +#include +#include +#include + +static void +_handle_closed(GObject *obj) +{ + g_message("closing"); + g_object_unref(obj); +} + static void emit_notification(int x, int y) { - NotifyHints *hints; - static char buffer[BUFSIZ]; + char *buffer; + NotifyNotification *n; - hints = notify_hints_new(); - notify_hints_set_int(hints, "x", x); - notify_hints_set_int(hints, "y", y); + buffer = g_strdup_printf("This notification should point to %d, %d.", + x, y); - g_snprintf(buffer, sizeof(buffer), - "This notification should point to %d, %d.", x, y); + n = notify_notification_new("X, Y Test", buffer, NULL, NULL); + g_free(buffer); - NotifyHandle *n = notify_send_notification( - NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - "X, Y Test", - buffer, - NULL, // no icon - TRUE, 0, - hints, - NULL, // no user data - 0); // no actions + notify_notification_set_hint_int32(n, "x", x); + notify_notification_set_hint_int32(n, "y", y); - if (!n) { + g_signal_connect(G_OBJECT(n), "closed", + G_CALLBACK(_handle_closed), NULL); + + if (!notify_notification_show(n, NULL)) fprintf(stderr, "failed to send notification\n"); - } } -int -main(int argc, char **argv) +static gboolean +_popup_random_bubble(gpointer unused) { GdkDisplay *display; GdkScreen *screen; + int screen_x2, screen_y2; - - gdk_init(&argc, &argv); - - notify_init("XY"); + int x, y; display = gdk_display_get_default(); screen = gdk_display_get_default_screen(display); screen_x2 = gdk_screen_get_width(screen) - 1; screen_y2 = gdk_screen_get_height(screen) - 1; - emit_notification(0, 0); - emit_notification(screen_x2, 0); - emit_notification(5, 150); - emit_notification(screen_x2 - 5, 150); - emit_notification(0, screen_y2 / 2); - emit_notification(screen_x2, screen_y2 / 2); - emit_notification(5, screen_y2 - 150); - emit_notification(screen_x2 - 5, screen_y2 - 150); - emit_notification(0, screen_y2); - emit_notification(screen_x2, screen_y2); + x = g_random_int_range(0, screen_x2); + y = g_random_int_range(0, screen_y2); + emit_notification(x, y); + + return TRUE; +} + +int +main(int argc, char **argv) +{ + GMainLoop *loop; + DBusConnection *conn; + + gdk_init(&argc, &argv); + + notify_init("XY"); + + conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); + dbus_connection_setup_with_g_main(conn, NULL); + + g_timeout_add(1000, _popup_random_bubble, NULL); + + loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); return 0; } diff --git a/tests/test-xy.c b/tests/test-xy.c index ce36aa8..612a04f 100644 --- a/tests/test-xy.c +++ b/tests/test-xy.c @@ -24,27 +24,18 @@ #include int main() { - NotifyHints *hints; + NotifyNotification *n; notify_init("XY"); - hints = notify_hints_new(); - notify_hints_set_int(hints, "x", 150); - notify_hints_set_int(hints, "y", 10); + n = notify_notification_new ("X, Y Test", + "This notification should point to 150, 10", + NULL, NULL); - NotifyHandle *n = notify_send_notification( - NULL, // replaces nothing - NULL, - NOTIFY_URGENCY_NORMAL, - "X, Y Test", - "This notification should point to 150, 10.", - NULL, // no icon - TRUE, 0, - hints, - NULL, // no user data - 0); // no actions + notify_notification_set_hint_int32 (n, "x", 30); + notify_notification_set_hint_int32 (n, "y", 10); - if (!n) { + if (!notify_notification_show (n, NULL)) { fprintf(stderr, "failed to send notification\n"); return 1; } diff --git a/tools/Makefile.am b/tools/Makefile.am index 41b1b69..8edb788 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -8,4 +8,5 @@ common_ldflags = \ notify_send_SOURCES = notify-send.c notify_send_LDADD = $(common_ldflags) -INCLUDES = $(PACKAGE_CFLAGS) -I$(top_srcdir) +INCLUDES = $(PACKAGE_CFLAGS) \ + -I$(top_srcdir) diff --git a/tools/notify-send.c b/tools/notify-send.c index 8e0748a..2a459ae 100644 --- a/tools/notify-send.c +++ b/tools/notify-send.c @@ -24,6 +24,7 @@ #include #include #include +#include #define N_(x) (x) @@ -34,14 +35,15 @@ main(int argc, const char **argv) const gchar *body = NULL; const gchar *type = NULL; char *urgency_str = NULL; - gchar *icons = NULL; gchar *icon_str = NULL; - NotifyIcon *icon = NULL; + gchar *icons = NULL; NotifyUrgency urgency = NOTIFY_URGENCY_NORMAL; - time_t expire_timeout = 0; + long expire_timeout = NOTIFY_TIMEOUT_DEFAULT; char ch; poptContext opt_ctx; const char **args; + NotifyNotification *notify; + struct poptOption options[] = { { "urgency", 'u', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &urgency_str, @@ -57,11 +59,13 @@ main(int argc, const char **argv) N_("ICON1,ICON2,...") }, { "type", 't', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &type, 0, N_("Specifies the notification type."), - N_("TYPE") }, + N_("ICON1,ICON2,...") }, POPT_AUTOHELP POPT_TABLEEND }; + g_type_init (); + opt_ctx = poptGetContext("notify-send", argc, argv, options, 0); poptSetOtherOptionHelp(opt_ctx, "[OPTIONS]* [body]"); @@ -74,7 +78,8 @@ main(int argc, const char **argv) exit(1); } - summary = args[0]; + if (args[0] != NULL) + summary = args[0]; if (summary == NULL) { @@ -82,12 +87,15 @@ main(int argc, const char **argv) exit(1); } - body = args[1]; - - if (body != NULL && args[2] != NULL) + if (args[1] != NULL) { - poptPrintUsage(opt_ctx, stderr, 0); - exit(1); + body = args[1]; + + if (args[2] != NULL) + { + poptPrintUsage(opt_ctx, stderr, 0); + exit(1); + } } if (icons != NULL) @@ -100,7 +108,6 @@ main(int argc, const char **argv) icon_str = icons; - icon = notify_icon_new_from_uri(icon_str); } if (urgency_str != NULL) @@ -121,11 +128,13 @@ main(int argc, const char **argv) if (!notify_init("notify-send")) exit(1); - notify_send_notification(NULL, type, urgency, summary, body, icon, - TRUE, expire_timeout, NULL, NULL, 0); - if (icon != NULL) - notify_icon_destroy(icon); + notify = notify_notification_new (summary, body, icon_str, NULL); + notify_notification_set_category (notify, type); + notify_notification_set_urgency (notify, urgency); + notify_notification_set_timeout (notify, expire_timeout); + + notify_notification_show_and_forget (notify, NULL); poptFreeContext(opt_ctx); notify_uninit();