diff --git a/src/gs-listener-dbus.c b/src/gs-listener-dbus.c
index 85a0328..e7690a5 100644
--- a/src/gs-listener-dbus.c
+++ b/src/gs-listener-dbus.c
@@ -108,6 +108,7 @@ enum {
         SIMULATE_USER_ACTIVITY,
         ACTIVE_CHANGED,
         THROTTLE_CHANGED,
+        SHOW_MESSAGE,
         LAST_SIGNAL
 };
 
@@ -1223,6 +1224,52 @@ listener_get_active_time (GSListener     *listener,
 }
 
 static DBusHandlerResult
+listener_show_message (GSListener     *listener,
+                       DBusConnection *connection,
+                       DBusMessage    *message)
+{
+        DBusMessageIter iter;
+        DBusMessage    *reply;
+        DBusError       error;
+
+        reply = dbus_message_new_method_return (message);
+
+        dbus_message_iter_init_append (reply, &iter);
+
+        if (reply == NULL) {
+                g_error ("No memory");
+        }
+
+        if (listener->priv->active) {
+                char *summary;
+                char *body;
+                char *icon;
+
+                /* if we're not active we ignore the request */
+
+                dbus_error_init (&error);
+                if (! dbus_message_get_args (message, &error,
+                                             DBUS_TYPE_STRING, &summary,
+                                             DBUS_TYPE_STRING, &body,
+                                             DBUS_TYPE_STRING, &icon,
+                                             DBUS_TYPE_INVALID)) {
+                        raise_syntax (connection, message, "ShowMessage");
+                        return DBUS_HANDLER_RESULT_HANDLED;
+                }
+
+                g_signal_emit (listener, signals [SHOW_MESSAGE], 0, summary, body, icon);
+        }
+
+        if (! dbus_connection_send (connection, reply, NULL)) {
+                g_error ("No memory");
+        }
+
+        dbus_message_unref (reply);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
 do_introspect (DBusConnection *connection,
                DBusMessage    *message,
                dbus_bool_t     local_interface)
@@ -1278,6 +1325,11 @@ do_introspect (DBusConnection *connection,
                                "    <method name=\"SetActive\">\n"
                                "      <arg name=\"value\" direction=\"in\" type=\"b\"/>\n"
                                "    </method>\n"
+                               "    <method name=\"ShowMessage\">\n"
+                               "      <arg name=\"summary\" direction=\"in\" type=\"s\"/>\n"
+                               "      <arg name=\"body\" direction=\"in\" type=\"s\"/>\n"
+                               "      <arg name=\"icon\" direction=\"in\" type=\"s\"/>\n"
+                               "    </method>\n"
                                "    <signal name=\"ActiveChanged\">\n"
                                "      <arg name=\"new_value\" type=\"b\"/>\n"
                                "    </signal>\n"
@@ -1362,6 +1414,9 @@ listener_dbus_handle_session_message (DBusConnection *connection,
         if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetActiveTime")) {
                 return listener_get_active_time (listener, connection, message);
         }
+        if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "ShowMessage")) {
+                return listener_show_message (listener, connection, message);
+        }
         if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "SimulateUserActivity")) {
                 g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
                 return DBUS_HANDLER_RESULT_HANDLED;
@@ -1796,6 +1851,19 @@ gs_listener_class_init (GSListenerClass *klass)
                               G_TYPE_NONE,
                               1,
                               G_TYPE_BOOLEAN);
+        signals [SHOW_MESSAGE] =
+                g_signal_new ("show-message",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GSListenerClass, show_message),
+                              NULL,
+                              NULL,
+                              gs_marshal_VOID__STRING_STRING_STRING,
+                              G_TYPE_NONE,
+                              3,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING);
 
         g_object_class_install_property (object_class,
                                          PROP_ACTIVE,
diff --git a/src/gs-listener-dbus.h b/src/gs-listener-dbus.h
index a788e40..c57f367 100644
--- a/src/gs-listener-dbus.h
+++ b/src/gs-listener-dbus.h
@@ -52,6 +52,10 @@ typedef struct
                                                       gboolean    active);
         void            (* throttle_changed)         (GSListener *listener,
                                                       gboolean    throttled);
+        void            (* show_message)             (GSListener *listener,
+                                                      const char *summary,
+                                                      const char *body,
+                                                      const char *icon);
 
 } GSListenerClass;
 
diff --git a/src/gs-manager.c b/src/gs-manager.c
index e8ffc0b..1bc018b 100644
--- a/src/gs-manager.c
+++ b/src/gs-manager.c
@@ -1174,6 +1174,23 @@ find_window_at_pointer (GSManager *manager)
         return window;
 }
 
