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