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