1 /* treewm - an X11 window manager.
2  * Copyright (c) 2001-2002 Thomas J�ger <TheHunter2000 at web dot de>
3  * This code is released under the terms of the GNU GPL.
4  * See the included file LICENSE for details.
5  */
6 
7 #include "desktop.h"
8 #include "manager.h"
9 #include "icon.h"
10 #include "uehandler.h"
11 #include "resmanager.h"
12 #include "clienttree.h"
13 #include "sceme.h"
14 #include "menu.h"
15 #include "MwmUtil.h"
16 #include "textdialog.h"
17 #include "action.h"
18 #include <unistd.h>
19 #include <X11/Xmd.h>
20 #include <X11/Xatom.h>
21 #include <signal.h>
22 #include <sys/wait.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <X11/keysym.h>
26 
27 Manager *man;
28 
29 Atom wm_state, wm_change_state, wm_protos, wm_delete, wm_cmapwins, wm_client_leader,
30      mwm_hints;
31 
32 
33 
34 
handle_xerror(Display * dpy,XErrorEvent * e)35 int handle_xerror(Display *dpy, XErrorEvent *e) {
36   Client *c = ct ? ct->FindClient(e->resourceid, 0) : 0;
37 
38   if (e->error_code == BadAccess && e->resourceid == root) {
39     fprintf(stderr,"root window unavailible (maybe another wm is running?)\n");
40     exit(1);
41   } else {
42     char msg[80], req[80], number[80];
43     XGetErrorText(dpy, e->error_code, msg, sizeof msg - 1);
44     msg[79]='\0';
45     snprintf(number, sizeof number - 1, "%d", e->request_code);
46     number[79] = '\0';
47     XGetErrorDatabaseText(dpy, "XRequest", number, "", req, sizeof(req) - 1);
48     req[79]='\0';
49     if (req[0] == '\0')
50       sprintf(req, "<request-code-%d>", e->request_code);
51     fprintf(stderr,"XERROR: %s(0x%x): %s\n", req, (unsigned int)e->resourceid, msg);
52 
53   }
54 #ifdef DEBUG
55   fprintf(stderr,"\7");
56 #endif
57 
58 
59 //  if (c && c->parent)
60 //    c->parent->RemoveChild(c,R_WITHDRAW);
61 /*
62  *  Here, a very interesting bug happens. If we call an X function during the
63  *  processing of an event and an error occurs and we immediately delete the window
64  *  we're working with, we cause a lot of shit
65  *  therefore, we have to be _very_ careful
66 
67  *  it's just because of these crappy programs that change their titles before they
68  *  kill themselves
69 */
70   if (e->error_code == BadWindow && c && c->parent)
71     man->AddDeleteClient(c);
72   return 0;
73 }
74 
AddDeleteClient(Client * c)75 void Manager::AddDeleteClient(Client *c) {
76   if (man->MustBeDeleted) {
77     ClientList *cl = man->MustBeDeleted;
78     for (;;cl=cl->next) {
79       if (cl->client == c)
80         break;
81       if (!cl->next) {
82         cl->next = new ClientList;
83         cl = cl->next;
84         cl->next = 0;
85         cl->client = c;
86         break;
87       }
88     }
89   } else {
90     man->MustBeDeleted = new ClientList;
91     man->MustBeDeleted->client = c;
92     man->MustBeDeleted->next = 0;
93   }
94 }
95 
SigHandler(int signal)96 void SigHandler(int signal) {
97   switch (signal) {
98     case SIGHUP:
99 //      man->Restart();
100 //      break;
101     case SIGINT:
102     case SIGTERM:
103       man->Quit();
104       exit(0);
105       break;
106     case SIGCHLD:
107       wait(0);
108       break;
109   }
110 }
111 
112 
Manager(int argc,char ** argv)113 Manager::Manager(int argc, char **argv) {
114   man = this;
115 
116   XSetWindowAttributes sattr;
117   struct sigaction act;
118 
119   ct = NULL; // if an error happens, this value is used
120 
121   act.sa_handler = SigHandler;
122   act.sa_flags = 0;
123   sigaction(SIGTERM, &act, NULL);
124   sigaction(SIGINT, &act, NULL);
125   sigaction(SIGHUP, &act, NULL);
126   sigaction(SIGCHLD, &act, NULL);
127 
128   int display = 0;
129   int config = 0;
130   for (int i=1;i!=argc;++i) {
131     if (!strcmp(argv[i],"-display") && ++i != argc) {
132       display = i;
133       continue;
134     }
135     if (!strcmp(argv[i],"-f") && ++i != argc) {
136       config = i;
137       continue;
138     }
139     printf("usage: %s [-display displayname] [-f configfile]\n",argv[0]);
140     exit(1);
141 
142   }
143   dpy = XOpenDisplay(display ? argv[display] : 0);
144   if (dpy) {
145 // thx to Volker Grabsch
146     if (display)
147       setenv("DISPLAY",argv[display],1);
148   }
149   else {
150     fprintf(stderr,"can't open display %s!\n",display ? argv[display]: 0);
151     exit(1);
152   }
153 
154 
155   XSetErrorHandler(handle_xerror);
156 
157   char filename[256];
158   snprintf(filename,255,"%s/%s",getenv("HOME"),".cmdtreewm");
159   filename[255] = '\0';
160   mkfifo(filename,0600);
161   fifo = open(filename,O_RDWR | O_NONBLOCK);
162   if (!(fifo + 1))
163     fprintf(stderr,"warning: couldn't create fifo (~/.cmdtreewm)");
164 
165   screen = DefaultScreen(dpy);
166   root = RootWindow(dpy, screen);
167 
168 
169   wm_state = XInternAtom(dpy, "WM_STATE", false);
170   wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", false);
171   wm_protos = XInternAtom(dpy, "WM_PROTOCOLS", false);
172   wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", false);
173   wm_cmapwins = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", false);
174   wm_client_leader = XInternAtom(dpy, "WM_CLIENT_LEADER", false);
175   mwm_hints = XInternAtom(dpy, _XA_MWM_HINTS, false);
176 #ifdef SHAPE
177   int dummy;
178   shape = XShapeQueryExtension(dpy, &shape_event, &dummy);
179 #endif
180 
181   sattr.event_mask = ChildMask|ColormapChangeMask|ButtonMask;
182   XChangeWindowAttributes(dpy, root, CWEventMask, &sattr);
183 
184 
185 	sattr.override_redirect = 1;
186 	sattr.event_mask = KeyPressMask | KeyReleaseMask;
187 	input = XCreateWindow(dpy, root, 0, 0, 1, 1, 0,
188 		CopyFromParent, InputOnly, CopyFromParent, CWEventMask | CWOverrideRedirect, &sattr);
189 	XMapWindow(dpy, input);
190 
191 //  XSelectInput(dpy,root,sattr.event_mask);
192 
193   LastReDraw = 0;
194   FirstReDraw  = 0;
195 
196 // Scan Windows now
197   /*rman = */ new ResManager(config ? argv[config] : 0);
198   /*UEH = */  new UEHandler();
199   /*ct = */   new ClientTree();
200   for (InfoListIter il = rman->infos[SE_ACTION]->begin(); il!=rman->infos[SE_ACTION]->end(); ++il)
201     if (((Action *)il->second)->flags & AC_AUTOSTART) {
202       ((Action *)il->second)->Execute();
203     }
204 
205   unsigned int nwins, i;
206   Window dummyw1, dummyw2, *wins;
207   XWindowAttributes attr;
208   XQueryTree(dpy, root, &dummyw1, &dummyw2, &wins, &nwins);
209   for (i = 0; i != nwins; ++i) {
210     XGetWindowAttributes(dpy, wins[i], &attr);
211     if (!attr.override_redirect && attr.map_state == IsViewable)
212       ct->AddWindow(wins[i]);
213   }
214   XFree(wins);
215 
216 
217   ct->RootDesktop->GetFocus();
218   WantFocus = 0;
219   ToBeRaised = 0;
220 
221 }
222 
~Manager()223 Manager::~Manager() {
224 
225 }
226 
Quit()227 void Manager::Quit() {
228   delete ct;
229   delete UEH;
230   delete rman;
231   XInstallColormap(dpy, DefaultColormap(dpy, screen));
232   XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
233 
234   XCloseDisplay(dpy);
235 
236 }
237 
238 
239 #ifdef DEBUG
240 
241 #define SHOW_EV(name, memb) \
242     case name: s = #name; w = e.memb.window; break;
243 
ShowEvent(XEvent e)244 void ShowEvent(XEvent e) {
245     char sbuf[20],*s=sbuf, buf[20];
246     sbuf[0]='\0';
247     Window w=0;
248     Client *c=NULL;
249     switch (e.type) {
250         SHOW_EV(ButtonPress, xbutton)
251         SHOW_EV(ButtonRelease, xbutton)
252         SHOW_EV(ClientMessage, xclient)
253         SHOW_EV(ColormapNotify, xcolormap)
254         SHOW_EV(ConfigureNotify, xconfigure)
255         SHOW_EV(ConfigureRequest, xconfigurerequest)
256         SHOW_EV(CreateNotify, xcreatewindow)
257         SHOW_EV(DestroyNotify, xdestroywindow)
258         SHOW_EV(EnterNotify, xcrossing)
259         SHOW_EV(Expose, xexpose)
260         SHOW_EV(LeaveNotify, xcrossing)
261         SHOW_EV(MapNotify, xmap)
262         SHOW_EV(MapRequest, xmaprequest)
263         SHOW_EV(MappingNotify, xmapping)
264         SHOW_EV(MotionNotify, xmotion)
265         SHOW_EV(PropertyNotify, xproperty)
266         SHOW_EV(ReparentNotify, xreparent)
267         SHOW_EV(ResizeRequest, xresizerequest)
268         SHOW_EV(UnmapNotify, xunmap)
269         SHOW_EV(KeyPress, xkey)
270         SHOW_EV(KeyRelease, xkey)
271         default:
272 #ifdef SHAPE
273           if (shape && e.type == shape_event) {
274             strcpy(s,"ShapeNotify"); w = ((XShapeEvent *)&e)->window;
275           } else {
276 #endif
277             snprintf(s, 19,"unknown event %i",e.type);
278             w = None;
279 #ifdef SHAPE
280           }
281 #endif
282           break;
283     }
284     Icon *i = 0;
285     c = ct->FindClient(w, &i);
286     if (i)
287       c = i->parent;
288     strncpy(buf, c ? (c->name ? c->name : "") : "(none)", sizeof buf-1);
289     buf[sizeof buf-1]='\0';
290     err("%#-10lx: %-20s: %s", w, buf, s);
291 
292 }
293 
294 #endif
295 
AddReDrawClient(Client * c)296 void Manager::AddReDrawClient(Client *c) {
297   if (LastReDraw) {
298     LastReDraw->next = new ClientList;
299     LastReDraw = LastReDraw->next;
300   } else {
301     FirstReDraw = new ClientList;
302     LastReDraw = FirstReDraw;
303   }
304   LastReDraw->next = 0;
305   LastReDraw->client = c;
306 }
307 
CleanUp()308 void Manager::CleanUp() {
309 // Now delete the windows Xerror found
310   for (;MustBeDeleted;) {
311     ClientList *cl = MustBeDeleted;
312     if (MustBeDeleted->client)
313       MustBeDeleted->client->parent->RemoveChild(MustBeDeleted->client,R_WITHDRAW);
314     MustBeDeleted = MustBeDeleted->next;
315     delete cl;
316   }
317 
318   if (ToBeRaised && !XPending(dpy)) {
319 #ifdef DEBUG
320     err("Raising Window");
321 #endif
322     ToBeRaised->Raise(R_PARENT|R_INDIRECT);
323     ToBeRaised = 0;
324   }
325   if (WantFocus && !XPending(dpy)) {
326 #ifdef DEBUG
327     err("Giving Focus to Window");
328 #endif
329     WantFocus->GetFocus();
330     WantFocus = 0;
331   }
332   if (!ct->Focus && !ct->MaxWindow && !XPending(dpy)) {
333 #ifdef DEBUG
334     err("Focus Lost");
335 #endif
336     Client *c = ct->FindPointerClient();
337 //      c->Raise(R_PARENT|R_INDIRECT);
338     c->GetFocus();
339   }
340 
341   if (FirstReDraw && !XPending(dpy)) {
342     ClientList *i;
343     for (;FirstReDraw;FirstReDraw = i) {
344       i = FirstReDraw->next;
345       if (FirstReDraw->client) {
346         if (FirstReDraw->client->visible)
347           FirstReDraw->client->ReDraw();
348         FirstReDraw->client->flags &= ~CF_IGNOREEXPOSE;
349       }
350       delete FirstReDraw;
351     }
352     LastReDraw = 0;
353 
354   }
355 
356   if (!XPending(dpy)) { // No more events
357     if (IgnoreLeave && XSync(dpy,false) && !XPending(dpy)) // avoid annoying race condition
358       IgnoreLeave = false;
359   }
360 }
361 
362 
NextEvent()363 void Manager::NextEvent() {
364   fd_set read_fds;
365   while (!XPending(dpy) && fifo+1) {
366 #define BUFSIZE 255
367     char buffer[BUFSIZE+1];
368     int length = read(fifo, buffer, BUFSIZE);
369     while (length > 0) {
370       int i1=0,i2=0;
371       while (i2 != length) {
372         if (buffer[i2] == '\n') {
373           buffer[i2++] = '\0';
374           UEH->ExecCommands(buffer+i1);
375           i1 = i2;
376         } else {
377           ++i2;
378         }
379       }
380       if (length < BUFSIZE)
381         break;
382       if (i1) {
383         i1 = i2 - i1;
384         int i = i1;
385         while (i1)
386           buffer[--i1] = buffer[--i2];
387         length = read(fifo,buffer + i,BUFSIZE - i) + i;
388         if (length == i)
389           break;
390       } else {
391         while (read(fifo,buffer,BUFSIZE) > 0);
392         break;
393       }
394     }
395     if (XPending(dpy))
396       break;
397     CleanUp();
398     if (XPending(dpy))
399       break;
400     FD_ZERO(&read_fds);
401     int fd1 = ConnectionNumber(dpy);
402     FD_SET(fd1, &read_fds);
403     FD_SET(fifo,&read_fds);
404     select(1 + ((fd1<fifo) ? fifo : fd1), &read_fds, 0, 0, 0);
405   }
406 
407 }
408 
HandleExpose(XExposeEvent & e)409 void Manager::HandleExpose(XExposeEvent &e) {
410   if (e.count)
411     return;
412   Icon *i=0;
413   Client *c = ct->FindClient(e.window, &i);
414   if (i) {
415     i->ReDraw();
416     return;
417   }
418   if (c) {
419     AddReDrawClient(c);
420     return;
421   }
422   if (UEH->menu) {
423     Menu *m = UEH->menu;
424     while (m) {
425       if (e.window == m->window) {
426         m->ReDraw();
427         return;
428       }
429       m = m->submenu;
430     }
431   }
432   if (UEH->dialog && e.window == UEH->dialog->window) {
433     UEH->dialog->ReDraw();
434     return;
435   }
436 
437 }
438 
439 
Run()440 int Manager::Run() {
441   XEvent ev;
442 #ifdef DEBUG
443   err("Entering event loop");
444 #endif
445   MustBeDeleted = 0;
446   IgnoreLeave = false;
447   alive = true;
448   for (;alive;) {
449     NextEvent();
450     XNextEvent(dpy, &ev);
451 #ifdef DEBUG
452     ShowEvent(ev);
453 #endif
454     Client *c;
455     switch (ev.type) {
456       case KeyRelease:
457       case KeyPress:
458         UEH->Key(ev.xkey);
459         break;
460       case ButtonPress:
461         UEH->Press(ev.xbutton);
462         break;
463       case ButtonRelease:
464         UEH->Release(ev.xbutton);
465         break;
466       case MotionNotify:
467         UEH->Motion(ev.xmotion);
468         break;
469       case ConfigureRequest:
470         c = ct->FindClient(ev.xconfigurerequest.window, 0);
471         if (c) {
472           c->Configure(ev.xconfigurerequest);
473         } else {
474           XWindowChanges wc;
475           wc.x = ev.xconfigurerequest.x;
476           wc.y = ev.xconfigurerequest.y;
477           wc.width = ev.xconfigurerequest.width;
478           wc.height = ev.xconfigurerequest.height;
479           wc.sibling = ev.xconfigurerequest.above;
480           wc.stack_mode = ev.xconfigurerequest.detail;
481           XConfigureWindow(dpy, ev.xconfigurerequest.window, ev.xconfigurerequest.value_mask, &wc);
482         }
483         break;
484       case MapRequest:
485         c = ct->FindClient(ev.xmaprequest.window, 0);
486         if (c) {
487           if (!c->visible && c->parent) {
488             c->Map();
489             c->Raise(0);
490           }
491         } else {
492           ct->AddWindow(ev.xmaprequest.window);
493         }
494         break;
495       case MapNotify:
496         c = ct->FindClient(ev.xmap.window,0);
497         if (c) {
498           if (ev.xunmap.window != c->window)
499             break;
500           c->Map();
501         }
502         break;
503       case UnmapNotify:
504         c = ct->FindClient(ev.xunmap.window,0);
505         if (c) {
506           if (ev.xunmap.window != c->window)
507             break;
508           c->Unmap(true);
509         }
510         break;
511       case DestroyNotify:
512         c = ct->FindClient(ev.xdestroywindow.window, 0);
513         if (c)
514           c->parent->RemoveChild(c,R_WITHDRAW);
515         break;
516       case ClientMessage:
517         c = ct->FindClient(ev.xclient.window,0);
518         if (c && ev.xclient.message_type == wm_change_state &&
519             ev.xclient.format == 32 && ev.xclient.data.l[0] == IconicState)
520           c->Hide();
521         break;
522       case ColormapNotify:
523         c = ct->FindClient(ev.xcolormap.window, 0);
524         if (c && ev.xcolormap.c_new) {
525           c->cmap = ev.xcolormap.colormap;
526           if (ct->Focus == c)
527             XInstallColormap(dpy, c->cmap);
528         }
529         break;
530       case PropertyNotify:
531         switch (ev.xproperty.atom) {
532           case XA_WM_NAME:
533             c = ct->FindClient(ev.xproperty.window,0);
534             if (c) {
535               c->ChangeName();
536               c->UpdateName(false);
537               if (c->icon)
538                 c->icon->ChangeName(0);
539             }
540             break;
541           case XA_WM_ICON_NAME:
542             c = ct->FindClient(ev.xproperty.window, 0);
543             if (c && c->icon && !c->name)
544               c->icon->ChangeName(0);
545             break;
546           case XA_WM_NORMAL_HINTS:
547             c = ct->FindClient(ev.xproperty.window, 0);
548             if (c) {
549               long dummy;
550               XGetWMNormalHints(dpy, c->window, c->size, &dummy);
551 #ifdef DEBUG
552               c->dump();
553 #endif
554             }
555             break;
556         }
557         break;
558       case EnterNotify:
559         c = ct->FindClient(ev.xcrossing.window, 0);
560         if (c) {
561           if ((c->flags & CF_AUTOSHADE) && (c->flags & CF_SHADED))
562             c->Shade();
563           if (c->flags & CF_RAISEONENTER)
564             ToBeRaised = c;
565           if (c->flags & CF_FOCUSONENTER)
566             WantFocus = c;
567         } else {
568           if (IgnoreLeave)
569             break;
570           for (int i=0;i!=4;++i)
571             if (ev.xcrossing.window == ct->RootBorder[i]) {
572               c = ct->RootDesktop;
573               goto leave; // quick and dirty
574             }
575         }
576         break;
577       case LeaveNotify:
578         if (IgnoreLeave)
579           break;
580         c = ct->FindClient(ev.xcrossing.window, 0);
581         leave:
582         {
583           if (!c)
584             break;
585           if ((c->flags & CF_AUTOSHADE) && !(c->flags & CF_SHADED) && (c->frame == ev.xcrossing.window))
586             c->Shade();
587           Desktop *d = dynamic_cast<Desktop *>(c);
588           if (!d)
589             break;
590           if (d->parent && (ev.xcrossing.state & ControlMask))
591             d = d->parent;
592           if ((ev.xcrossing.state & Mod1Mask) || (d->flags & DF_AUTOSCROLL))
593             IgnoreLeave = d->Leave(ev.xcrossing.x_root,ev.xcrossing.y_root +
594                        (ev.xcrossing.window==c->window ? d->THeight : 0),ev.xcrossing.state & Mod1Mask);
595         }
596         break;
597       case Expose:
598         HandleExpose(ev.xexpose);
599         break;
600       default:
601 #ifdef SHAPE
602         if (shape && ev.type == shape_event) {
603           c = ct->FindClient(((XShapeEvent *)&ev)->window, 0);
604           if (c)
605             c->SetShape();
606         }
607 #endif
608         break;
609     }
610 #ifdef DEBUG2
611     XSync(dpy,false);
612 #endif
613     CleanUp();
614 
615   }
616   Quit();
617   return 0;
618 }
619