+void
+gs_manager_show_message (GSManager  *manager,
+                         const char *summary,
+                         const char *body,
+                         const char *icon)
+{
+        GSWindow *window;
+
+        g_return_if_fail (GS_IS_MANAGER (manager));
+
+        /* Find the GSWindow that contains the pointer */
+        window = find_window_at_pointer (manager);
+        gs_window_show_message (window, summary, body, icon);
+
+        gs_manager_request_unlock (manager);
+}
+
 static gboolean
 manager_maybe_grab_window (GSManager *manager,
                            GSWindow  *window)
diff --git a/src/gs-manager.h b/src/gs-manager.h
index b0493ab..3a3e349 100644
--- a/src/gs-manager.h
+++ b/src/gs-manager.h
@@ -95,6 +95,10 @@ void        gs_manager_set_themes           (GSManager  *manager,
                                              GSList     *themes);
 void        gs_manager_set_mode             (GSManager  *manager,
                                              GSSaverMode mode);
+void        gs_manager_show_message         (GSManager  *manager,
+                                             const char *summary,
+                                             const char *body,
+                                             const char *icon);
 gboolean    gs_manager_request_unlock       (GSManager  *manager);
 void        gs_manager_cancel_unlock_request (GSManager *manager);
 
diff --git a/src/gs-marshal.list b/src/gs-marshal.list
index 228ba92..9a4d620 100644
--- a/src/gs-marshal.list
+++ b/src/gs-marshal.list
@@ -1,3 +1,4 @@
 BOOLEAN:VOID
 BOOLEAN:INT
 BOOLEAN:BOOLEAN
+VOID:STRING,STRING,STRING
diff --git a/src/gs-monitor.c b/src/gs-monitor.c
index 0fd4402..640ee74 100644
--- a/src/gs-monitor.c
+++ b/src/gs-monitor.c
@@ -220,6 +220,19 @@ listener_cycle_cb (GSListener *listener,
         gs_manager_cycle (monitor->priv->manager);
 }
 
+static void
+listener_show_message_cb (GSListener *listener,
+                          const char *summary,
+                          const char *body,
+                          const char *icon,
+                          GSMonitor  *monitor)
+{
+        gs_manager_show_message (monitor->priv->manager,
+                                 summary,
+                                 body,
+                                 icon);
+}
+
 static gboolean
 listener_active_changed_cb (GSListener *listener,
                             gboolean    active,
@@ -334,6 +347,7 @@ disconnect_listener_signals (GSMonitor *monitor)
         g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_active_changed_cb, monitor);
         g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_throttle_changed_cb, monitor);
         g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_simulate_user_activity_cb, monitor);
+        g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_show_message_cb, monitor);
 }
 
 static void
@@ -351,6 +365,8 @@ connect_listener_signals (GSMonitor *monitor)
                           G_CALLBACK (listener_throttle_changed_cb), monitor);
         g_signal_connect (monitor->priv->listener, "simulate-user-activity",
                           G_CALLBACK (listener_simulate_user_activity_cb), monitor);
+        g_signal_connect (monitor->priv->listener, "show-message",
+                          G_CALLBACK (listener_show_message_cb), monitor);
 }
 
 static void
diff --git a/src/gs-window-x11.c b/src/gs-window-x11.c
index 2a74e10..5e0ebe5 100644
--- a/src/gs-window-x11.c
+++ b/src/gs-window-x11.c
@@ -52,6 +52,7 @@ enum {
 };
 
 #define MAX_QUEUED_EVENTS 16
+#define INFO_BAR_SECONDS 30
 
 #define GS_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_WINDOW, GSWindowPrivate))
 
@@ -77,6 +78,8 @@ struct GSWindowPrivate
         GtkWidget *lock_box;
         GtkWidget *lock_socket;
         GtkWidget *keyboard_socket;
+        GtkWidget *info_bar;
+        GtkWidget *info_content;
 
         GdkPixmap *background_pixmap;
 
@@ -87,6 +90,7 @@ struct GSWindowPrivate
         guint      dialog_response_signal_id;
 
         guint      watchdog_timer_id;
+        guint      info_bar_timer_id;
 
         gint       lock_pid;
         gint       lock_watch_id;
@@ -896,6 +900,96 @@ gs_window_real_show (GtkWidget *widget)
         gdk_window_add_filter (NULL, (GdkFilterFunc)xevent_filter, window);
 }
 
