1 #include <string>
2 #include <cstring>
3 #include "wmapp.h"
4 #include "wmwindow.h"
5
6 using std::string;
7
8 extern "C" {
9 # include <time.h> // for nanosleep()
10 }
11
12 // All the xpms we need:
13 namespace Xpms {
14 # include "xpm/charmap-micro.xpm"
15 # include "xpm/charmap-mini.xpm"
16 # include "xpm/charmap-small.xpm"
17 # include "xpm/charmap-medium.xpm"
18 # include "xpm/charmap-large.xpm"
19 # include "xpm/checkbox.xpm"
20 # include "xpm/xbutton.xpm"
21 # include "xpm/leds.xpm"
22 # include "xpm/emptybar.xpm"
23 # include "xpm/fullbar.xpm"
24 # include "xpm/tile.xpm"
25 }
26
27 // Xlib doesn't seem to work if these are class members.
28 // They're declared as extern in xwrapper.h.
29 X::Window wActiveWin, wProgramWin;
30 X::Atom deleteWin;
31 X::Atom _XA_GNUSTEP_WM_FUNC;
32
33 // static class members of WMApp declared here:
34
35 Xwrapper WMApp::Xw;
36 WMPixmap WMApp::char_pixmaps[5];
37 WMPixmap WMApp::checkbox_pixmap, WMApp::xbutton_pixmap, WMApp::leds_pixmap[4];
38 WMPixmap WMApp::emptybar_pixmap, WMApp::fullbar_pixmap;
39 WMPixmap WMApp::tile_pixmap;
40 Color WMApp::colormap[WMColor::numcolors];
41
42 char ** WMApp::wArgv;
43 string WMApp::wName;
44 int WMApp::wArgc;
45 int WMApp::wWindowSize;
46 enum WindowManager::WindowManager WMApp::wManager;
47
48
49 // initialize: static function to create pixmaps and set default colors.
50 // This should be called as the very first line of a program using this
51 // library.
52 void
initialize(int argc,char * argv[])53 WMApp::initialize(int argc, char *argv[])
54 {
55 static bool inited = false;
56 if (inited) return;
57
58 wArgc = argc;
59 wName = (argc && argv) ? string(argv[0]) : string("WMApplication");
60 wArgv = argv;
61 deleteWin = X::XInternAtom(Xw.xdisplay(), "WM_DELETE_WINDOW", false);
62 _XA_GNUSTEP_WM_FUNC = X::XInternAtom(Xw.xdisplay(),
63 "_GNUSTEP_WM_FUNCTION", false);
64
65 // create all the required pixmaps
66 Xw.create_pixmap(char_pixmaps[0], Xpms::charmap_small_xpm);
67 Xw.create_pixmap(char_pixmaps[1], Xpms::charmap_medium_xpm);
68 Xw.create_pixmap(char_pixmaps[2], Xpms::charmap_large_xpm);
69 Xw.create_pixmap(char_pixmaps[3], Xpms::charmap_micro_xpm);
70 Xw.create_pixmap(char_pixmaps[4], Xpms::charmap_mini_xpm);
71 Xw.create_pixmap(checkbox_pixmap, Xpms::checkbox_xpm);
72 Xw.create_pixmap(xbutton_pixmap, Xpms::xbutton_xpm);
73 Xw.create_pixmap(fullbar_pixmap, Xpms::fullbar_xpm);
74 Xw.create_pixmap(emptybar_pixmap, Xpms::emptybar_xpm);
75 Xw.create_pixmap(tile_pixmap, Xpms::tile_xpm);
76
77 // create four LED pixmaps
78 WMPixmap all_leds_pixmap;
79 int ledwidth = 4, ledheight = 4;
80
81 Xw.create_pixmap(all_leds_pixmap, Xpms::leds_xpm);
82 for (unsigned int s = 0; s < 4; s++) {
83 leds_pixmap[s].attr.width = ledwidth;
84 leds_pixmap[s].attr.width = ledheight;
85 Xw.create_pixmap(leds_pixmap[s], ledwidth, ledheight);
86 Xw.copy_rectangle(all_leds_pixmap, leds_pixmap[s], ledwidth * s, 0,
87 ledwidth, ledheight, 0, 0);
88 }
89
90 # define colormap(x) colormap[static_cast<int>(WMColor:: x)]
91 // create colormap
92 // XXX: eventually, read from RC file
93 colormap(Background) = 0x212121;
94 colormap(Dim) = 0x283C28;
95 colormap(Medium) = 0x188A86;
96 colormap(Bright) = 0x20B2AE;
97 colormap(ButtonFace) = 0xAEAAAE;
98 colormap(ButtonBorderDim) = 0;
99 colormap(ButtonBorderMedium) = 0x86828E;
100 colormap(ButtonBorderBright) = 0xF7F3FF;
101 colormap(FrameBorderDim) = 0;
102 colormap(FrameBorderMedium) = 0;
103 colormap(FrameBorderBright) = 0xC8C8C8; //0xAEAAAE;
104 # undef colormap
105
106 inited = true;
107
108 // set the window behavior from command-line options
109 wManager = WindowManager::WindowMaker;
110 wWindowSize = 64;
111
112 for (int i = 1; i < wArgc && strcmp(wArgv[i], "--"); i++) {
113 if (strcmp(wArgv[i], "-n") == 0) {
114 wManager = WindowManager::Other; break;
115 }
116 else if (strcmp(wArgv[i], "-a") == 0) {
117 wManager = WindowManager::Afterstep;
118 wWindowSize = 56;
119 break;
120 }
121 }
122 }
123
124 // Here begin the magic Xlib incantations required to create a dockapp window.
125 // I have no idea how this works, it just does. Code mostly obtained from
126 // wmsmixer by Damian Kramer, based in turn upon wmmixer by Sam Hawker.
127
128 // simple function to create an XWindow
129 void
create_window(X::Window * dest,int left,int top) const130 WMApp::create_window(X::Window *dest, int left, int top) const
131 {
132 X::XClassHint classHint;
133
134 *dest = X::XCreateSimpleWindow(Xw.xdisplay(), Xw.xrootwin(), left, top,
135 wWindowSize, wWindowSize, 0, 0, 0);
136
137 //XXX: May be able to get away without allocating new char arrays, but I don't
138 //trust X not to modify them.
139 classHint.res_name = new char[wName.size() + 1];
140 classHint.res_class = new char[wName.size() + 1];
141 strcpy(classHint.res_name, wName.c_str());
142 strcpy(classHint.res_class, wName.c_str());
143 X::XSetClassHint(Xw.xdisplay(), *dest, &classHint);
144 delete[] classHint.res_name;
145 delete[] classHint.res_class;
146 }
147
148 // This function sets up the X window.
149 // Called from WMApp::run()
Xsetup()150 void WMApp::Xsetup()
151 {
152 X::Display * display = Xw.xdisplay();
153
154 X::XWMHints wmhints;
155 X::XSizeHints shints;
156 int winsize = wWindowSize;
157 bool pos;
158
159 shints.x = shints.y = shints.flags = 0;
160 pos = (X::XWMGeometry(display, X::XDefaultScreen(display), "" /*geometry*/,
161 0, 0, &shints, &shints.x, &shints.y, &shints.width, &shints.height,
162 &shints.win_gravity) & X_MACRO(XValue | YValue));
163 shints.min_width = shints.min_height = winsize;
164 shints.max_width = shints.max_height = winsize;
165 shints.base_width = shints.base_height = winsize;
166 shints.flags = X_MACRO(PMinSize | PMaxSize | PBaseSize);
167 create_window(&wProgramWin, shints.x, shints.y);
168
169 if (pos || wManager == WindowManager::WindowMaker
170 || wManager == WindowManager::Afterstep)
171 shints.flags |= X_MACRO(USPosition);
172
173 if (wManager == WindowManager::WindowMaker) {
174 wmhints.initial_state = X_MACRO(WithdrawnState);
175 wmhints.flags = X_MACRO(WindowGroupHint | StateHint | IconWindowHint);
176 create_window(&wActiveWin, shints.x, shints.y);
177 wmhints.icon_window = wActiveWin;
178 }
179 else {
180 wmhints.initial_state = X_MACRO(NormalState);
181 wmhints.flags = X_MACRO(WindowGroupHint | StateHint);
182 wActiveWin = wProgramWin;
183 }
184
185 wmhints.window_group = wProgramWin;
186 X::XSetWMHints(display, wProgramWin, &wmhints);
187 X::XSetWMNormalHints(display, wProgramWin, &shints);
188 if (wArgc > 0)
189 X::XSetCommand(display, wProgramWin, wArgv, wArgc);
190 X::XStoreName(display, wProgramWin, wName.c_str());
191 X::XSetIconName(display, wProgramWin, wName.c_str());
192 X::XSetWMProtocols(display, wActiveWin, &deleteWin, 1);
193 }
194
195 // This function masks out the "clear" parts of the X window.
196 // Called from WMWindow::real_display()
197 void
mask() const198 WMApp::mask() const
199 {
200 X::Display * display = Xw.xdisplay();
201 WMPixmap pixmap = current()->pixmap();
202
203 if (wManager == WindowManager::WindowMaker ||
204 wManager == WindowManager::Afterstep)
205 // apply the mask if using a compliant window manager
206 X::XShapeCombineMask(display, wActiveWin, X_MACRO(ShapeBounding),
207 0, 0, pixmap.mask, X_MACRO(ShapeSet));
208 else
209 // otherwise, create a tile background for the window
210 Xw.copy_rectangle(tile_pixmap, pixmap.pixmap, 0, 0, wWindowSize,
211 wWindowSize);
212
213 Xw.fill_rectangle(pixmap, 0, 0, wWindowSize, wWindowSize,
214 WMColor(Background));
215 }
216
217 // This function redraws the X window if necessary.
218 // Called from WMWindow::real_display()
219 void
repaint() const220 WMApp::repaint() const
221 {
222 X::XEvent xev;
223
224 Xw.copy_rectangle(current()->pixmap(), wActiveWin,
225 0, 0, wWindowSize, wWindowSize);
226
227 while (X::XCheckTypedEvent(Xw.xdisplay(), X_MACRO(Expose), &xev))
228 /* loop */;
229 X::XFlush(Xw.xdisplay());
230 }
231
232 // This function displays the X window.
233 // Called from WMWindow::real_activate()
234 void
Xshow() const235 WMApp::Xshow() const
236 {
237 X::Display * display = Xw.xdisplay();
238
239 // request input events and map the window
240 X::XSelectInput(display, wActiveWin,
241 X_MACRO(ExposureMask | ButtonPressMask | ButtonReleaseMask)
242 | X_MACRO(ButtonMotionMask));
243 X::XMapWindow(display, wProgramWin);
244 repaint();
245
246 // loop over X events and callbacks
247 Xwait();
248 }
249
250 // This function waits for X events, redraws the window when necessary,
251 // and calls callbacks of the window.
252 void
Xwait() const253 WMApp::Xwait() const
254 {
255 X::Display * display = Xw.xdisplay();
256 X::XEvent xev;
257 timespec T;
258
259 while (true) {
260 // sleep for the specified time in milliseconds
261 T.tv_sec = current()->updatefreq() / 1000;
262 T.tv_nsec = (current()->updatefreq() % 1000) * 1000000;
263 nanosleep(&T, 0);
264 // execute any timed functions which need it
265 current()->run_timed_functions();
266
267 // execute any pending X events
268 while (XPending(display)) {
269 X::XNextEvent(display, &xev);
270
271 switch (xev.type) {
272 case Expose:
273 repaint();
274 break;
275 case ButtonPress:
276 current()->press(xev.xbutton.button, xev.xbutton.x, xev.xbutton.y);
277 break;
278 case ButtonRelease:
279 wMouseEvent.button = xev.xbutton.button;
280 wMouseEvent.x = xev.xbutton.x;
281 wMouseEvent.y = xev.xbutton.y;
282 current()->release(xev.xbutton.button, xev.xbutton.x, xev.xbutton.y);
283 if (done()) return;
284 break;
285 case ClientMessage:
286 if (static_cast<unsigned int>(xev.xclient.data.l[0]) == deleteWin)
287 { stop(); return; }
288 break;
289 default: break;
290 }
291 }
292 }
293 }
294
295 // End of magic Xlib incantations.
296
297 // The following functions deal with setting the state of the application
298 // and running it.
299
300 void
switch_to(WMWindow * w) const301 WMApp::switch_to(WMWindow *w) const
302 {
303 for (unsigned int i = 0; i < wWindows.size(); i++)
304 if (w == wWindows[i]) {
305 switch_to(i);
306 return;
307 }
308 }
309
310 void
switch_to(WMWindow & w) const311 WMApp::switch_to(WMWindow &w) const { switch_to(&w); }
312
313 // run: This function starts the GUI of the dockapp. Call it as the
314 // last line in a program using this library. Calling run() with no
315 // argument starts the program on the first window. You can also
316 // call run(WMWindow *), run(WMWindow &), or run(unsigned int) in order
317 // to start with a different window.
318 //
319 // So your program in the simplest case should look like this:
320 //
321 // int main(int argc, char *argv[]) {
322 // WMApp::initialize(argc, argv);
323 // WMWindow w;
324 // WMApp a = WMApp(w);
325 // // layout the window and set up callbacks here
326 // // ...
327 // a.addwindow(w);
328 // a.run();
329 // return 0;
330 // }
331
332 void
run(WMWindow * w)333 WMApp::run(WMWindow *w)
334 {
335 for (unsigned int i = 0; i < wWindows.size(); i++)
336 if (w == wWindows[i]) {
337 run(i);
338 return;
339 }
340 }
341
342 void
run(WMWindow & w)343 WMApp::run(WMWindow &w) { run(&w); }
344
345 void
run(int w)346 WMApp::run(int w) { run(static_cast<unsigned int>(w)); }
347
348 void
run(unsigned int w)349 WMApp::run(unsigned int w)
350 {
351 if (w >= wWindows.size()) return;
352
353 // set up pointers from windows to "this"
354 for (unsigned int i = 0; i < wWindows.size(); i++)
355 wWindows[i]->wApp = this;
356
357 Xsetup();
358 wActiveWindow = wNextWindow = w;
359
360 while (wFinished == false) {
361 // This function does not exit until wNextWindow != wActiveWindow
362 // or wFinished == true. User interaction happens in here.
363 current()->display();
364
365 // To switch to a different window, set a callback on a button within
366 // this window, like this:
367 // void callback1(const WMApp *a, void *) { a->switch_to(5); }
368 // To exit the program, likewise, you need a callback like
369 // void callback2(const WMApp *a, void *) { a->stop(); }
370 // (Alternately you could put the window on a timer by setting one of
371 // these as a callback of the window itself, substituting WMWindow for
372 // WMButton in the function definitions.)
373
374 wActiveWindow = wNextWindow;
375
376 // if we have switched to a different window, continue with that one.
377 }
378
379 // otherwise, exit the WMApp::run() function. (Normally a call like
380 // wm_app->run(); will be the last in a program's main() function,
381 // in which case we also exit the program.)
382 return;
383 }
384
385