2
// FIXME: Width and Height should automatically take shadow into account
3
public const int WIDTH = 320; //300 + (2 * 10)px shadow
4
public const int HEIGHT = 76; //56 + (2 * 10)px shadow
5
public const int SHADOW = 10;
6
public const int EXPIRE = 7000; //in ms
9
[DBus (name="org.freedesktop.Notifications")]
10
public class NotifyServer : Object {
12
internal int id_counter = 0;
14
public Gtk.Window main_window;
15
public Clutter.Box box;
17
public NotifyServer () {
18
var clutter = new GtkClutter.Embed ();
20
this.main_window = new Gtk.Window (Gtk.WindowType.POPUP);
21
this.main_window.set_visual (Gdk.Screen.get_default ().get_rgba_visual ());
22
this.main_window.type_hint = Gdk.WindowTypeHint.NOTIFICATION;
23
this.main_window.stick ();
24
this.main_window.set_keep_above (true);
25
this.main_window.focus_on_map = false;
26
this.main_window.accept_focus = false;
27
this.main_window.skip_pager_hint = true;
28
this.main_window.skip_taskbar_hint = true;
29
this.main_window.focus_visible = false;
30
this.box = new Clutter.Box (new Clutter.BoxLayout ());
32
((Clutter.BoxLayout)this.box.layout_manager).vertical = true;
33
this.box.add_constraint (new Clutter.BindConstraint (clutter.get_stage (), Clutter.BindCoordinate.WIDTH, 0));
34
this.box.add_constraint (new Clutter.BindConstraint (clutter.get_stage (), Clutter.BindCoordinate.HEIGHT, 0));
36
((Clutter.Stage)clutter.get_stage ()).color = {0, 0, 0, 0};
37
((Clutter.Stage)clutter.get_stage ()).use_alpha = true;
38
clutter.get_stage ().add_child (box);
40
this.main_window.add (clutter);
41
this.main_window.set_size_request (WIDTH, Gdk.Screen.get_default ().height ());
42
this.main_window.show_all ();
44
this.update_input_shape ();
48
Gdk.Screen.get_default ().monitors_changed.connect ( () => {
53
public void reposition () {
55
Gdk.Screen.get_default ().get_monitor_geometry (Gdk.Screen.get_default ().get_primary_monitor (), out rect);
56
main_window.move (rect.x + rect.width - WIDTH, rect.y + 30);
59
[DBus (visibility=false)]
60
public void update_input_shape () {
61
this.main_window.get_window ().input_shape_combine_region (new Cairo.Region.rectangle
62
({0, 0, WIDTH, this.box.get_n_children () * HEIGHT}), 0, 0);
65
public void close_notification (uint32 id) {
68
public string [] get_capabilities () {
69
return {"body", "body-markup"};
71
public void get_server_information (out string name, out string vendor, out string version, out string spec_version) {
72
name = "pantheon-notify";
73
vendor = "ElementaryOS";
77
public new uint32 notify (string app_name, uint32 replaces_id, string app_icon, string summary,
78
string body, string [] actions, HashTable<string, Variant> hints, int32 expire_timeout, BusName name) {
80
debug ("Bus Name: %s", name);
83
if (hints.contains ("urgency"))
84
urgency = hints.lookup ("urgency").get_byte ();
85
message ("New notification, ID: %i, app: %s, icon: %s, priority: %i\n", id_counter + 1,
86
app_name, app_icon, urgency);
89
if (replaces_id != 0) { //replace the notification
91
bool found_one = false;
92
for (var i=0;i<this.box.get_children ().length ();i++) {
93
if (((Notification)this.box.get_children ().nth_data (i)).id == replaces_id) {
95
((Notification)this.box.get_children ().nth_data (i)).set_text (summary, body);
96
((Notification)this.box.get_children ().nth_data (i)).set_pix (hints, app_name, app_icon);
97
((Notification)this.box.get_children ().nth_data (i)).updated ();
104
if (!found_one) //just add a normal one
105
this.box.insert_child_at_index (new Notification (summary, body, app_icon,
106
expire_timeout, app_name, this, hints, replaces_id, actions), 0);
108
this.update_input_shape ();
113
var notification = new Notification (summary, body, app_icon,
114
expire_timeout, app_name, this, hints, id_counter+1, actions);
115
this.box.insert_child_at_index (notification, 0);
117
this.update_input_shape ();
118
return id_counter ++;
123
public static void main (string [] args) {
124
var cl = GtkClutter.init (ref args);
125
if (cl != Clutter.InitError.SUCCESS)
126
error ("Clutter failed to initialize");
128
Granite.Services.Logger.initialize ("pantheon-notifications");
129
Granite.Services.Logger.DisplayLevel = Granite.Services.LogLevel.DEBUG;
131
var n = new NotifyServer ();
134
"org.freedesktop.Notifications",
135
BusNameOwnerFlags.NONE,
138
con.register_object ("/org/freedesktop/Notifications", n);
139
} catch (Error e) { warning (e.message); }
142
(con, name)=> warning ("Could not aquire bus %s", name));