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