aboutsummaryrefslogtreecommitdiff
path: root/src/SystemTray.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/SystemTray.cc')
-rw-r--r--src/SystemTray.cc285
1 files changed, 285 insertions, 0 deletions
diff --git a/src/SystemTray.cc b/src/SystemTray.cc
new file mode 100644
index 0000000..e282a76
--- /dev/null
+++ b/src/SystemTray.cc
@@ -0,0 +1,285 @@
1// SystemTray.cc
2// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net)
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,v 1.3 2003/08/27 00:11:24 fluxgen Exp $
23
24#include "SystemTray.hh"
25
26#include "FbTk/EventManager.hh"
27
28#include "AtomHandler.hh"
29#include "fluxbox.hh"
30
31#include <X11/Xutil.h>
32
33#include <iostream>
34#include <string>
35
36using namespace std;
37
38/// helper class for tray windows, so we dont call XDestroyWindow
39class TrayWindow: public FbTk::FbWindow {
40public:
41 TrayWindow(Window win):FbTk::FbWindow(win) { }
42};
43
44/// handles clientmessage event and notifies systemtray
45class SystemTrayHandler: public AtomHandler {
46public:
47 SystemTrayHandler(SystemTray &tray):m_tray(tray) {
48 }
49 // client message is the only thing we care about
50 bool checkClientMessage(const XClientMessageEvent &ce,
51 BScreen * screen, WinClient * const winclient) {
52 return m_tray.clientMessage(ce);
53 }
54
55 void initForScreen(BScreen &screen) { };
56 void setupFrame(FluxboxWindow &win) { };
57 void setupClient(WinClient &winclient) { };
58
59 void updateClientList(BScreen &screen) { };
60 void updateWorkspaceNames(BScreen &screen) { };
61 void updateCurrentWorkspace(BScreen &screen) { };
62 void updateWorkspaceCount(BScreen &screen) { };
63
64 void updateFrameClose(FluxboxWindow &win) { };
65 void updateClientClose(WinClient &winclient) { };
66 void updateWorkspace(FluxboxWindow &win) { };
67 void updateState(FluxboxWindow &win) { };
68 void updateHints(FluxboxWindow &win) { };
69 void updateLayer(FluxboxWindow &win) { };
70
71 virtual bool propertyNotify(WinClient &winclient, Atom the_property) { return false; }
72
73private:
74 SystemTray &m_tray;
75};
76
77SystemTray::SystemTray(const FbTk::FbWindow &parent):
78 ToolbarItem(ToolbarItem::FIXED),
79 m_window(parent, 0, 0, 1, 1, ExposureMask | ButtonPressMask | ButtonReleaseMask |
80 SubstructureNotifyMask | SubstructureRedirectMask) {
81
82 FbTk::EventManager::instance()->add(*this, m_window);
83
84 // setup atom name to _NET_SYSTEM_TRAY_S<screen number>
85 char intbuff[16];
86 sprintf(intbuff, "%d", m_window.screenNumber());
87 std::string atom_name("_NET_SYSTEM_TRAY_S");
88 atom_name += intbuff; // append number
89
90 Display *disp = FbTk::App::instance()->display();
91
92 // get selection owner and see if it's free
93 Atom tray_atom = XInternAtom(disp, atom_name.c_str(), False);
94 Window owner = XGetSelectionOwner(disp, tray_atom);
95 if (owner != 0)
96 return; // the're can't be more than one owner
97
98 // ok, it was free. Lets set owner
99#ifdef DEBUG
100 cerr<<__FILE__<<"("<<__FUNCTION__<<"): SETTING OWNER!"<<endl;
101#endif // DEBUG
102 // set owner
103 XSetSelectionOwner(disp, tray_atom, m_window.window(), CurrentTime);
104 m_handler.reset(new SystemTrayHandler(*this));
105 Fluxbox::instance()->addAtomHandler(m_handler.get());
106 Window root_window = RootWindow(disp, m_window.screenNumber());
107
108 // send selection owner msg
109 XEvent ce;
110 ce.xclient.type = ClientMessage;
111 ce.xclient.message_type = XInternAtom(disp, "MANAGER", False);
112 ce.xclient.display = disp;
113 ce.xclient.window = root_window;
114 ce.xclient.format = 32;
115 ce.xclient.data.l[0] = CurrentTime; // timestamp
116 ce.xclient.data.l[1] = tray_atom; // manager selection atom
117 ce.xclient.data.l[2] = m_window.window(); // the window owning the selection
118 ce.xclient.data.l[3] = 0l; // selection specific data
119 ce.xclient.data.l[4] = 0l; // selection specific data
120
121 XSendEvent(disp, root_window, false, StructureNotifyMask, &ce);
122
123}
124
125SystemTray::~SystemTray() {
126 // remove us, else fluxbox might delete the memory too
127 Fluxbox::instance()->removeAtomHandler(m_handler.get());
128 removeAllClients();
129}
130
131void SystemTray::move(int x, int y) {
132 m_window.move(x, y);
133}
134
135void SystemTray::resize(unsigned int width, unsigned int height) {
136 if (width != m_window.width() ||
137 height != m_window.height()) {
138 m_window.resize(SystemTray::width(), height);
139 rearrangeClients();
140 }
141}
142
143void SystemTray::moveResize(int x, int y,
144 unsigned int width, unsigned int height) {
145 move(x, y);
146 resize(width, height);
147}
148
149void SystemTray::hide() {
150 m_window.hide();
151}
152
153void SystemTray::show() {
154 m_window.show();
155}
156
157unsigned int SystemTray::width() const {
158 return m_clients.size()*height(); //*m_tray_width;
159}
160
161unsigned int SystemTray::height() const {
162 return m_window.height();
163}
164
165unsigned int SystemTray::borderWidth() const {
166 return m_window.borderWidth();
167}
168
169bool SystemTray::clientMessage(const XClientMessageEvent &event) {
170 static const int SYSTEM_TRAY_REQUEST_DOCK = 0;
171 // static const int SYSTEM_TRAY_BEGIN_MESSAGE = 1;
172 // static const int SYSTEM_TRAY_CANCEL_MESSAGE = 2;
173
174 if (event.message_type ==
175 XInternAtom(FbTk::App::instance()->display(), "_NET_SYSTEM_TRAY_OPCODE", False)) {
176
177 int type = event.data.l[1];
178 if (type == SYSTEM_TRAY_REQUEST_DOCK) {
179 addClient(event.data.l[2]);
180 }
181 /*
182 else if (type == SYSTEM_TRAY_BEGIN_MESSAGE)
183 cerr<<"BEGIN MESSAGE"<<endl;
184 else if (type == SYSTEM_TRAY_CANCEL_MESSAGE)
185 cerr<<"CANCEL MESSAGE"<<endl;
186 */
187
188 return true;
189 }
190
191 return false;
192}
193
194SystemTray::ClientList::iterator SystemTray::findClient(Window win) {
195
196 ClientList::iterator it = m_clients.begin();
197 ClientList::iterator it_end = m_clients.end();
198 for (; it != it_end; ++it) {
199 if ((*it)->window() == win)
200 break;
201 }
202
203 return it;
204}
205
206void SystemTray::addClient(Window win) {
207 if (win == 0)
208 return;
209
210 ClientList::iterator it = findClient(win);
211 if (it != m_clients.end())
212 return;
213
214#ifdef DEBUG
215 cerr<<__FILE__<<"("<<__FUNCTION__<<"): 0x"<<hex<<win<<dec<<endl;
216#endif // DEBUG
217
218 FbTk::FbWindow *traywin = new TrayWindow(win);
219 m_clients.push_back(traywin);
220 FbTk::EventManager::instance()->add(*this, win);
221 XChangeSaveSet(FbTk::App::instance()->display(), win, SetModeInsert);
222 traywin->reparent(m_window, 0, 0);
223 traywin->show();
224
225 resize(width(), m_clients.size()*height());
226
227 rearrangeClients();
228}
229
230void SystemTray::removeClient(Window win) {
231 ClientList::iterator tray_it = findClient(win);
232 if (tray_it == m_clients.end())
233 return;
234
235#ifdef DEBUG
236 cerr<<__FILE__<<"("<<__FUNCTION__<<"): 0x"<<hex<<win<<dec<<endl;
237#endif // DEBUG
238
239 FbTk::FbWindow *traywin = *tray_it;
240 m_clients.erase(tray_it);
241 delete traywin;
242 resize(width(), height());
243 rearrangeClients();
244}
245
246void SystemTray::exposeEvent(XExposeEvent &event) {
247 m_window.clear();
248}
249
250void SystemTray::handleEvent(XEvent &event) {
251 if (event.type == DestroyNotify)
252 removeClient(event.xdestroywindow.window);
253 else if (event.type == ConfigureNotify) {
254 // we got configurenotify from an client
255 // check and see if we need to update it's size
256 ClientList::iterator it = findClient(event.xconfigure.window);
257 if (it != m_clients.end()) {
258 if (static_cast<unsigned int>(event.xconfigure.width) != (*it)->width() ||
259 static_cast<unsigned int>(event.xconfigure.height) != (*it)->height())
260 (*it)->resize((*it)->width(), (*it)->height());
261 }
262 }
263}
264
265void SystemTray::rearrangeClients() {
266 // move and resize clients
267 ClientList::iterator client_it = m_clients.begin();
268 ClientList::iterator client_it_end = m_clients.end();
269 int next_x = 0;
270 for (; client_it != client_it_end; ++client_it, next_x += height()) {
271 (*client_it)->moveResize(next_x, 0, height(), height());
272
273#ifdef DEBUG
274 cerr<<__FILE__<<"("<<__FUNCTION__<<"): "<<(*client_it)->width()<<", "<<(*client_it)->height()<<endl;
275#endif // DEBUG
276 }
277}
278
279void SystemTray::removeAllClients() {
280 while (!m_clients.empty()) {
281 m_clients.back()->hide();
282 delete m_clients.back();
283 m_clients.pop_back();
284 }
285}