+static void
+set_info_text_and_icon (GSWindow   *window,
+                        const char *icon_stock_id,
+                        const char *primary_text,
+                        const char *secondary_text)
+{
+        GtkWidget *content_area;
+        GtkWidget *hbox_content;
+        GtkWidget *image;
+        GtkWidget *vbox;
+        gchar *primary_markup;
+        gchar *secondary_markup;
+        GtkWidget *primary_label;
+        GtkWidget *secondary_label;
+
+        hbox_content = gtk_hbox_new (FALSE, 8);
+        gtk_widget_show (hbox_content);
+
+        image = gtk_image_new_from_stock (icon_stock_id, GTK_ICON_SIZE_DIALOG);
+        gtk_widget_show (image);
+        gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0);
+        gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+
+        vbox = gtk_vbox_new (FALSE, 6);
+        gtk_widget_show (vbox);
+        gtk_box_pack_start (GTK_BOX (hbox_content), vbox, FALSE, FALSE, 0);
+
+        primary_markup = g_strdup_printf ("<b>%s</b>", primary_text);
+        primary_label = gtk_label_new (primary_markup);
+        g_free (primary_markup);
+        gtk_widget_show (primary_label);
+        gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0);
+        gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
+        gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
+        gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
+
+        if (secondary_text != NULL) {
+                secondary_markup = g_strdup_printf ("<small>%s</small>",
+                                                    secondary_text);
+                secondary_label = gtk_label_new (secondary_markup);
+                g_free (secondary_markup);
+                gtk_widget_show (secondary_label);
+                gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0);
+                gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE);
+                gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
+                gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5);
+        }
+
+        /* remove old content */
+        content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (window->priv->info_bar));
+        if (window->priv->info_content != NULL) {
+                gtk_container_remove (GTK_CONTAINER (content_area), window->priv->info_content);
+        }
+        gtk_box_pack_start (GTK_BOX (content_area),
+                            hbox_content,
+                            TRUE, FALSE, 0);
+        window->priv->info_content = hbox_content;
+}
+
+static gboolean
+info_bar_timeout (GSWindow *window)
+{
+        window->priv->info_bar_timer_id = 0;
+        gtk_widget_hide (window->priv->info_bar);
+        return FALSE;
+}
+
+void
+gs_window_show_message (GSWindow   *window,
+                        const char *summary,
+                        const char *body,
+                        const char *icon)
+{
+        g_return_if_fail (GS_IS_WINDOW (window));
+
+        set_info_text_and_icon (window,
+                                icon,
+                                summary,
+                                body);
+        gtk_widget_show (window->priv->info_bar);
+
+        if (window->priv->info_bar_timer_id > 0) {
+                g_source_remove (window->priv->info_bar_timer_id);
+        }
+
+        window->priv->info_bar_timer_id = g_timeout_add_seconds (INFO_BAR_SECONDS,
+                                                                 (GSourceFunc)info_bar_timeout,
+                                                                 window);
+}
+
 void
 gs_window_show (GSWindow *window)
 {
@@ -2218,6 +2312,14 @@ gs_window_class_init (GSWindowClass *klass)
 }
 
 static void
+create_info_bar (GSWindow *window)
+{
+        window->priv->info_bar = gtk_info_bar_new ();
+        gtk_widget_set_no_show_all (window->priv->info_bar, TRUE);
+        gtk_box_pack_end (GTK_BOX (window->priv->vbox), window->priv->info_bar, FALSE, FALSE, 0);
+}
+
+static void
 gs_window_init (GSWindow *window)
 {
         window->priv = GS_WINDOW_GET_PRIVATE (window);
@@ -2258,6 +2360,7 @@ gs_window_init (GSWindow *window)
         window->priv->drawing_area = gtk_drawing_area_new ();
         gtk_widget_show (window->priv->drawing_area);
         gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->drawing_area, TRUE, TRUE, 0);
+        create_info_bar (window);
 
         force_no_pixmap_background (window->priv->drawing_area);
 }
@@ -2290,6 +2393,10 @@ gs_window_finalize (GObject *object)
         g_free (window->priv->logout_command);
         g_free (window->priv->keyboard_command);
 
+        if (window->priv->info_bar_timer_id > 0) {
+                g_source_remove (window->priv->info_bar_timer_id);
+        }
+
         remove_watchdog_timer (window);
         remove_popup_dialog_idle (window);
 
diff --git a/src/gs-window.h b/src/gs-window.h
index 6440a6d..fc2287e 100644
--- a/src/gs-window.h
+++ b/src/gs-window.h
@@ -83,6 +83,10 @@ void        gs_window_set_logout_command (GSWindow   *window,
                                           const char *command);
 void        gs_window_set_status_message   (GSWindow   *window,
                                             const char *status_message);
+void        gs_window_show_message         (GSWindow   *window,
+                                            const char *summary,
+                                            const char *body,
+                                            const char *icon);
 
 void        gs_window_request_unlock     (GSWindow  *window);
 void        gs_window_cancel_unlock_request (GSWindow  *window);
