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