1 /****************************************************************************
2 * This module is based on Twm, but has been siginificantly modified
3 * by Rob Nation
4 *
5 * slightly modified by Bo Yang
6 *
7 ****************************************************************************/
8 /*****************************************************************************/
9 /** Copyright 1988 by Evans & Sutherland Computer Corporation, **/
10 /** Salt Lake City, Utah **/
11 /** Portions Copyright 1989 by the Massachusetts Institute of Technology **/
12 /** Cambridge, Massachusetts **/
13 /** **/
14 /** All Rights Reserved **/
15 /** **/
16 /** Permission to use, copy, modify, and distribute this software and **/
17 /** its documentation for any purpose and without fee is hereby **/
18 /** granted, provided that the above copyright notice appear in all **/
19 /** copies and that both that copyright notice and this permis- **/
20 /** sion notice appear in supporting documentation, and that the **/
21 /** names of Evans & Sutherland and M.I.T. not be used in advertising **/
22 /** in publicity pertaining to distribution of the software without **/
23 /** specific, written prior permission. **/
24 /** **/
25 /** EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD **/
26 /** TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- **/
27 /** ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR **/
28 /** M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM- **/
29 /** AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **/
30 /** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **/
31 /** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **/
32 /** OR PERFORMANCE OF THIS SOFTWARE. **/
33 /*****************************************************************************/
34
35
36 /**************************************************************************
37 *
38 * Assorted odds and ends
39 *
40 **************************************************************************/
41
42
43 #include "../configure.h"
44
45 #include <stdio.h>
46 #include <string.h>
47 #include <sys/types.h>
48 #include <sys/time.h>
49 #include <unistd.h>
50 #include <signal.h>
51 #include <limits.h>
52
53 #include "afterstep.h"
54 #include <X11/Xatom.h>
55 #include "menus.h"
56 #include "misc.h"
57 #include "parse.h"
58 #include "screen.h"
59 #include "module.h"
60
61
62
63 /**************************************************************************
64 *
65 * Releases dynamically allocated space used to store window/icon names
66 *
67 **************************************************************************/
free_window_names(ASWindow * tmp,Bool nukename,Bool nukeicon)68 void free_window_names (ASWindow *tmp, Bool nukename, Bool nukeicon)
69 {
70 if (!tmp)
71 return;
72
73 if (nukename && nukeicon)
74 {
75 if (tmp->name == tmp->icon_name)
76 {
77 if (tmp->name != NoName)
78 XFree (tmp->name);
79 tmp->name = NULL;
80 tmp->icon_name = NULL;
81 }
82 else
83 {
84 if (tmp->name != NoName)
85 XFree (tmp->name);
86 tmp->name = NULL;
87 if (tmp->icon_name != NoName)
88 XFree (tmp->icon_name);
89 tmp->icon_name = NULL;
90 }
91 }
92 else if (nukename)
93 {
94 if (tmp->name != tmp->icon_name && tmp->name != NoName)
95 XFree (tmp->name);
96 tmp->name = NULL;
97 }
98 else
99 { /* if (nukeicon) */
100 if (tmp->icon_name != tmp->name && tmp->icon_name != NoName)
101 XFree (tmp->icon_name);
102 tmp->icon_name = NULL;
103 }
104
105 return;
106 }
107
108 /***************************************************************************
109 *
110 * Handles destruction of a window
111 *
112 ****************************************************************************/
Destroy(ASWindow * Tmp_win)113 void Destroy(ASWindow *Tmp_win)
114 {
115 int i;
116 extern ASWindow *ButtonWindow;
117 extern ASWindow *colormap_win;
118 /*
119 * Warning, this is also called by HandleUnmapNotify; if it ever needs to
120 * look at the event, HandleUnmapNotify will have to mash the UnmapNotify
121 * into a DestroyNotify.
122 */
123 if(!Tmp_win)
124 return;
125
126 XUnmapWindow(dpy, Tmp_win->frame);
127 XSync(dpy,0);
128
129 if(Tmp_win == Scr.Hilite)
130 Scr.Hilite = NULL;
131
132 Broadcast(M_DESTROY_WINDOW,3,Tmp_win->w,Tmp_win->frame,
133 (unsigned long)Tmp_win,0,0,0,0);
134
135 if(Scr.PreviousFocus == Tmp_win)
136 Scr.PreviousFocus = NULL;
137
138 if(ButtonWindow == Tmp_win)
139 ButtonWindow = NULL;
140
141 if((Tmp_win == Scr.Focus)&&(Scr.flags & ClickToFocus))
142 {
143 ASWindow *t, *tn = NULL;
144 long best = LONG_MIN;
145 for (t = Scr.ASRoot.next; t != NULL; t = t->next)
146 {
147 if ((t->focus_sequence > best) && (t != Tmp_win))
148 {
149 best = t->focus_sequence;
150 tn = t;
151 }
152 }
153 if (tn)
154 SetFocus(tn->w, tn, False);
155 else
156 SetFocus(Scr.NoFocusWin, NULL, False);
157 }
158
159 if(Scr.Focus == Tmp_win)
160 SetFocus(Scr.NoFocusWin, NULL, False);
161
162 if(Tmp_win == Scr.pushed_window)
163 Scr.pushed_window = NULL;
164
165 if(Tmp_win == colormap_win)
166 colormap_win = NULL;
167
168 XDestroyWindow(dpy, Tmp_win->frame);
169 XDeleteContext(dpy, Tmp_win->frame, ASContext);
170
171 XDestroyWindow(dpy, Tmp_win->Parent);
172
173 XDeleteContext(dpy, Tmp_win->Parent, ASContext);
174
175 XDeleteContext(dpy, Tmp_win->w, ASContext);
176
177
178 #ifndef NO_PAGER
179 if ((Scr.Pager_w)&& !(Tmp_win->flags & STICKY))
180 XDestroyWindow(dpy, Tmp_win->pager_view);
181 #endif
182
183 if((Tmp_win->flags &ICON_OURS)&&(Tmp_win->icon_pixmap_w != None))
184 XDestroyWindow(dpy, Tmp_win->icon_pixmap_w);
185 if(Tmp_win->icon_pixmap_w != None)
186 XDeleteContext(dpy, Tmp_win->icon_pixmap_w, ASContext);
187
188 if (Tmp_win->flags & TITLE)
189 {
190 XDeleteContext(dpy, Tmp_win->title_w, ASContext);
191 for(i=0;i<Scr.nr_left_buttons;i++)
192 XDeleteContext(dpy, Tmp_win->left_w[i], ASContext);
193 for(i=0;i<Scr.nr_right_buttons;i++)
194 if(Tmp_win->right_w[i] != None)
195 XDeleteContext(dpy, Tmp_win->right_w[i], ASContext);
196 }
197 if (Tmp_win->flags & BORDER)
198 {
199 XDeleteContext(dpy, Tmp_win->side, ASContext);
200 for(i=0;i<2;i++)
201 XDeleteContext(dpy, Tmp_win->corners[i], ASContext);
202 }
203
204 Tmp_win->prev->next = Tmp_win->next;
205 if (Tmp_win->next != NULL)
206 Tmp_win->next->prev = Tmp_win->prev;
207 free_window_names (Tmp_win, True, True);
208 if (Tmp_win->wmhints)
209 XFree ((char *)Tmp_win->wmhints);
210 if (Tmp_win->class.res_name && Tmp_win->class.res_name != NoName)
211 XFree ((char *)Tmp_win->class.res_name);
212 if (Tmp_win->class.res_class && Tmp_win->class.res_class != NoName)
213 XFree ((char *)Tmp_win->class.res_class);
214 if(Tmp_win->mwm_hints)
215 XFree((char *)Tmp_win->mwm_hints);
216
217 if(Tmp_win->cmap_windows != (Window *)NULL)
218 XFree((void *)Tmp_win->cmap_windows);
219 #ifdef ENABLE_TEXTURE
220 if (Tmp_win->backPixmap != None)
221 XFreePixmap(dpy, Tmp_win->backPixmap);
222 if (Tmp_win->backPixmap2 != None)
223 XFreePixmap(dpy, Tmp_win->backPixmap2);
224 #endif
225
226 free((char *)Tmp_win);
227
228 #ifndef NO_PAGER
229 RedrawPager();
230 #endif
231 XSync(dpy,0);
232 return;
233 }
234
235
236
237 /**************************************************************************
238 *
239 * Removes expose events for a specific window from the queue
240 *
241 *************************************************************************/
flush_expose(Window w)242 int flush_expose (Window w)
243 {
244 XEvent dummy;
245 int i=0;
246
247 while (XCheckTypedWindowEvent (dpy, w, Expose, &dummy))i++;
248 return i;
249 }
250
251
252
253 /***********************************************************************
254 *
255 * Procedure:
256 * RestoreWithdrawnLocation
257 *
258 * Puts windows back where they were before afterstep took over
259 *
260 ************************************************************************/
RestoreWithdrawnLocation(ASWindow * tmp,Bool restart)261 void RestoreWithdrawnLocation (ASWindow *tmp,Bool restart)
262 {
263 int a,b,w2,h2;
264 unsigned int bw,mask;
265 XWindowChanges xwc;
266
267 if(!tmp)
268 return;
269
270 if (XGetGeometry (dpy, tmp->w, &JunkRoot, &xwc.x, &xwc.y,
271 &JunkWidth, &JunkHeight, &bw, &JunkDepth))
272 {
273 XTranslateCoordinates(dpy,tmp->frame,Scr.Root,xwc.x,xwc.y,
274 &a,&b,&JunkChild);
275 xwc.x = a + tmp->xdiff;
276 xwc.y = b + tmp->ydiff;
277 xwc.border_width = tmp->old_bw;
278 mask = (CWX | CWY|CWBorderWidth);
279
280 /* We can not assume that the window is currently on the screen.
281 * Although this is normally the case, it is not always true. The
282 * most common example is when the user does something in an
283 * application which will, after some amount of computational delay,
284 * cause the window to be unmapped, but then switches screens before
285 * this happens. The XTranslateCoordinates call above will set the
286 * window coordinates to either be larger than the screen, or negative.
287 * This will result in the window being placed in odd, or even
288 * unviewable locations when the window is remapped. The followin code
289 * forces the "relative" location to be within the bounds of the display.
290 *
291 * gpw -- 11/11/93
292 *
293 * Unfortunately, this does horrendous things during re-starts,
294 * hence the "if(restart) clause (RN)
295 *
296 * Also, fixed so that it only does this stuff if a window is more than
297 * half off the screen. (RN)
298 */
299
300 if(!restart)
301 {
302 /* Don't mess with it if its partially on the screen now */
303 if((tmp->frame_x < 0)||(tmp->frame_y<0)||
304 (tmp->frame_x >= Scr.MyDisplayWidth)||
305 (tmp->frame_y >= Scr.MyDisplayHeight))
306 {
307 w2 = (tmp->frame_width>>1);
308 h2 = (tmp->frame_height>>1);
309 if (( xwc.x < -w2) || (xwc.x > (Scr.MyDisplayWidth-w2 )))
310 {
311 xwc.x = xwc.x % Scr.MyDisplayWidth;
312 if ( xwc.x < -w2 )
313 xwc.x += Scr.MyDisplayWidth;
314 }
315 if ((xwc.y < -h2) || (xwc.y > (Scr.MyDisplayHeight-h2 )))
316 {
317 xwc.y = xwc.y % Scr.MyDisplayHeight;
318 if ( xwc.y < -h2 )
319 xwc.y += Scr.MyDisplayHeight;
320 }
321 }
322 }
323 XReparentWindow (dpy, tmp->w,Scr.Root,xwc.x,xwc.y);
324
325 if((tmp->flags & ICONIFIED)&&(!(tmp->flags & SUPPRESSICON)))
326 {
327 if (tmp->icon_pixmap_w)
328 XUnmapWindow(dpy, tmp->icon_pixmap_w);
329 }
330
331 XConfigureWindow (dpy, tmp->w, mask, &xwc);
332 XSync(dpy,0);
333 }
334 }
335
336
337 /***************************************************************************
338 *
339 * Start/Stops the auto-raise timer
340 *
341 ****************************************************************************/
SetTimer(int delay)342 void SetTimer(int delay)
343 {
344 #ifdef HAVE_SETITIMER
345 struct itimerval value;
346
347 value.it_value.tv_usec = 1000*(delay%1000);
348 value.it_value.tv_sec = delay/1000;
349 value.it_interval.tv_usec = 0;
350 value.it_interval.tv_sec = 0;
351 setitimer(ITIMER_REAL,&value,NULL);
352 #endif
353 }
354
355 /***************************************************************************
356 *
357 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
358 * client messages will have the following form:
359 *
360 * event type ClientMessage
361 * message type _XA_WM_PROTOCOLS
362 * window tmp->w
363 * format 32
364 * data[0] message atom
365 * data[1] time stamp
366 *
367 ****************************************************************************/
send_clientmessage(Window w,Atom a,Time timestamp)368 void send_clientmessage (Window w, Atom a, Time timestamp)
369 {
370 XClientMessageEvent ev;
371
372 ev.type = ClientMessage;
373 ev.window = w;
374 ev.message_type = _XA_WM_PROTOCOLS;
375 ev.format = 32;
376 ev.data.l[0] = a;
377 ev.data.l[1] = timestamp;
378 XSendEvent (dpy, w, False, 0L, (XEvent *) &ev);
379 }
380
381
382
383
384
385 /****************************************************************************
386 *
387 * Records the time of the last processed event. Used in XSetInputFocus
388 *
389 ****************************************************************************/
390 Time lastTimestamp = CurrentTime; /* until Xlib does this for us */
391
StashEventTime(XEvent * ev)392 Bool StashEventTime (XEvent *ev)
393 {
394 Time NewTimestamp = CurrentTime;
395
396 switch (ev->type)
397 {
398 case KeyPress:
399 case KeyRelease:
400 NewTimestamp = ev->xkey.time;
401 break;
402 case ButtonPress:
403 case ButtonRelease:
404 NewTimestamp = ev->xbutton.time;
405 break;
406 case MotionNotify:
407 NewTimestamp = ev->xmotion.time;
408 break;
409 case EnterNotify:
410 case LeaveNotify:
411 NewTimestamp = ev->xcrossing.time;
412 break;
413 case PropertyNotify:
414 NewTimestamp = ev->xproperty.time;
415 break;
416 case SelectionClear:
417 NewTimestamp = ev->xselectionclear.time;
418 break;
419 case SelectionRequest:
420 NewTimestamp = ev->xselectionrequest.time;
421 break;
422 case SelectionNotify:
423 NewTimestamp = ev->xselection.time;
424 break;
425 default:
426 return False;
427 }
428 if(NewTimestamp > lastTimestamp)
429 lastTimestamp = NewTimestamp;
430 return True;
431 }
432
433
434 /******************************************************************************
435 *
436 * Move a window to the top (dir 1) or bottom (dir -1) of the circulate seq.
437 *
438 *****************************************************************************/
439
SetCirculateSequence(ASWindow * tw,int dir)440 void SetCirculateSequence(ASWindow* tw, int dir)
441 {
442 ASWindow *t;
443 long best = (dir == -1) ? LONG_MAX : LONG_MIN;
444
445 t = Scr.ASRoot.next;
446 if (t)
447 {
448 do
449 {
450 if ((dir == -1) ? (t->circulate_sequence < best)
451 : (t->circulate_sequence > best))
452 best = t->circulate_sequence;
453 } while ((t = t->next));
454 }
455 else
456 best = 0;
457
458 tw->circulate_sequence = best + dir;
459 }
460
461
462
463 /******************************************************************************
464 *
465 * Versions of grab primitives that circumvent modifier problems
466 *
467 *****************************************************************************/
468
469 unsigned mygrabs_no_mods[] = { 0 };
470
MyXGrabButton(Display * display,unsigned button,unsigned modifiers,Window grab_window,Bool owner_events,unsigned event_mask,int pointer_mode,int keyboard_mode,Window confine_to,Cursor cursor)471 void MyXGrabButton(Display* display, unsigned button, unsigned modifiers,
472 Window grab_window, Bool owner_events, unsigned event_mask,
473 int pointer_mode, int keyboard_mode, Window confine_to,
474 Cursor cursor)
475 {
476 unsigned mod, *mods;
477 mods = (modifiers != AnyModifier) ? Scr.lock_mods : mygrabs_no_mods;
478 do {
479 mod = *mods++;
480 XGrabButton(display, button, modifiers | mod, grab_window,
481 owner_events, event_mask, pointer_mode, keyboard_mode,
482 confine_to, cursor);
483 } while (mod);
484 }
485
MyXUngrabButton(Display * display,unsigned button,unsigned modifiers,Window grab_window)486 void MyXUngrabButton(Display* display, unsigned button, unsigned modifiers,
487 Window grab_window)
488 {
489 unsigned mod, *mods;
490 mods = (modifiers != AnyModifier) ? Scr.lock_mods : mygrabs_no_mods;
491 do {
492 mod = *mods++;
493 XUngrabButton(display, button, modifiers | mod, grab_window);
494 } while (mod);
495 }
496
MyXGrabKey(Display * display,int keycode,unsigned modifiers,Window grab_window,Bool owner_events,int pointer_mode,int keyboard_mode)497 void MyXGrabKey(Display* display, int keycode, unsigned modifiers,
498 Window grab_window, Bool owner_events, int pointer_mode,
499 int keyboard_mode)
500 {
501 unsigned mod, *mods;
502 mods = (modifiers != AnyModifier) ? Scr.lock_mods : mygrabs_no_mods;
503 do {
504 mod = *mods++;
505 XGrabKey(display, keycode, modifiers | mod, grab_window, owner_events,
506 pointer_mode, keyboard_mode);
507 } while (mod);
508 }
509
510 /******************************************************************************
511 *
512 * Grab ClickToRaise button press events for a window
513 *
514 *****************************************************************************/
GrabRaiseClick(ASWindow * t)515 void GrabRaiseClick(ASWindow* t)
516 {
517 int b;
518 for (b = 1; b <= MAX_BUTTONS; b++)
519 {
520 if (Scr.RaiseButtons & (1<<b))
521 MyXGrabButton(dpy, b, 0, t->w, True, ButtonPressMask, GrabModeSync,
522 GrabModeAsync, None, Scr.ASCursors[TITLE_CURSOR]);
523 }
524 }
525
526 /******************************************************************************
527 *
528 * Ungrab ClickToRaise button press events to allow their use in applications
529 *
530 *****************************************************************************/
UngrabRaiseClick(ASWindow * t)531 void UngrabRaiseClick(ASWindow* t)
532 {
533 int b;
534 for (b = 1; b <= MAX_BUTTONS; b++)
535 {
536 if (Scr.RaiseButtons & (1<<b))
537 MyXUngrabButton(dpy, b, 0, t->w);
538 }
539 }
540
541 /******************************************************************************
542 *
543 * Recalculate the visibility flags
544 *
545 *****************************************************************************/
546
UpdateVisibility(void)547 void UpdateVisibility(void)
548 {
549 ASWindow *t, *s, *tbase;
550 int tx1, ty1, tx2, ty2;
551 int ontop = 1, visible;
552
553 tbase = Scr.ASRoot.next;
554 for (t = Scr.ASRoot.next; t != NULL; t = t->next)
555 {
556 if (t->flags & MAPPED)
557 {
558 tx1 = t->frame_x;
559 ty1 = t->frame_y;
560 tx2 = t->frame_x + t->frame_width;
561 ty2 = t->frame_y + t->frame_height;
562 }
563 else if (t->flags & ICONIFIED)
564 {
565 tx1 = t->icon_x_loc;
566 ty1 = t->icon_y_loc;
567 tx2 = t->icon_x_loc + t->icon_p_width;
568 ty2 = t->icon_y_loc + t->icon_p_height;
569 }
570 else if (t->flags & SHADED)
571 {
572 tx1 = t->frame_x;
573 ty1 = t->frame_y;
574 tx2 = t->frame_x + t->frame_width;
575 ty2 = t->frame_y + NS_TITLE_HEIGHT;
576 }
577 else continue;
578
579 if (ontop && !(t->flags & ONTOP))
580 {
581 ontop = 0;
582 tbase = t;
583 }
584 if ((tx2 > 0) && (tx1 < Scr.MyDisplayWidth) &&
585 (ty2 > 0) && (ty1 < Scr.MyDisplayHeight))
586 {
587 visible = VISIBLE;
588 for (s = tbase; s != t; s = s->next)
589 {
590 if ((s->flags & TRANSIENT) && (s->transientfor == t->w))
591 continue;
592 if (s->flags & MAPPED)
593 {
594 if ((tx2 > s->frame_x)&&(tx1 < s->frame_x+s->frame_width)&&
595 (ty2 > s->frame_y)&&(ty1 < s->frame_y+s->frame_height))
596 {
597 visible = 0;
598 break;
599 }
600 }
601 else if (s->flags & ICONIFIED)
602 {
603 if ((tx2 > s->icon_x_loc)&&(tx1 < s->icon_x_loc+s->icon_p_width) &&
604 (ty2 > s->icon_y_loc)&&(ty1 < s->icon_y_loc+s->icon_p_height))
605 {
606 visible = 0;
607 break;
608 }
609 }
610 else if (s->flags & SHADED)
611 {
612 if ((tx2 > s->frame_x)&&(tx1 < s->frame_x+s->frame_width)&&
613 (ty2 > s->frame_y)&&(ty1 < s->frame_y+NS_TITLE_HEIGHT))
614
615 {
616 visible = 0;
617 break;
618 }
619 }
620
621 }
622 }
623 else
624 visible = 0;
625 if ((t->flags & VISIBLE) != visible)
626 {
627 t->flags ^= VISIBLE;
628 if ((Scr.flags & ClickToRaise) && ! (Scr.flags & ClickToFocus)
629 && (t->flags & MAPPED))
630 {
631 if (visible)
632 UngrabRaiseClick(t);
633 else
634 GrabRaiseClick(t);
635 }
636 }
637 }
638 }
639
640 /******************************************************************************
641 *
642 * Get the correct window stacking order from the X server and
643 * make sure everything that depends on the order is fine and dandy
644 *
645 *****************************************************************************/
646
CorrectStackOrder(void)647 void CorrectStackOrder(void)
648 {
649 Window root, parent, *children, *cp;
650 unsigned nchildren;
651 ASWindow *t;
652
653 if (XQueryTree(dpy, Scr.ASRoot.w, &root, &parent, &children,
654 &nchildren))
655 {
656 for (cp = children; nchildren-- > 0; cp++)
657 {
658 if ((XFindContext(dpy, *cp, ASContext, (caddr_t*) &t) == XCSUCCESS)
659 && (t->frame == *cp))
660 {
661 if (t != Scr.ASRoot.next)
662 {
663 t->prev->next = t->next;
664 if (t->next) t->next->prev = t->prev;
665 t->next = Scr.ASRoot.next;
666 t->prev = &Scr.ASRoot;
667 Scr.ASRoot.next->prev = t;
668 Scr.ASRoot.next = t;
669 }
670 }
671 }
672 XFree(children);
673 }
674 else
675 {
676 fprintf(stderr, "CorrectStackOrder(): XQueryTree failed!\n");
677 }
678
679 t = Scr.ASRoot.next;
680 if (t)
681 {
682 if (t->flags & ONTOP)
683 {
684 do { t = t->next; } while (t && (t->flags & ONTOP));
685 if (t)
686 {
687 do { t = t->next; } while (t && ! (t->flags & ONTOP));
688 if (t) RaiseWindow(Scr.ASRoot.next);
689 }
690 }
691 else
692 {
693 do { t = t->next; } while (t && ! (t->flags & ONTOP));
694 if (t) RaiseWindow(t);
695 }
696 }
697
698 UpdateVisibility();
699 }
700