1 #include "config.h"
2 #include "intl.h"
3 #include "ylib.h"
4 #include "ylocale.h"
5 #include "yxapp.h"
6 #include "yxtray.h"
7 #include "base.h"
8 #include "debug.h"
9 #include "sysdep.h"
10 #include "ypaths.h"
11 #include "yprefs.h"
12 #include "yconfig.h"
13 #include "ypointer.h"
14
15 char const *ApplicationName = "icewmtray";
16
17 XSV(const char *, clrDefaultTaskBar, "rgb:C0/C0/C0")
18 XIV(bool, trayDrawBevel, false)
19
20 YColorName taskBarBg(&clrDefaultTaskBar);
21 ref<YPixmap> taskbackPixmap;
22
23 #ifdef CONFIG_EXTERNAL_TRAY
24 class SysTray: public YWindow, public YXTrayNotifier {
25 public:
26 SysTray();
27 void requestDock();
28
handleUnmap(const XUnmapEvent &)29 void handleUnmap(const XUnmapEvent &) {
30 MSG(("SysTray::handleUnmap hide %s", boolstr(visible())));
31 if (visible())
32 hide();
33 }
34
35 void trayChanged();
count() const36 int count() const { return fTray2->countClients(); }
37
38 private:
39 YAtom icewm_internal_tray;
40 YAtom _NET_SYSTEM_TRAY_OPCODE;
41 osmart<YXTray> fTray2;
42
show()43 void show() {
44 YWindow::show();
45 XMapWindow(xapp->display(), handle());
46 }
hide()47 void hide() {
48 YWindow::hide();
49 XUnmapWindow(xapp->display(), handle());
50 }
51 };
52
53 class SysTrayApp: public YXApplication {
54 public:
55 SysTrayApp(int *argc, char ***argv,
56 const char* configFile = 0,
57 const char* overrideTheme = 0);
58 ~SysTrayApp();
59
60 void loadConfig();
61 virtual bool filterEvent(const XEvent &xev);
62 virtual void handleSignal(int sig);
63
64 private:
65 YAtom _ICEWM_ACTION;
66 YAtom icewm_internal_tray;
67 YAtom manager;
68 osmart<SysTray> tray;
69 const char* configFile;
70 const char* overrideTheme;
71 };
72
handler(Display * display,XErrorEvent * xev)73 static int handler(Display *display, XErrorEvent *xev) {
74 XDBG {
75 char message[80], req[80], number[80];
76
77 snprintf(number, sizeof number, "%d", xev->request_code);
78 XGetErrorDatabaseText(display, "XRequest", number, "", req, sizeof req);
79 if (req[0] == 0)
80 snprintf(req, sizeof req, "[request_code=%d]", xev->request_code);
81
82 if (XGetErrorText(display, xev->error_code, message, sizeof message))
83 *message = '\0';
84
85 tlog("X error %s(0x%lX): %s", req, xev->resourceid, message);
86 }
87 return 0;
88 }
89
SysTrayApp(int * argc,char *** argv,const char * _configFile,const char * _overrideTheme)90 SysTrayApp::SysTrayApp(int *argc, char ***argv,
91 const char* _configFile, const char* _overrideTheme):
92 YXApplication(argc, argv),
93 _ICEWM_ACTION("_ICEWM_ACTION"),
94 icewm_internal_tray("_ICEWM_INTTRAY_S", true),
95 manager("MANAGER"),
96 tray(0), configFile(_configFile), overrideTheme(_overrideTheme)
97 {
98 desktop->setStyle(YWindow::wsDesktopAware);
99 catchSignal(SIGINT);
100 catchSignal(SIGTERM);
101 catchSignal(SIGHUP);
102 loadConfig();
103
104 XSetErrorHandler(handler);
105 tray = new SysTray();
106 }
107
loadConfig()108 void SysTrayApp::loadConfig() {
109 static cfoption tray_prefs[] = {
110 OSV("ColorDefaultTaskBar", &clrDefaultTaskBar, "Background of the taskbar"),
111 OBV("TrayDrawBevel", &trayDrawBevel, "Surround the tray with plastic border"),
112 OK0()
113 };
114
115 if (configFile == 0 || *configFile == 0)
116 configFile = "preferences";
117 if (overrideTheme && *overrideTheme)
118 themeName = overrideTheme;
119 else
120 {
121 cfoption theme_prefs[] = {
122 OSV("Theme", &themeName, "Theme name"),
123 OK0()
124 };
125 YConfig(theme_prefs).load(configFile).load("theme");
126 }
127 YConfig(tray_prefs).load(configFile).loadTheme().loadOverride();
128 taskbackPixmap = YResourcePaths::loadPixmapFile("taskbarbg.xpm");
129 }
130
~SysTrayApp()131 SysTrayApp::~SysTrayApp() {
132 }
133
filterEvent(const XEvent & xev)134 bool SysTrayApp::filterEvent(const XEvent &xev) {
135 #ifdef DEBUG
136 extern void logEvent(const XEvent &xev);
137 logEvent(xev);
138 #endif
139 if (xev.type == ClientMessage) {
140 if (xev.xclient.message_type == _ICEWM_ACTION) {
141 MSG(("loadConfig"));
142 loadConfig();
143 tray->trayChanged();
144 return true;
145 }
146 else if (xev.xclient.message_type == manager &&
147 (Atom) xev.xclient.data.l[1] == icewm_internal_tray)
148 {
149 if (tray->count() > 0)
150 tray->requestDock();
151 else
152 tray = new SysTray;
153 return true;
154 }
155 }
156 return false;
157 }
158
handleSignal(int sig)159 void SysTrayApp::handleSignal(int sig) {
160 switch (sig) {
161 case SIGHUP:
162 // Reload config colors from theme file and notify tray to repaint
163 loadConfig();
164 tray->trayChanged();
165 return;
166 case SIGINT:
167 case SIGTERM:
168 MSG(("exiting."));
169 this->exit(0);
170 return;
171 }
172 YXApplication::handleSignal(sig);
173 }
174
SysTray()175 SysTray::SysTray():
176 icewm_internal_tray("_ICEWM_INTTRAY_S", true),
177 _NET_SYSTEM_TRAY_OPCODE("_NET_SYSTEM_TRAY_OPCODE")
178 {
179 setTitle("SysTray");
180 setParentRelative();
181 desktop->setStyle(YWindow::wsDesktopAware);
182
183 YAtom trayatom("_NET_SYSTEM_TRAY_S", true);
184 fTray2 = new YXTray(this, false, trayatom, this);
185 fTray2->relayout();
186 setSize(fTray2->width(),
187 fTray2->height());
188 fTray2->show();
189 requestDock();
190 }
191
trayChanged()192 void SysTray::trayChanged() {
193 fTray2->backgroundChanged();
194 setSize(fTray2->width(),
195 fTray2->height());
196 if (fTray2->visible())
197 show();
198 else
199 hide();
200 }
201
requestDock()202 void SysTray::requestDock() {
203 Window w = XGetSelectionOwner(xapp->display(), icewm_internal_tray);
204
205 if (w && w != handle()) {
206 XClientMessageEvent xev = {};
207 xev.type = ClientMessage;
208 xev.window = w;
209 xev.message_type = _NET_SYSTEM_TRAY_OPCODE;
210 xev.format = 32;
211 xev.data.l[0] = CurrentTime;
212 xev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
213 xev.data.l[2] = handle(); //fTray2->handle();
214 xapp->send(xev, w, StructureNotifyMask);
215 }
216 }
217
get_help_text()218 static const char* get_help_text() {
219 return _(
220 " -n, --notify Notify parent process by sending signal USR1.\n"
221 " --display=NAME Use NAME to connect to the X server.\n"
222 " --sync Synchronize communication with X11 server.\n"
223 "\n"
224 " -c, --config=FILE Load preferences from FILE.\n"
225 " -t, --theme=FILE Load the theme from FILE.\n"
226 );
227 }
228
main(int argc,char ** argv)229 int main(int argc, char **argv) {
230 YLocale locale;
231
232 bool notifyParent = false;
233 const char* configFile = 0;
234 const char* overrideTheme = 0;
235
236 for (char **arg = 1 + argv; arg < argv + argc; ++arg) {
237 if (**arg == '-') {
238 char* value(0);
239 if (is_switch(*arg, "n", "notify")) {
240 notifyParent = true;
241 }
242 else if (GetArgument(value, "c", "config", arg, argv+argc)) {
243 configFile = value;
244 }
245 else if (GetArgument(value, "t", "theme", arg, argv+argc)) {
246 overrideTheme = value;
247 }
248 else if (is_help_switch(*arg)) {
249 print_help_exit(get_help_text());
250 }
251 else if (is_version_switch(*arg)) {
252 print_version_exit(VERSION);
253 }
254 #ifdef DEBUG
255 else if (is_long_switch(*arg, "debug")) {
256 debug = 1;
257 }
258 #endif
259 }
260 }
261
262 SysTrayApp stapp(&argc, &argv, configFile, overrideTheme);
263
264 if (notifyParent) {
265 kill(getppid(), SIGUSR1);
266 }
267
268 return stapp.mainLoop();
269 }
270 #else /*CONFIG_EXTERNAL_TRAY*/
main(int argc,char ** argv)271 int main(int argc, char** argv) {
272 check_argv(argc, argv, nullptr, VERSION);
273 return 0;
274 }
275 #endif /*CONFIG_EXTERNAL_TRAY*/
276
277 // vim: set sw=4 ts=4 et:
278