summaryrefslogtreecommitdiff
path: root/src/tray.vala
blob: d1dfdd22c76bd94879a8116c7bb41e655694cdb6 (plain)
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
namespace AstalTray {

  [DBus (name="org.kde.StatusNotifierWatcher")]
    internal interface IWatcher : Object {

      public abstract string[] RegisteredStatusNotifierItems { owned get; }
      public abstract int ProtocolVersion { owned get; }

      public abstract void RegisterStatusNotifierItem(string service, BusName sender) throws DBusError, IOError;
      public abstract void RegisterStatusNotifierHost(string service) throws DBusError, IOError;

      public signal void StatusNotifierItemRegistered(string service);
      public signal void StatusNotifierItemUnregistered(string service);
      public signal void StatusNotifierHostRegistered();
      public signal void StatusNotifierHostUnregistered();

    }

  public class Tray : Object {

    private StatusNotifierWatcher watcher;
    private IWatcher proxy;

    private HashTable<string, TrayItem> _items;
    public List<weak TrayItem> items { owned get { return _items.get_values(); }}

    public signal void item_added(string service);
    public signal void item_removed(string service);

    construct {
      _items = new HashTable<string, TrayItem>(GLib.str_hash, GLib.str_equal);
      try {

        Bus.own_name(
          BusType.SESSION,
          "org.kde.StatusNotifierWatcher",
          BusNameOwnerFlags.NONE,
          start_watcher,
          () => {
            if (proxy != null) {
              proxy = null;
            }
          },
          start_host);

      } catch (Error err) {
        critical("%s", err.message);
      }

    }

    private void start_watcher(DBusConnection conn) {
      try {
        watcher = new StatusNotifierWatcher();
        conn.register_object("/StatusNotifierWatcher", watcher);
        watcher.StatusNotifierItemRegistered.connect(on_item_register);
        watcher.StatusNotifierItemUnregistered.connect(on_item_unregister);
      } catch (Error err) {
        critical(err.message);
      }
    }

    private void start_host() {
      if(proxy != null) return;

      try {
        proxy = Bus.get_proxy_sync(BusType.SESSION,
            "org.kde.StatusNotifierWatcher",
            "/StatusNotifierWatcher"); 

        proxy.StatusNotifierItemRegistered.connect(on_item_register);
        proxy.StatusNotifierItemUnregistered.connect(on_item_unregister);

        proxy.notify["g-name-owner"].connect(() => {
          _items.foreach((service, _) => {
            item_removed(service);
          });
          _items.remove_all();

          if(proxy != null) {
            foreach (string item in proxy.RegisteredStatusNotifierItems) {
              on_item_register(item);
            }
          } else {
            foreach (string item in watcher.RegisteredStatusNotifierItems) {
              on_item_register(item);
            }
          }
        });

        foreach (string item in proxy.RegisteredStatusNotifierItems) {
          on_item_register(item);
        }
      } catch (Error err) {
        critical("cannot get proxy: %s", err.message);
      }
    }

    private void on_item_register(string service) {
      if(_items.contains(service)) return;
      string[] parts = service.split("/", 2);
      TrayItem item = new TrayItem(parts[0], "/" + parts[1]);
      item.ready.connect(() => {
        _items.set(service, item);
        item_added(service);
      });
    }

    private void on_item_unregister(string service) {
      _items.remove(service);
      item_removed(service);
    }

    public TrayItem get_item(string service) {
      return _items.get(service);
    }

  }
}