~ubuntu-branches/ubuntu/trusty/fluxbox/trusty-proposed

« back to all changes in this revision

Viewing changes to src/SystemTray.cc

  • Committer: Bazaar Package Importer
  • Author(s): Dmitry E. Oboukhov
  • Date: 2008-07-01 10:38:14 UTC
  • mfrom: (2.1.12 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080701103814-khx2b6il152x9p93
Tags: 1.0.0+deb1-8
* x-dev has been removed from build-depends (out-of-date package).
* Standards-Version bumped to 3.8.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// SystemTray.cc
2
 
// Copyright (c) 2003 - 2005 Henrik Kinnunen (fluxgen at fluxbox dot org)
3
 
//
4
 
// Permission is hereby granted, free of charge, to any person obtaining a
5
 
// copy of this software and associated documentation files (the "Software"),
6
 
// to deal in the Software without restriction, including without limitation
7
 
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
 
// and/or sell copies of the Software, and to permit persons to whom the
9
 
// Software is furnished to do so, subject to the following conditions:
10
 
//
11
 
// The above copyright notice and this permission notice shall be included in
12
 
// all copies or substantial portions of the Software.
13
 
//
14
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
 
// DEALINGS IN THE SOFTWARE.
21
 
 
22
 
// $Id: SystemTray.cc 4023 2005-05-17 11:24:50Z simonb $
23
 
 
24
 
#include "SystemTray.hh"
25
 
 
26
 
#include "FbTk/EventManager.hh"
27
 
#include "FbTk/ImageControl.hh"
28
 
 
29
 
#include "AtomHandler.hh"
30
 
#include "fluxbox.hh"
31
 
#include "WinClient.hh"
32
 
#include "Screen.hh"
33
 
 
34
 
#include <X11/Xutil.h>
35
 
#include <X11/Xatom.h>
36
 
 
37
 
#include <string>
38
 
 
39
 
using namespace std;
40
 
 
41
 
/// helper class for tray windows, so we dont call XDestroyWindow
42
 
class TrayWindow: public FbTk::FbWindow {
43
 
public:
44
 
    TrayWindow(Window win):FbTk::FbWindow(win) { }
45
 
};
46
 
 
47
 
/// handles clientmessage event and notifies systemtray
48
 
class SystemTrayHandler: public AtomHandler {
49
 
public:
50
 
    SystemTrayHandler(SystemTray &tray):m_tray(tray) {
51
 
    }
52
 
    // client message is the only thing we care about
53
 
    bool checkClientMessage(const XClientMessageEvent &ce,
54
 
                            BScreen * screen, WinClient * const winclient) {
55
 
        // must be on the same screen
56
 
        if ((screen && screen->screenNumber() != m_tray.window().screenNumber()) ||
57
 
            (winclient && winclient->screenNumber() != m_tray.window().screenNumber()) )
58
 
            return false;
59
 
        return m_tray.clientMessage(ce);
60
 
    }
61
 
 
62
 
    void initForScreen(BScreen &screen) { };
63
 
    void setupFrame(FluxboxWindow &win) { };
64
 
    void setupClient(WinClient &winclient) {
65
 
        // must be on the same screen
66
 
        if (winclient.screenNumber() != m_tray.window().screenNumber())
67
 
            return;
68
 
 
69
 
        // we dont want a managed window
70
 
        if (winclient.fbwindow() != 0)
71
 
            return;
72
 
        // if not kde dockapp...
73
 
        if (!winclient.screen().isKdeDockapp(winclient.window()))
74
 
            return;
75
 
        // if not our screen...
76
 
        if (winclient.screenNumber() != m_tray.window().screenNumber())
77
 
            return;
78
 
        winclient.setEventMask(StructureNotifyMask |
79
 
                               SubstructureNotifyMask | EnterWindowMask);
80
 
        m_tray.addClient(winclient.window());
81
 
 
82
 
    };
83
 
 
84
 
    void updateWorkarea(BScreen &) { }
85
 
    void updateFocusedWindow(BScreen &, Window) { }
86
 
    void updateClientList(BScreen &screen) { };
87
 
    void updateWorkspaceNames(BScreen &screen) { };
88
 
    void updateCurrentWorkspace(BScreen &screen) { };
89
 
    void updateWorkspaceCount(BScreen &screen) { };
90
 
 
91
 
    void updateFrameClose(FluxboxWindow &win) { };
92
 
    void updateClientClose(WinClient &winclient) { };
93
 
    void updateWorkspace(FluxboxWindow &win) { };
94
 
    void updateState(FluxboxWindow &win) { };
95
 
    void updateHints(FluxboxWindow &win) { };
96
 
    void updateLayer(FluxboxWindow &win) { };
97
 
 
98
 
    virtual bool propertyNotify(WinClient &winclient, Atom the_property) { return false; }
99
 
 
100
 
private:
101
 
    SystemTray &m_tray;
102
 
};
103
 
 
104
 
SystemTray::SystemTray(const FbTk::FbWindow& parent, ButtonTheme& theme, BScreen& screen):
105
 
    ToolbarItem(ToolbarItem::FIXED),
106
 
    m_window(parent, 0, 0, 1, 1, ExposureMask | ButtonPressMask | ButtonReleaseMask |
107
 
             SubstructureNotifyMask | SubstructureRedirectMask),
108
 
    m_theme(theme),
109
 
    m_screen(screen),
110
 
    m_pixmap(0) {
111
 
 
112
 
    FbTk::EventManager::instance()->add(*this, m_window);
113
 
    m_theme.reconfigSig().attach(this);
114
 
 
115
 
    Fluxbox* fluxbox = Fluxbox::instance();
116
 
    Display *disp = fluxbox->display();
117
 
 
118
 
    // setup atom name to _NET_SYSTEM_TRAY_S<screen number>
119
 
    char intbuff[16];
120
 
    sprintf(intbuff, "%d", m_window.screenNumber());
121
 
    std::string atom_name("_NET_SYSTEM_TRAY_S");
122
 
    atom_name += intbuff; // append number
123
 
 
124
 
    // get selection owner and see if it's free
125
 
    Atom tray_atom = XInternAtom(disp, atom_name.c_str(), False);
126
 
    Window owner = XGetSelectionOwner(disp, tray_atom);
127
 
    if (owner != 0) {
128
 
#ifdef DEBUG
129
 
        cerr<<__FILE__<<"(SystemTray(const FbTk::FbWindow)): can't set owner!"<<endl;
130
 
#endif // DEBUG
131
 
        return;  // the're can't be more than one owner
132
 
    }
133
 
 
134
 
    // ok, it was free. Lets set owner
135
 
#ifdef DEBUG
136
 
    cerr<<__FILE__<<"(SystemTray(const FbTk::FbWindow)): SETTING OWNER!"<<endl;
137
 
#endif // DEBUG
138
 
    // set owner
139
 
    XSetSelectionOwner(disp, tray_atom, m_window.window(), CurrentTime);
140
 
 
141
 
    m_handler.reset(new SystemTrayHandler(*this));
142
 
 
143
 
    fluxbox->addAtomHandler(m_handler.get(), atom_name);
144
 
 
145
 
 
146
 
    // send selection owner msg
147
 
    Window root_window = m_screen.rootWindow().window();
148
 
    XEvent ce;
149
 
    ce.xclient.type = ClientMessage;
150
 
    ce.xclient.message_type = XInternAtom(disp, "MANAGER", False);
151
 
    ce.xclient.display = disp;
152
 
    ce.xclient.window = root_window;
153
 
    ce.xclient.format = 32;
154
 
    ce.xclient.data.l[0] = CurrentTime; // timestamp
155
 
    ce.xclient.data.l[1] = tray_atom; // manager selection atom
156
 
    ce.xclient.data.l[2] = m_window.window(); // the window owning the selection
157
 
    ce.xclient.data.l[3] = 0l; // selection specific data
158
 
    ce.xclient.data.l[4] = 0l; // selection specific data
159
 
 
160
 
    XSendEvent(disp, root_window, false, StructureNotifyMask, &ce);
161
 
 
162
 
    update(0);
163
 
}
164
 
 
165
 
SystemTray::~SystemTray() {
166
 
    // remove us, else fluxbox might delete the memory too
167
 
    Fluxbox::instance()->removeAtomHandler(m_handler.get());
168
 
    removeAllClients();
169
 
 
170
 
    if (m_pixmap)
171
 
        m_screen.imageControl().removeImage(m_pixmap);
172
 
 
173
 
    // ~FbWindow cleans EventManager
174
 
}
175
 
 
176
 
void SystemTray::move(int x, int y) {
177
 
    m_window.move(x, y);
178
 
}
179
 
 
180
 
void SystemTray::resize(unsigned int width, unsigned int height) {
181
 
    if (width != m_window.width() ||
182
 
        height != m_window.height()) {
183
 
        m_window.resize(width, height);
184
 
        if (!m_clients.empty()) {
185
 
            rearrangeClients();
186
 
            resizeSig().notify();
187
 
        }
188
 
    }
189
 
}
190
 
 
191
 
void SystemTray::moveResize(int x, int y,
192
 
                            unsigned int width, unsigned int height) {
193
 
    if (width != m_window.width() ||
194
 
        height != m_window.height()) {
195
 
        m_window.moveResize(x, y, width, height);
196
 
        if (!m_clients.empty()) {
197
 
            rearrangeClients();
198
 
            resizeSig().notify();
199
 
        }
200
 
    } else {
201
 
        move(x, y);
202
 
    }
203
 
}
204
 
 
205
 
void SystemTray::hide() {
206
 
    m_window.hide();
207
 
}
208
 
 
209
 
void SystemTray::show() {
210
 
 
211
 
    update(0);
212
 
    m_window.show();
213
 
}
214
 
 
215
 
unsigned int SystemTray::width() const {
216
 
    return m_clients.size()* (height() - 2 * m_theme.border().width());
217
 
}
218
 
 
219
 
unsigned int SystemTray::height() const {
220
 
    return m_window.height();
221
 
}
222
 
 
223
 
unsigned int SystemTray::borderWidth() const {
224
 
    return m_window.borderWidth();
225
 
}
226
 
 
227
 
bool SystemTray::clientMessage(const XClientMessageEvent &event) {
228
 
    static const int SYSTEM_TRAY_REQUEST_DOCK  =  0;
229
 
    //    static const int SYSTEM_TRAY_BEGIN_MESSAGE =  1;
230
 
    //    static const int SYSTEM_TRAY_CANCEL_MESSAGE = 2;
231
 
 
232
 
    if (event.message_type ==
233
 
        XInternAtom(FbTk::App::instance()->display(), "_NET_SYSTEM_TRAY_OPCODE", False)) {
234
 
 
235
 
        int type = event.data.l[1];
236
 
        if (type == SYSTEM_TRAY_REQUEST_DOCK) {
237
 
#ifndef DEBUG
238
 
            cerr<<"SystemTray::clientMessage(const XClientMessageEvent): SYSTEM_TRAY_REQUEST_DOCK"<<endl;
239
 
            cerr<<"window = event.data.l[2] = "<<event.data.l[2]<<endl;
240
 
#endif // DEBUG
241
 
            addClient(event.data.l[2]);
242
 
        }
243
 
        /*
244
 
        else if (type == SYSTEM_TRAY_BEGIN_MESSAGE)
245
 
            cerr<<"BEGIN MESSAGE"<<endl;
246
 
        else if (type == SYSTEM_TRAY_CANCEL_MESSAGE)
247
 
            cerr<<"CANCEL MESSAGE"<<endl;
248
 
        */
249
 
 
250
 
        return true;
251
 
    }
252
 
 
253
 
    return false;
254
 
}
255
 
 
256
 
SystemTray::ClientList::iterator SystemTray::findClient(Window win) {
257
 
 
258
 
    ClientList::iterator it = m_clients.begin();
259
 
    ClientList::iterator it_end = m_clients.end();
260
 
    for (; it != it_end; ++it) {
261
 
        if ((*it)->window() == win)
262
 
            break;
263
 
    }
264
 
 
265
 
    return it;
266
 
}
267
 
 
268
 
void SystemTray::addClient(Window win) {
269
 
    if (win == 0)
270
 
        return;
271
 
 
272
 
    ClientList::iterator it = findClient(win);
273
 
    if (it != m_clients.end())
274
 
        return;
275
 
 
276
 
    // make sure we have the same screen number
277
 
    XWindowAttributes attr;
278
 
    attr.screen = 0;
279
 
    if (XGetWindowAttributes(FbTk::App::instance()->display(),
280
 
                             win, &attr) != 0 &&
281
 
        attr.screen != 0 &&
282
 
        XScreenNumberOfScreen(attr.screen) != window().screenNumber()) {
283
 
        return;
284
 
    }
285
 
 
286
 
    FbTk::FbWindow *traywin = new TrayWindow(win);
287
 
 
288
 
#ifdef DEBUG
289
 
    cerr<<"SystemTray::addClient(Window): 0x"<<hex<<win<<dec<<endl;
290
 
#endif // DEBUG
291
 
    if (m_clients.empty())
292
 
        show();
293
 
 
294
 
    m_clients.push_back(traywin);
295
 
    FbTk::EventManager::instance()->add(*this, win);
296
 
    FbTk::EventManager::instance()->addParent(*this, window());
297
 
    XChangeSaveSet(FbTk::App::instance()->display(), win, SetModeInsert);
298
 
    traywin->reparent(m_window, 0, 0);
299
 
    traywin->show();
300
 
 
301
 
 
302
 
#ifdef DEBUG
303
 
    cerr<<"SystemTray: number of clients = "<<m_clients.size()<<endl;
304
 
#endif // DEBUG
305
 
    rearrangeClients();
306
 
}
307
 
 
308
 
void SystemTray::removeClient(Window win) {
309
 
    ClientList::iterator tray_it = findClient(win);
310
 
    if (tray_it == m_clients.end())
311
 
        return;
312
 
 
313
 
#ifdef DEBUG
314
 
    cerr<<__FILE__<<"(SystemTray::removeClient(Window)): 0x"<<hex<<win<<dec<<endl;
315
 
#endif // DEBUG
316
 
    FbTk::FbWindow *traywin = *tray_it;
317
 
    m_clients.erase(tray_it);
318
 
    delete traywin;
319
 
    resize(width(), height());
320
 
    rearrangeClients();
321
 
    if (m_clients.empty()) {
322
 
        // so we send notify signal to parent
323
 
        resizeSig().notify();
324
 
    }
325
 
}
326
 
 
327
 
void SystemTray::exposeEvent(XExposeEvent &event) {
328
 
    m_window.clear();
329
 
}
330
 
 
331
 
void SystemTray::handleEvent(XEvent &event) {
332
 
    if (event.type == DestroyNotify) {
333
 
        removeClient(event.xdestroywindow.window);
334
 
    } else if (event.type == UnmapNotify && event.xany.send_event) {
335
 
        // we ignore server-generated events, which can occur
336
 
        // on restart. The ICCCM says that a client must send
337
 
        // a synthetic event for the withdrawn state
338
 
        removeClient(event.xunmap.window);
339
 
    } else if (event.type == ConfigureNotify) {
340
 
        // we got configurenotify from an client
341
 
        // check and see if we need to update it's size
342
 
        // and we must reposition and resize them to fit
343
 
        // our toolbar
344
 
        ClientList::iterator it = findClient(event.xconfigure.window);
345
 
        if (it != m_clients.end()) {
346
 
            if (static_cast<unsigned int>(event.xconfigure.width) != (*it)->width() ||
347
 
                static_cast<unsigned int>(event.xconfigure.height) != (*it)->height()) {
348
 
                // the position might differ so we update from our local
349
 
                // copy of position
350
 
                XMoveResizeWindow(FbTk::App::instance()->display(), (*it)->window(), 
351
 
                                  (*it)->x(), (*it)->y(),
352
 
                                  (*it)->width(), (*it)->height());
353
 
 
354
 
                // this was why gaim wasn't centring the icon
355
 
                (*it)->sendConfigureNotify(0, 0, (*it)->width(), (*it)->height());
356
 
            }
357
 
            // so toolbar know that we changed size
358
 
            resizeSig().notify();
359
 
        }
360
 
 
361
 
    }
362
 
}
363
 
 
364
 
void SystemTray::rearrangeClients() {
365
 
    const unsigned int h = height();
366
 
    const unsigned int bw = m_theme.border().width();
367
 
    int final_size = m_clients.size()*h + bw;
368
 
    resize(final_size, h);
369
 
    update(0);
370
 
 
371
 
    // move and resize clients
372
 
    ClientList::iterator client_it = m_clients.begin();
373
 
    ClientList::iterator client_it_end = m_clients.end();
374
 
    int next_x = bw;
375
 
    for (; client_it != client_it_end;
376
 
         ++client_it, next_x += h+bw) {
377
 
        (*client_it)->moveResize(next_x, bw, h, h);
378
 
        (*client_it)->sendConfigureNotify(next_x, bw, h, h);
379
 
    }
380
 
 
381
 
    client_it = m_clients.begin();
382
 
}
383
 
 
384
 
void SystemTray::removeAllClients() {
385
 
    BScreen *screen = Fluxbox::instance()->findScreen(window().screenNumber());
386
 
    while (!m_clients.empty()) {
387
 
        if (screen)
388
 
            m_clients.back()->reparent(screen->rootWindow(), 0, 0);
389
 
        m_clients.back()->hide();
390
 
        delete m_clients.back();
391
 
        m_clients.pop_back();
392
 
    }
393
 
}
394
 
 
395
 
void SystemTray::update(FbTk::Subject* subject) {
396
 
 
397
 
    if (!m_theme.texture().usePixmap()) {
398
 
        m_window.setBackgroundColor(m_theme.texture().color());
399
 
    }
400
 
    else {
401
 
        if(m_pixmap)
402
 
            m_screen.imageControl().removeImage(m_pixmap);
403
 
        m_pixmap = m_screen.imageControl().renderImage(width(), height(),
404
 
                                                       m_theme.texture());
405
 
        m_window.setBackgroundPixmap(m_pixmap);
406
 
    }
407
 
 
408
 
    // "themereconfigure"
409
 
    if (subject) {
410
 
        ClientList::iterator client_it = m_clients.begin();
411
 
        ClientList::iterator client_it_end = m_clients.end();
412
 
        int next_x = 0;
413
 
        const unsigned int h = height();
414
 
        const unsigned int b = m_theme.border().width();
415
 
        for (; client_it != client_it_end;
416
 
             ++client_it, next_x += h - 2 * b) {
417
 
 
418
 
            // maybe not the best solution (yet), force a refresh of the
419
 
            // background of the client
420
 
            (*client_it)->hide();
421
 
            (*client_it)->show();
422
 
        }
423
 
    }
424
 
}