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