2
// Copyright (c) 2003 - 2005 Henrik Kinnunen (fluxgen at fluxbox dot org)
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:
11
// The above copyright notice and this permission notice shall be included in
12
// all copies or substantial portions of the Software.
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.
22
// $Id: SystemTray.cc 4023 2005-05-17 11:24:50Z simonb $
24
#include "SystemTray.hh"
26
#include "FbTk/EventManager.hh"
27
#include "FbTk/ImageControl.hh"
29
#include "AtomHandler.hh"
31
#include "WinClient.hh"
34
#include <X11/Xutil.h>
35
#include <X11/Xatom.h>
41
/// helper class for tray windows, so we dont call XDestroyWindow
42
class TrayWindow: public FbTk::FbWindow {
44
TrayWindow(Window win):FbTk::FbWindow(win) { }
47
/// handles clientmessage event and notifies systemtray
48
class SystemTrayHandler: public AtomHandler {
50
SystemTrayHandler(SystemTray &tray):m_tray(tray) {
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()) )
59
return m_tray.clientMessage(ce);
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())
69
// we dont want a managed window
70
if (winclient.fbwindow() != 0)
72
// if not kde dockapp...
73
if (!winclient.screen().isKdeDockapp(winclient.window()))
75
// if not our screen...
76
if (winclient.screenNumber() != m_tray.window().screenNumber())
78
winclient.setEventMask(StructureNotifyMask |
79
SubstructureNotifyMask | EnterWindowMask);
80
m_tray.addClient(winclient.window());
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) { };
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) { };
98
virtual bool propertyNotify(WinClient &winclient, Atom the_property) { return false; }
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),
112
FbTk::EventManager::instance()->add(*this, m_window);
113
m_theme.reconfigSig().attach(this);
115
Fluxbox* fluxbox = Fluxbox::instance();
116
Display *disp = fluxbox->display();
118
// setup atom name to _NET_SYSTEM_TRAY_S<screen number>
120
sprintf(intbuff, "%d", m_window.screenNumber());
121
std::string atom_name("_NET_SYSTEM_TRAY_S");
122
atom_name += intbuff; // append number
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);
129
cerr<<__FILE__<<"(SystemTray(const FbTk::FbWindow)): can't set owner!"<<endl;
131
return; // the're can't be more than one owner
134
// ok, it was free. Lets set owner
136
cerr<<__FILE__<<"(SystemTray(const FbTk::FbWindow)): SETTING OWNER!"<<endl;
139
XSetSelectionOwner(disp, tray_atom, m_window.window(), CurrentTime);
141
m_handler.reset(new SystemTrayHandler(*this));
143
fluxbox->addAtomHandler(m_handler.get(), atom_name);
146
// send selection owner msg
147
Window root_window = m_screen.rootWindow().window();
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
160
XSendEvent(disp, root_window, false, StructureNotifyMask, &ce);
165
SystemTray::~SystemTray() {
166
// remove us, else fluxbox might delete the memory too
167
Fluxbox::instance()->removeAtomHandler(m_handler.get());
171
m_screen.imageControl().removeImage(m_pixmap);
173
// ~FbWindow cleans EventManager
176
void SystemTray::move(int x, int y) {
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()) {
186
resizeSig().notify();
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()) {
198
resizeSig().notify();
205
void SystemTray::hide() {
209
void SystemTray::show() {
215
unsigned int SystemTray::width() const {
216
return m_clients.size()* (height() - 2 * m_theme.border().width());
219
unsigned int SystemTray::height() const {
220
return m_window.height();
223
unsigned int SystemTray::borderWidth() const {
224
return m_window.borderWidth();
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;
232
if (event.message_type ==
233
XInternAtom(FbTk::App::instance()->display(), "_NET_SYSTEM_TRAY_OPCODE", False)) {
235
int type = event.data.l[1];
236
if (type == SYSTEM_TRAY_REQUEST_DOCK) {
238
cerr<<"SystemTray::clientMessage(const XClientMessageEvent): SYSTEM_TRAY_REQUEST_DOCK"<<endl;
239
cerr<<"window = event.data.l[2] = "<<event.data.l[2]<<endl;
241
addClient(event.data.l[2]);
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;
256
SystemTray::ClientList::iterator SystemTray::findClient(Window win) {
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)
268
void SystemTray::addClient(Window win) {
272
ClientList::iterator it = findClient(win);
273
if (it != m_clients.end())
276
// make sure we have the same screen number
277
XWindowAttributes attr;
279
if (XGetWindowAttributes(FbTk::App::instance()->display(),
282
XScreenNumberOfScreen(attr.screen) != window().screenNumber()) {
286
FbTk::FbWindow *traywin = new TrayWindow(win);
289
cerr<<"SystemTray::addClient(Window): 0x"<<hex<<win<<dec<<endl;
291
if (m_clients.empty())
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);
303
cerr<<"SystemTray: number of clients = "<<m_clients.size()<<endl;
308
void SystemTray::removeClient(Window win) {
309
ClientList::iterator tray_it = findClient(win);
310
if (tray_it == m_clients.end())
314
cerr<<__FILE__<<"(SystemTray::removeClient(Window)): 0x"<<hex<<win<<dec<<endl;
316
FbTk::FbWindow *traywin = *tray_it;
317
m_clients.erase(tray_it);
319
resize(width(), height());
321
if (m_clients.empty()) {
322
// so we send notify signal to parent
323
resizeSig().notify();
327
void SystemTray::exposeEvent(XExposeEvent &event) {
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
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
350
XMoveResizeWindow(FbTk::App::instance()->display(), (*it)->window(),
351
(*it)->x(), (*it)->y(),
352
(*it)->width(), (*it)->height());
354
// this was why gaim wasn't centring the icon
355
(*it)->sendConfigureNotify(0, 0, (*it)->width(), (*it)->height());
357
// so toolbar know that we changed size
358
resizeSig().notify();
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);
371
// move and resize clients
372
ClientList::iterator client_it = m_clients.begin();
373
ClientList::iterator client_it_end = m_clients.end();
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);
381
client_it = m_clients.begin();
384
void SystemTray::removeAllClients() {
385
BScreen *screen = Fluxbox::instance()->findScreen(window().screenNumber());
386
while (!m_clients.empty()) {
388
m_clients.back()->reparent(screen->rootWindow(), 0, 0);
389
m_clients.back()->hide();
390
delete m_clients.back();
391
m_clients.pop_back();
395
void SystemTray::update(FbTk::Subject* subject) {
397
if (!m_theme.texture().usePixmap()) {
398
m_window.setBackgroundColor(m_theme.texture().color());
402
m_screen.imageControl().removeImage(m_pixmap);
403
m_pixmap = m_screen.imageControl().renderImage(width(), height(),
405
m_window.setBackgroundPixmap(m_pixmap);
408
// "themereconfigure"
410
ClientList::iterator client_it = m_clients.begin();
411
ClientList::iterator client_it_end = m_clients.end();
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) {
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();