aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/status_notifier_item.vala
blob: de29509daecc3794b7d7e5d44d6a48908e2fc217 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
using GLib;

namespace Dino.Ui {

public struct StatusNotifierIconPixmap {
    public int width;
    public int height;
    public uint8[] data;
}

public struct StatusNotifierToolTip {
    public string icon_name;
    public StatusNotifierIconPixmap[] icon_pixmap;
    public string title;
    public string description;
}

[DBus (name = "org.kde.StatusNotifierItem")]
public class StatusNotifierItem : Object {
    private const string BUS_NAME_TEMPLATE = "org.kde.StatusNotifierItem-%d-1";
    private const string ICON_NAME = "im.dino.Dino";
    private const string ITEM_OBJECT_PATH = "/StatusNotifierItem";
    private const string MENU_OBJECT_PATH = "/MenuBar";
    private const string WATCHER_BUS_NAME = "org.kde.StatusNotifierWatcher";
    private const string WATCHER_INTERFACE = "org.kde.StatusNotifierWatcher";
    private const string WATCHER_OBJECT_PATH = "/StatusNotifierWatcher";

    private weak Application application;
    private DBusConnection connection;
    private string bus_name;
    private bool name_acquired;
    private bool registration_in_progress;
    private bool shutting_down;
    private bool watcher_available;
    private bool registered_with_watcher;
    private string? activation_token;
    private string? watcher_owner;
    private Cancellable registration_cancellable = new Cancellable();
    private StatusNotifierMenu status_notifier_menu;
    private uint bus_name_owner_id;
    private uint menu_registration_id;
    private uint object_registration_id;
    private uint watcher_id;

    public string category { owned get { return "Communications"; } }
    public string id { owned get { return "Dino"; } }
    public string title { owned get { return "Dino"; } }
    public string status { owned get { return "Active"; } }
    public int window_id { get { return 0; } }
    public string icon_theme_path { owned get { return ""; } }
    public ObjectPath menu { owned get { return new ObjectPath(MENU_OBJECT_PATH); } }
    public bool item_is_menu { get { return false; } }
    public string icon_name { owned get { return ICON_NAME; } }
    public StatusNotifierIconPixmap[] icon_pixmap { owned get { return new StatusNotifierIconPixmap[0]; } }
    public string overlay_icon_name { owned get { return ""; } }
    public StatusNotifierIconPixmap[] overlay_icon_pixmap { owned get { return new StatusNotifierIconPixmap[0]; } }
    public string attention_icon_name { owned get { return ""; } }
    public StatusNotifierIconPixmap[] attention_icon_pixmap { owned get { return new StatusNotifierIconPixmap[0]; } }
    public string attention_movie_name { owned get { return ""; } }
    public StatusNotifierToolTip tool_tip {
        owned get {
            StatusNotifierToolTip tool_tip = StatusNotifierToolTip();
            tool_tip.icon_name = ICON_NAME;
            tool_tip.icon_pixmap = new StatusNotifierIconPixmap[0];
            tool_tip.title = "Dino";
            tool_tip.description = _("Open Dino");
            return tool_tip;
        }
    }

    public signal void new_title();
    public signal void new_icon();
    public signal void new_attention_icon();
    public signal void new_overlay_icon();
    public signal void new_menu();
    public signal void new_tool_tip();
    public signal void new_status(string status);

    public StatusNotifierItem(Application application) throws IOError {
        this.application = application;
        this.connection = Bus.get_sync(BusType.SESSION);
        this.bus_name = BUS_NAME_TEMPLATE.printf((int) Posix.getpid());

        status_notifier_menu = new StatusNotifierMenu(application);
        menu_registration_id = connection.register_object(MENU_OBJECT_PATH, status_notifier_menu);
        object_registration_id = connection.register_object(ITEM_OBJECT_PATH, this);
        bus_name_owner_id = Bus.own_name_on_connection(
                connection,
                bus_name,
                BusNameOwnerFlags.NONE,
                () => {
                    name_acquired = true;
                    register_with_watcher.begin();
                },
                () => {
                    name_acquired = false;
                    registered_with_watcher = false;
                });
        watcher_id = Bus.watch_name_on_connection(
                connection,
                WATCHER_BUS_NAME,
                BusNameWatcherFlags.NONE,
                (watcher_connection, watcher_name, name_owner) => {
                    watcher_owner = name_owner;
                    watcher_available = true;
                    registered_with_watcher = false;
                    register_with_watcher.begin();
                },
                () => {
                    watcher_owner = null;
                    watcher_available = false;
                    registered_with_watcher = false;
                });
    }

    internal void shutdown() {
        shutting_down = true;
        name_acquired = false;
        watcher_available = false;
        registration_cancellable.cancel();

        if (watcher_id != 0) {
            Bus.unwatch_name(watcher_id);
            watcher_id = 0;
        }
        if (bus_name_owner_id != 0) {
            Bus.unown_name(bus_name_owner_id);
            bus_name_owner_id = 0;
        }
        if (object_registration_id != 0) {
            connection.unregister_object(object_registration_id);
            object_registration_id = 0;
        }
        if (menu_registration_id != 0) {
            connection.unregister_object(menu_registration_id);
            menu_registration_id = 0;
        }
    }

    public void provide_xdg_activation_token(string token) throws Error {
        activation_token = token;
    }

    public void context_menu(int x, int y) throws Error {
    }

    public void activate(int x, int y) throws Error {
        show_window();
    }

    public void secondary_activate(int x, int y) throws Error {
        show_window();
    }

    public void scroll(int delta, string orientation) throws Error {
    }

    private async void register_with_watcher() {
        if (shutting_down || !name_acquired || !watcher_available ||
                registered_with_watcher || registration_in_progress) return;

        registration_in_progress = true;
        string? registering_watcher = watcher_owner;

        try {
            yield connection.call(
                    WATCHER_BUS_NAME,
                    WATCHER_OBJECT_PATH,
                    WATCHER_INTERFACE,
                    "RegisterStatusNotifierItem",
                    new Variant("(s)", bus_name),
                    null,
                    DBusCallFlags.NONE,
                    5000,
                    registration_cancellable);
            if (!shutting_down && name_acquired && watcher_owner == registering_watcher) {
                registered_with_watcher = true;
            }
        } catch (Error e) {
            if (!(e is IOError.CANCELLED)) {
                warning("Could not register Dino's tray icon: %s", e.message);
            }
        }

        registration_in_progress = false;
        if (!shutting_down && name_acquired && watcher_available && !registered_with_watcher &&
                watcher_owner != registering_watcher) {
            register_with_watcher.begin();
        }
    }

    private void show_window() {
        if (application.window == null) {
            application.activate();
            return;
        }

        if (activation_token != null) {
            application.window.set_startup_id(activation_token);
            activation_token = null;
        }
        application.window.present();
    }
}

}