~elementary-pantheon/pantheon-notify/pantheon-notify-trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// FIXME: Width and Height should automatically take shadow into account
public const int WIDTH = 320; //300 + (2 * 10)px shadow
public const int HEIGHT = 76; //56 + (2 * 10)px shadow
public const int SHADOW = 10;
public const int EXPIRE = 7000; //in ms


[DBus (name="org.freedesktop.Notifications")]
public class NotifyServer : Object {
    
    internal int id_counter = 0;
    
    public Granite.Widgets.CompositedWindow main_window;
    public Clutter.Box box;
    
    public NotifyServer () {
        var clutter = new GtkClutter.Embed ();
        
        this.main_window = new Granite.Widgets.CompositedWindow ();
        this.main_window.type_hint = Gdk.WindowTypeHint.NOTIFICATION;
        this.main_window.move (Gdk.Screen.get_default ().width ()-100, 0);
        this.main_window.stick ();
        this.main_window.set_keep_above (true);
        this.main_window.focus_on_map = false;
        this.main_window.accept_focus = false;
        this.main_window.skip_pager_hint = true;
        this.main_window.skip_taskbar_hint = true;
        this.main_window.focus_visible = false;
        this.box = new Clutter.Box (new Clutter.BoxLayout ());
        
        ((Clutter.BoxLayout)this.box.layout_manager).vertical = true;
        this.box.add_constraint (new Clutter.BindConstraint (clutter.get_stage (), Clutter.BindCoordinate.WIDTH, 0));
        this.box.add_constraint (new Clutter.BindConstraint (clutter.get_stage (), Clutter.BindCoordinate.HEIGHT, 0));
        
        ((Clutter.Stage)clutter.get_stage ()).color = {0, 0, 0, 0};
        ((Clutter.Stage)clutter.get_stage ()).use_alpha = true;
        clutter.get_stage ().add_child (box);
        
        this.main_window.add (clutter);
        this.main_window.set_size_request (WIDTH, Gdk.Screen.get_default ().height ());
        this.main_window.show_all ();
        
        this.update_input_shape ();
        
        Gdk.Screen.get_default ().size_changed.connect ( () => {
            this.main_window.move (Gdk.Screen.get_default ().width ()-100, 0);
        });
    }
    
    [DBus (visibility=false)]
    public void update_input_shape () {
        this.main_window.get_window ().input_shape_combine_region (new Cairo.Region.rectangle 
            ({0, 0, WIDTH, this.box.get_n_children () * HEIGHT}), 0, 0);
    }
    
    public void close_notification (uint32 id) {
        
    }
    public string [] get_capabilities () {
        return {"body", "body-markup"};
    }
    public void get_server_information (out string name, out string vendor, out string version, out string spec_version) {
        name = "pantheon-notify";
        vendor = "ElementaryOS";
        version = "0.1";
        spec_version = "1.1";
    }
    public new uint32 notify (string app_name, uint32 replaces_id, string app_icon, string summary, 
        string body, string [] actions, HashTable<string, Variant> hints, int32 expire_timeout, BusName name) {
        
        debug ("Bus Name: %s", name);
        
        int urgency = 1;
        if (hints.contains ("urgency"))
            urgency = hints.lookup ("urgency").get_byte ();
        message ("New notification, ID: %i, app: %s, icon: %s, priority: %i\n", id_counter + 1, 
                app_name, app_icon, urgency);
        
        
        if (replaces_id != 0) { //replace the notification
            
            bool found_one = false;
            for (var i=0;i<this.box.get_children ().length ();i++) {
                if (((Notification)this.box.get_children ().nth_data (i)).id == replaces_id) {
                    
                    ((Notification)this.box.get_children ().nth_data (i)).set_text (summary, body);
                    ((Notification)this.box.get_children ().nth_data (i)).set_pix  (hints, app_name, app_icon);
                    
                    found_one = true;
                    break;
                }
            }
            
            if (!found_one) //just add a normal one
                this.box.insert_child_at_index (new Notification (summary, body, app_icon, 
                    expire_timeout, app_name, this, hints, replaces_id, actions), 0);
            
            this.update_input_shape ();
            
            return replaces_id;
        } else {
            
            this.box.insert_child_at_index (new Notification (summary, body, app_icon, 
                expire_timeout, app_name, this, hints, id_counter+1, actions), 0);
            
            this.update_input_shape ();
            return id_counter ++;
        }
    }
}

public static void main (string [] args) {
    var cl = GtkClutter.init (ref args);
    if (cl != Clutter.InitError.SUCCESS)
        error ("Clutter failed to initialize");
    
    Granite.Services.Logger.initialize ("pantheon-notifications");
    Granite.Services.Logger.DisplayLevel = Granite.Services.LogLevel.DEBUG;
    
    var n = new NotifyServer ();
    Bus.own_name (
        BusType.SESSION, 
        "org.freedesktop.Notifications", 
        BusNameOwnerFlags.NONE, 
        (con) => {
            try {
                con.register_object ("/org/freedesktop/Notifications", n);
            } catch (Error e) { warning (e.message); }
        }, 
        () => {}, 
        (con, name)=> warning ("Could not aquire bus %s", name));
    
    Gtk.main ();
}