1 /****************************************************************************
2  * This module is based on Twm, but has been siginificantly modified
3  * by Rob Nation
4  *
5  * later modified for BowMan
6  * by Bo Yang
7  *
8  * modified slightly for AfterStep
9  * by Frank Fejes
10  *
11  * small modifications for textured backgrounds
12  * by Alfredo Kojima
13  ****************************************************************************/
14 /*****************************************************************************/
15 /**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
16 /**                          Salt Lake City, Utah                           **/
17 /**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
18 /**                        Cambridge, Massachusetts                         **/
19 /**                                                                         **/
20 /**                           All Rights Reserved                           **/
21 /**                                                                         **/
22 /**    Permission to use, copy, modify, and distribute this software and    **/
23 /**    its documentation  for  any  purpose  and  without  fee is hereby    **/
24 /**    granted, provided that the above copyright notice appear  in  all    **/
25 /**    copies and that both  that  copyright  notice  and  this  permis-    **/
26 /**    sion  notice appear in supporting  documentation,  and  that  the    **/
27 /**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
28 /**    in publicity pertaining to distribution of the  software  without    **/
29 /**    specific, written prior permission.                                  **/
30 /**                                                                         **/
31 /**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
32 /**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
33 /**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
34 /**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
35 /**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
36 /**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
37 /**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
38 /**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
39 /*****************************************************************************/
40 
41 
42 /***********************************************************************
43  * AfterStep: WM with NEXTSTEP look and feel
44  ***********************************************************************/
45 
46 #include "../configure.h"
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <signal.h>
51 #include <fcntl.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include "afterstep.h"
55 #include "menus.h"
56 #include "misc.h"
57 #include "screen.h"
58 #include "parse.h"
59 #include <X11/Xproto.h>
60 #include <X11/Xatom.h>
61 /* need to get prototype for XrmUniqueQuark for XUniqueContext call */
62 #include <X11/Xresource.h>
63 #include <X11/Xutil.h>
64 
65 #ifdef SHAPE
66 #include <X11/extensions/shape.h>
67 #endif /* SHAPE */
68 
69 #if defined (sparc) && defined (SVR4)
70 /* Solaris has sysinfo instead of gethostname.  */
71 #include <sys/systeminfo.h>
72 #endif
73 
74 #define MAXHOSTNAME 255
75 
76 #include "../version.h"
77 
78 #ifdef ENABLE_TEXTURE
79 TextureInfo Textures;		/* texture information */
80 #endif
81 
82 ScreenInfo Scr;		        /* structures for the screen */
83 Display *dpy;			/* which display are we talking to */
84 extern char *config_file;
85 
86 XErrorHandler CatchRedirectError(Display *, XErrorEvent *);
87 XErrorHandler ASErrorHandler(Display *, XErrorEvent *);
88 void newhandler(int sig);
89 void InitModifiers(void);
90 void CreateCursors(void);
91 void NoisyExit(int);
92 void ChildDied(int nonsense);
93 void SaveDesktopState(void);
94 
95 XContext ASContext;		/* context for afterstep windows */
96 XContext MenuContext;		/* context for afterstep menus */
97 
98 XClassHint NoClass;		/* for applications with no class */
99 
100 int JunkX = 0, JunkY = 0;
101 Window JunkRoot, JunkChild;		/* junk window */
102 unsigned int JunkWidth, JunkHeight, JunkBW, JunkDepth, JunkMask;
103 
104 /* assorted gray bitmaps for decorative borders */
105 #define g_width 2
106 #define g_height 2
107 static char g_bits[] = {0x02, 0x01};
108 
109 #define l_g_width 4
110 #define l_g_height 2
111 static char l_g_bits[] = {0x08, 0x02};
112 Bool debugging = False,PPosOverride;
113 
114 #define s_g_width 4
115 #define s_g_height 4
116 static char s_g_bits[] = {0x01, 0x02, 0x04, 0x08};
117 char **g_argv;
118 
119 #ifdef M4
120 int  m4_enable;			/* use m4? */
121 int  m4_prefix;			/* Do GNU m4 prefixing (-P) */
122 char m4_options[BUFSIZ];	/* Command line options to m4 */
123 char m4_prog[BUFSIZ];		/* Name of the m4 program */
124 int  m4_default_quotes;		/* Use default m4 quotes */
125 char m4_startquote[16];		/* Left quote characters for m4 */
126 char m4_endquote[16];		/* Right quote characters for m4 */
127 #endif
128 
129 #ifndef NO_PAGER
130 extern Pixel PagerForeColor;
131 #endif
132 
133 #ifdef SHAPE
134 int ShapeEventBase, ShapeErrorBase;
135 #endif
136 
137 long isIconicState = 0;
138 extern XEvent Event;
139 Bool Restarting = False;
140 int fd_width, x_fd;
141 char *display_name = NULL;
142 
143 /***********************************************************************
144  *
145  *  Procedure:
146  *	main - start of afterstep
147  *
148  ***********************************************************************
149  */
main(int argc,char ** argv)150 int main(int argc, char **argv)
151 {
152     unsigned long valuemask;	/* mask for create windows */
153     XSetWindowAttributes attributes;	/* attributes for create windows */
154     void InternUsefulAtoms (void);
155     void InitVariables(void);
156     void enterAlarm(int);
157     int i;
158     extern int x_fd;
159     int len;
160     char *display_string;
161     char message[255];
162     char num[10];
163 
164     Bool single = False;
165     Bool option_error = FALSE;
166 
167 #ifdef M4
168     /* Set the defaults for m4 processing */
169 
170     m4_enable = TRUE;
171     m4_prefix = FALSE;
172     strcpy(m4_prog, "m4");
173     *m4_options = '\0';
174     m4_default_quotes = 1;
175     strcpy(m4_startquote, "`");
176     strcpy(m4_endquote, "'");
177 #endif
178 
179     for (i = 1; i < argc; i++)
180       {
181 	if (mystrncasecmp(argv[i],"-debug",6)==0)
182 	  debugging = True;
183 	else if (mystrncasecmp(argv[i],"-s",2)==0)
184 	  {
185 	    single = True;
186 	  }
187 	else if (mystrncasecmp(argv[i],"-d",2)==0)
188 	  {
189 	    if (++i >= argc)
190 	      usage();
191 	    display_name = argv[i];
192 	  }
193 	else if (mystrncasecmp(argv[i],"-f",2)==0)
194 	 {
195 	    if (++i >= argc)
196 	      usage();
197 	    config_file = argv[i];
198 	  }
199 #ifdef M4
200 	else if (mystrncasecmp(argv[i], "-no-m4", 6) == 0)
201 	  {
202 	    m4_enable = FALSE;
203 	  }
204 	else if (mystrncasecmp(argv[i], "-m4-prefix", 10) == 0)
205 	  {
206 	    m4_prefix = TRUE;
207 	  }
208 	else if (mystrncasecmp(argv[i],"-m4opt", 6) == 0)
209 	  {
210 	    if (++i < argc)
211 	      {
212 		strcat(m4_options, argv[i]);
213 		strcat(m4_options, " ");
214 	      }
215 	  }
216 	else if (mystrncasecmp(argv[i], "-m4-squote", 6) == 0)
217 	  {
218 	    if (++i < argc)
219 	      {
220 		strcpy(m4_startquote, argv[i]);
221 		m4_default_quotes = 0;
222 	      }
223 	  }
224 	else if (mystrncasecmp(argv[i], "-m4-equote", 6) == 0)
225 	  {
226 	    if (++i < argc)
227 	      {
228 		strcpy(m4_endquote, argv[i]);
229 		m4_default_quotes = 0;
230 	      }
231 	  }
232 	else if (mystrncasecmp(argv[i], "-m4prog", 7) == 0)
233 	  {
234 	    if (++i < argc)
235 	      {
236 		strcpy(m4_prog, argv[i]);
237 	      }
238 	  }
239 #endif
240 	else if (mystrncasecmp(argv[i], "-version", 8) == 0)
241 	  {
242 	    fprintf(stderr, "AfterStep Version %s\n", VERSION);
243 	  }
244 	else
245 	  {
246 	    fprintf(stderr, "afterstep:  Unknown option:  `%s'\n", argv[i]);
247 	    option_error = TRUE;
248 	  }
249       }
250 
251     if (option_error)
252       {
253 	usage();
254       }
255 
256     g_argv = argv;
257 
258     newhandler (SIGINT);
259     newhandler (SIGHUP);
260     newhandler (SIGQUIT);
261     newhandler (SIGTERM);
262     signal (SIGUSR1, Restart);
263 
264     signal (SIGPIPE, DeadPipe);
265     signal(SIGALRM,enterAlarm);
266 
267     ReapChildren();
268 
269     if (!(dpy = XOpenDisplay(display_name)))
270       {
271 	afterstep_err("can't open display %s", XDisplayName(display_name),
272 		 NULL,NULL);
273 	exit (1);
274       }
275     x_fd = XConnectionNumber(dpy);
276 
277     if (fcntl(x_fd, F_SETFD, 1) == -1)
278       {
279 	afterstep_err("close-on-exec failed",NULL,NULL,NULL);
280 	exit (1);
281       }
282     Scr.screen= DefaultScreen(dpy);
283     Scr.NumberOfScreens = ScreenCount(dpy);
284 
285     if(!single)
286       {
287 	for(i=0;i<Scr.NumberOfScreens;i++)
288 	  {
289 	    if(i!= Scr.screen)
290 	      {
291 		sprintf(message,"%s -d %s",argv[0],XDisplayString(dpy));
292 		len = strlen(message);
293 		message[len-1] = 0;
294 		sprintf(num,"%d",i);
295 		strcat(message,num);
296 		strcat(message," -s ");
297 		if(debugging)
298 		  strcat(message," -debug");
299 		strcat(message," -f ");
300 		strcat(message,config_file);
301 		strcat(message," &\n");
302 		system(message);
303 	      }
304 	  }
305       }
306 
307 
308     /*  Add a DISPLAY entry to the environment, incase we were started
309      * with afterstep -display term:0.0
310      */
311     len = strlen(XDisplayString(dpy));
312     display_string = safemalloc(len+10);
313     sprintf(display_string,"DISPLAY=%s",XDisplayString(dpy));
314     putenv(display_string);
315     /* Add a HOSTDISPLAY environment variable, which is the same as
316      * DISPLAY, unless display = :0.0 or unix:0.0, in which case the full
317      * host name will be used for ease in networking . */
318     if(strncmp(display_string,"DISPLAY=:",9)==0)
319       {
320 	char client[MAXHOSTNAME], *rdisplay_string;
321 	mygethostname(client,MAXHOSTNAME);
322 	rdisplay_string = safemalloc(len+14 + strlen(client));
323 	sprintf(rdisplay_string,"HOSTDISPLAY=%s:%s",client,&display_string[9]);
324 	putenv(rdisplay_string);
325       }
326     else if(strncmp(display_string,"DISPLAY=unix:",13)==0)
327       {
328 	char client[MAXHOSTNAME], *rdisplay_string;
329 	mygethostname(client,MAXHOSTNAME);
330 	rdisplay_string = safemalloc(len+14 + strlen(client));
331 	sprintf(rdisplay_string,"HOSTDISPLAY=%s:%s",client,
332 		&display_string[13]);
333 	putenv(rdisplay_string);
334       }
335     else
336       {
337 	char *rdisplay_string;
338 	rdisplay_string = safemalloc(len+14);
339 	sprintf(rdisplay_string,"HOSTDISPLAY=%s",XDisplayString(dpy));
340 	putenv(rdisplay_string);
341       }
342 
343     Scr.Root = RootWindow(dpy, Scr.screen);
344     if(Scr.Root == None)
345       {
346 	afterstep_err("Screen %d is not a valid screen",(char *)Scr.screen,
347 		 NULL,NULL);
348 	exit(1);
349       }
350 
351 #ifdef SHAPE
352     XShapeQueryExtension(dpy, &ShapeEventBase, &ShapeErrorBase);
353 #endif /* SHAPE */
354 
355     InternUsefulAtoms ();
356 
357     /* Make sure property priority colors is empty */
358     XChangeProperty (dpy, Scr.Root, _XA_MIT_PRIORITY_COLORS,
359 		     XA_CARDINAL, 32, PropModeReplace, NULL, 0);
360 
361     XSetErrorHandler((XErrorHandler)CatchRedirectError);
362     XSelectInput(dpy, Scr.Root,
363 		 LeaveWindowMask| EnterWindowMask | PropertyChangeMask |
364 		 SubstructureRedirectMask | /* SubstructureNotifyMask | */
365 		 KeyPressMask | ButtonPressMask | ButtonReleaseMask );
366     XSync(dpy, 0);
367 
368     XSetErrorHandler((XErrorHandler)ASErrorHandler);
369 
370     CreateCursors();
371     InitVariables();
372     initModules();
373     XGrabServer(dpy);
374 
375     Scr.gray_bitmap =
376       XCreateBitmapFromData(dpy,Scr.Root,g_bits, g_width,g_height);
377 
378     /* read config file, set up menus, colors, fonts */
379 #ifdef M4
380     MakeMenus(display_name, m4_options);
381 #else
382     MakeMenus(display_name, NULL);
383 #endif
384     if(Scr.d_depth<2)
385       {
386 	Scr.gray_pixmap =
387 	XCreatePixmapFromBitmapData(dpy,Scr.Root,g_bits, g_width,g_height,
388 				    Scr.StdColors.fore,Scr.StdColors.back,
389 				    Scr.d_depth);
390 	Scr.light_gray_pixmap =
391 	XCreatePixmapFromBitmapData(dpy,Scr.Root,l_g_bits,l_g_width,l_g_height,
392 				    Scr.StdColors.fore,Scr.StdColors.back,
393 				    Scr.d_depth);
394 	Scr.sticky_gray_pixmap =
395 	XCreatePixmapFromBitmapData(dpy,Scr.Root,s_g_bits,s_g_width,s_g_height,
396 				   Scr.StickyColors.fore,Scr.StickyColors.back,
397 				   Scr.d_depth);
398       }
399 
400     /* create a window which will accept the keyboard focus when no other
401        windows have it */
402     attributes.event_mask = KeyPressMask|FocusChangeMask;
403     attributes.override_redirect = True;
404     Scr.NoFocusWin=XCreateWindow(dpy,Scr.Root,-10, -10, 10, 10, 0, 0,
405 				 InputOnly,CopyFromParent,
406 				 CWEventMask|CWOverrideRedirect,
407 				 &attributes);
408     XMapWindow(dpy, Scr.NoFocusWin);
409 
410     XSetInputFocus (dpy, Scr.NoFocusWin, RevertToParent, CurrentTime);
411     Scr.TitleHeight=NS_TITLE_HEIGHT;
412 
413     XSync(dpy, 0);
414     if(debugging)
415       XSynchronize(dpy,1);
416 
417     Scr.SizeStringWidth = XTextWidth (Scr.StdFont.font,
418 				      " +8888 x +8888 ", 15);
419     attributes.border_pixel = Scr.StdColors.fore;
420     attributes.background_pixel = Scr.StdColors.back;
421     attributes.bit_gravity = NorthWestGravity;
422     valuemask = (CWBorderPixel | CWBackPixel | CWBitGravity);
423     Scr.SizeWindow = XCreateWindow (dpy, Scr.Root,
424 					(DisplayWidth(dpy, Scr.screen) -
425 					(unsigned int)(Scr.SizeStringWidth +
426 						       SIZE_HINDENT*2))/2,
427 					(DisplayHeight(dpy, Scr.screen) -
428 					(unsigned int) (Scr.StdFont.height +
429 							SIZE_VINDENT*2))/2,
430 					(unsigned int)(Scr.SizeStringWidth +
431 						       SIZE_HINDENT*2),
432 					(unsigned int) (Scr.StdFont.height +
433 							SIZE_VINDENT*2),
434 					(unsigned int) 0, 0,
435 					(unsigned int) CopyFromParent,
436 					(Visual *) CopyFromParent,
437 					valuemask, &attributes);
438 
439 #ifndef NON_VIRTUAL
440     initPanFrames();
441 #endif
442     CaptureAllWindows();
443 #ifndef NON_VIRTUAL
444     checkPanFrames();
445 #endif
446     XUngrabServer(dpy);
447     MoveResizeViewPortIndicator();
448     fd_width = GetFdWidth();
449 
450 
451     if(Restarting)
452       {
453 	if(Scr.RestartFunction != NULL)
454 	  ExecuteFunction(F_FUNCTION,NULL,None,NULL,&Event,C_ROOT,0,0,
455 			  0,0,Scr.RestartFunction,-1);
456       }
457     else
458       {
459 	if(Scr.InitFunction != NULL)
460 	  ExecuteFunction(F_FUNCTION,NULL,None,NULL,&Event,C_ROOT,0,0,
461 			  0,0,Scr.InitFunction,-1);
462       }
463 
464     XDefineCursor(dpy, Scr.Root, Scr.ASCursors[DEFAULT]);
465     HandleEvents();
466     return 0;
467 }
468 
469 /***********************************************************************
470  *
471  *  Procedure:
472  *      CaptureAllWindows
473  *
474  *   Decorates all windows at start-up
475  *
476  ***********************************************************************/
477 
CaptureAllWindows(void)478 void CaptureAllWindows(void)
479 {
480   int i,j;
481   unsigned int nchildren;
482   Window root, parent, *children;
483 
484   PPosOverride = TRUE;
485 
486   if(!XQueryTree(dpy, Scr.Root, &root, &parent, &children, &nchildren))
487     return;
488 
489   /*
490    * weed out icon windows
491    */
492   for (i = 0; i < nchildren; i++)
493     {
494       if (children[i])
495 	{
496 	  XWMHints *wmhintsp = XGetWMHints (dpy, children[i]);
497 	  if (wmhintsp)
498 	    {
499 	      if (wmhintsp->flags & IconWindowHint)
500 		{
501 		  for (j = 0; j < nchildren; j++)
502 		    {
503 		      if (children[j] == wmhintsp->icon_window)
504 			{
505 			  children[j] = None;
506 			  break;
507 			}
508 		    }
509 		}
510 	      XFree ((char *) wmhintsp);
511 	    }
512 	}
513     }
514   /*
515    * map all of the non-override windows
516    */
517 
518   for (i = 0; i < nchildren; i++)
519     {
520       if (children[i] && MappedNotOverride(children[i]))
521 	{
522 	  XUnmapWindow(dpy, children[i]);
523 	  Event.xmaprequest.window = children[i];
524 	  HandleMapRequest ();
525 	}
526     }
527 
528   isIconicState = DontCareState;
529 
530   if(nchildren > 0)
531     XFree((char *)children);
532 
533   /* after the windows already on the screen are in place,
534    * don't use PPosition */
535   PPosOverride = FALSE;
536 }
537 
538 /***********************************************************************
539  *
540  *  Procedure:
541  *	MappedNotOverride - checks to see if we should really
542  *		put a afterstep frame on the window
543  *
544  *  Returned Value:
545  *	TRUE	- go ahead and frame the window
546  *	FALSE	- don't frame the window
547  *
548  *  Inputs:
549  *	w	- the window to check
550  *
551  ***********************************************************************/
552 
MappedNotOverride(Window w)553 int MappedNotOverride(Window w)
554 {
555   XWindowAttributes wa;
556   Atom atype;
557   int aformat;
558   unsigned long nitems, bytes_remain;
559   unsigned char *prop;
560 
561   isIconicState = DontCareState;
562 
563   if(!XGetWindowAttributes(dpy, w, &wa))
564     return False;
565 
566   if(XGetWindowProperty(dpy,w,_XA_WM_STATE,0L,3L,False,_XA_WM_STATE,
567 			&atype,&aformat,&nitems,&bytes_remain,&prop)==Success)
568     {
569       if(prop != NULL)
570 	{
571 	  isIconicState = *(long *)prop;
572 	  XFree(prop);
573 	}
574     }
575 #ifndef NO_PAGER
576   if(w == Scr.Pager_w)
577     return True;
578 #endif
579   return (((isIconicState == IconicState)||(wa.map_state != IsUnmapped)) &&
580 	  (wa.override_redirect != True));
581 }
582 
583 
584 /***********************************************************************
585  *
586  *  Procedure:
587  *	InternUsefulAtoms:
588  *            Dont really know what it does
589  *
590  ***********************************************************************
591  */
592 Atom _XA_MIT_PRIORITY_COLORS;
593 Atom _XA_WM_CHANGE_STATE;
594 Atom _XA_WM_STATE;
595 Atom _XA_WM_COLORMAP_WINDOWS;
596 Atom _XA_WM_PROTOCOLS;
597 Atom _XA_WM_TAKE_FOCUS;
598 Atom _XA_WM_DELETE_WINDOW;
599 Atom _XA_WM_DESKTOP;
600 Atom _XA_MwmAtom;
601 
InternUsefulAtoms(void)602 void InternUsefulAtoms (void)
603 {
604   /*
605    * Create priority colors if necessary.
606    */
607   _XA_MIT_PRIORITY_COLORS = XInternAtom(dpy, "_MIT_PRIORITY_COLORS", False);
608   _XA_WM_CHANGE_STATE = XInternAtom (dpy, "WM_CHANGE_STATE", False);
609   _XA_WM_STATE = XInternAtom (dpy, "WM_STATE", False);
610   _XA_WM_COLORMAP_WINDOWS = XInternAtom (dpy, "WM_COLORMAP_WINDOWS", False);
611   _XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
612   _XA_WM_TAKE_FOCUS = XInternAtom (dpy, "WM_TAKE_FOCUS", False);
613   _XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
614   _XA_WM_DESKTOP = XInternAtom (dpy, "WM_DESKTOP", False);
615   _XA_MwmAtom=XInternAtom(dpy,"_MOTIF_WM_HINTS",False);
616   return;
617 }
618 
619 /***********************************************************************
620  *
621  *  Procedure:
622  *	newhandler: Installs new signal handler
623  *
624  ************************************************************************/
newhandler(int sig)625 void newhandler(int sig)
626 {
627   if (signal (sig, SIG_IGN) != SIG_IGN)
628     signal (sig, SigDone);
629 }
630 
631 
632 /*************************************************************************
633  * Restart on a signal
634  ************************************************************************/
Restart(int nonsense)635 void Restart(int nonsense)
636 {
637   Done(1, *g_argv);
638   SIGNAL_RETURN;
639 }
640 
641 /***********************************************************************
642  *
643  *  Procedure:
644  *	CreateCursors - Loads afterstep cursors
645  *
646  ***********************************************************************
647  */
CreateCursors(void)648 void CreateCursors(void)
649 {
650   /* define cursors */
651   Scr.ASCursors[POSITION] = XCreateFontCursor(dpy,XC_left_ptr);
652 /*  Scr.ASCursors[DEFAULT] = XCreateFontCursor(dpy, XC_top_left_arrow); */
653   Scr.ASCursors[DEFAULT] = XCreateFontCursor(dpy,XC_left_ptr);
654   Scr.ASCursors[SYS] = XCreateFontCursor(dpy, XC_left_ptr);
655   Scr.ASCursors[TITLE_CURSOR] = XCreateFontCursor(dpy, XC_left_ptr);
656   Scr.ASCursors[MOVE] = XCreateFontCursor(dpy, XC_fleur);
657   Scr.ASCursors[MENU] = XCreateFontCursor(dpy, XC_left_ptr);
658   Scr.ASCursors[WAIT] = XCreateFontCursor(dpy, XC_watch);
659   Scr.ASCursors[SELECT] = XCreateFontCursor(dpy, XC_dot);
660   Scr.ASCursors[DESTROY] = XCreateFontCursor(dpy, XC_pirate);
661   Scr.ASCursors[LEFT] = XCreateFontCursor(dpy, XC_left_side);
662   Scr.ASCursors[RIGHT] = XCreateFontCursor(dpy, XC_right_side);
663   Scr.ASCursors[TOP] = XCreateFontCursor(dpy, XC_top_side);
664   Scr.ASCursors[BOTTOM] = XCreateFontCursor(dpy, XC_bottom_side);
665   Scr.ASCursors[TOP_LEFT] = XCreateFontCursor(dpy,XC_top_left_corner);
666   Scr.ASCursors[TOP_RIGHT] = XCreateFontCursor(dpy,XC_top_right_corner);
667   Scr.ASCursors[BOTTOM_LEFT] = XCreateFontCursor(dpy,XC_bottom_left_corner);
668   Scr.ASCursors[BOTTOM_RIGHT] =XCreateFontCursor(dpy,XC_bottom_right_corner);
669 }
670 
671 /***********************************************************************
672  *
673  *  Procedure:
674  *	InitVariables - initialize afterstep variables
675  *
676  ************************************************************************/
InitVariables(void)677 void InitVariables(void)
678 {
679   ASContext = XUniqueContext();
680   MenuContext = XUniqueContext();
681   NoClass.res_name = NoName;
682   NoClass.res_class = NoName;
683 
684   Scr.d_depth = DefaultDepth(dpy, Scr.screen);
685   Scr.ASRoot.w = Scr.Root;
686   Scr.ASRoot.next = 0;
687   XGetWindowAttributes(dpy,Scr.Root,&(Scr.ASRoot.attr));
688   Scr.root_pushes = 0;
689   Scr.pushed_window = &Scr.ASRoot;
690   Scr.ASRoot.number_cmap_windows = 0;
691 
692 
693   Scr.MyDisplayWidth = DisplayWidth(dpy, Scr.screen);
694   Scr.MyDisplayHeight = DisplayHeight(dpy, Scr.screen);
695 
696   Scr.NoBoundaryWidth = 1;
697   Scr.BoundaryWidth = BOUNDARY_WIDTH;
698   Scr.CornerWidth = CORNER_WIDTH;
699   Scr.Hilite = NULL;
700   Scr.Focus = NULL;
701   Scr.Ungrabbed = NULL;
702 
703   Scr.StdFont.font = NULL;
704   Scr.StdFont.name = "fixed";
705   Scr.WindowFont.name = "fixed";
706 
707   Scr.VScale = 32;
708 #ifndef NON_VIRTUAL
709   Scr.VxMax = 3;
710   Scr.VyMax = 3;
711 #else
712   Scr.VxMax = 1;
713   Scr.VyMax = 1;
714 #endif
715   Scr.Vx = Scr.Vy = 0;
716 
717   /* Sets the current desktop number to zero */
718   /* Multiple desks are available even in non-virtual
719    * compilations */
720   {
721     Atom atype;
722     int aformat;
723     unsigned long nitems, bytes_remain;
724     unsigned char *prop;
725 
726     Scr.CurrentDesk = 0;
727     if ((XGetWindowProperty(dpy, Scr.Root, _XA_WM_DESKTOP, 0L, 1L, True,
728 			    _XA_WM_DESKTOP, &atype, &aformat, &nitems,
729 			    &bytes_remain, &prop))==Success)
730       {
731 	if(prop != NULL)
732 	  {
733 	    Restarting = True;
734 	    Scr.CurrentDesk = *(unsigned long *)prop;
735 	  }
736       }
737    }
738 
739   Scr.EdgeScrollX = Scr.EdgeScrollY = -100000;
740   Scr.ScrollResistance = Scr.MoveResistance = 0;
741   Scr.OpaqueSize = 5;
742   Scr.ClickTime = 150;
743   Scr.AutoRaiseDelay = 0;
744   Scr.RaiseButtons = 0;
745 
746   /* set major operating modes */
747   Scr.flags = 0;
748   Scr.NumBoxes = 0;
749 
750   Scr.randomx = Scr.randomy = 0;
751   Scr.buttons2grab = 7;
752 
753   Scr.next_focus_sequence = 0;
754 
755 #ifndef NO_PAGER
756   Scr.PagerFont.name = NULL;
757   Scr.PagerFont.height = 0;
758   Scr.PagerFont.y = 0;
759   Scr.ASPager = (ASWindow *)0;
760   Scr.Pager_w = None;
761   Scr.CPagerWin = None;
762 #endif
763   Scr.InitFunction = NULL;
764   Scr.RestartFunction = NULL;
765 
766   InitModifiers();
767 
768   return;
769 }
770 
771 /* Read the server modifier mapping */
772 
InitModifiers(void)773 void InitModifiers(void)
774 {
775   int m, i, knl;
776   char* kn;
777   KeySym ks;
778   KeyCode kc, *kp;
779   unsigned lockmask, *mp;
780   XModifierKeymap* mm = XGetModifierMapping(dpy);
781   lockmask = LockMask;
782   if (mm)
783     {
784       kp = mm->modifiermap;
785       for (m = 0; m < 8; m++)
786         {
787           for (i = 0; i < mm->max_keypermod; i++)
788             {
789       	if ((kc = *kp++) &&
790       	    ((ks = XkbKeycodeToKeysym(dpy, kc, 0, 0)) != NoSymbol))
791       	  {
792       	    kn = XKeysymToString(ks);
793       	    knl = strlen(kn);
794       	    if ((knl > 6) && (strcasecmp(kn + knl - 4, "lock") == 0))
795       		lockmask |= (1 << m);
796       	  }
797             }
798         }
799       XFreeModifiermap(mm);
800     }
801   lockmask &= ~(ShiftMask | ControlMask);  /* forget shift & control locks */
802   Scr.nonlock_mods = ((ShiftMask | ControlMask | Mod1Mask | Mod2Mask
803       		 | Mod3Mask| Mod4Mask| Mod5Mask) & ~lockmask);
804 
805   if (Scr.lock_mods == NULL)
806       Scr.lock_mods = (unsigned*) safemalloc(256 * sizeof(unsigned));
807   mp = Scr.lock_mods;
808   for (m = 0, i = 1; i < 256; i++)
809     {
810       if ((i & lockmask) > m)
811           m = *mp++ = (i & lockmask);
812     }
813   *mp = 0;
814 }
815 
816 
817 /***********************************************************************
818  *
819  *  Procedure:
820  *	Reborder - Removes afterstep border windows
821  *
822  ************************************************************************/
823 
Reborder_unmap(ASWindow * t)824 void Reborder_unmap(ASWindow* t)
825 {
826   if (t->next)  Reborder_unmap(t->next);
827   XUnmapWindow(dpy, t->frame);
828   RestoreWithdrawnLocation(t, True);
829   XDestroyWindow(dpy, t->frame);
830 }
831 
Reborder(void)832 void Reborder(void)
833 {
834   ASWindow *tmp;			/* temp afterstep window structure */
835   int i;
836   extern unsigned PopupCount;
837   extern MenuRoot *PopupTable[MAXPOPUPS];
838 
839   /* put a border back around all windows */
840   XGrabServer (dpy);
841 #ifndef NO_PAGER
842   if(Scr.Pager_w != None)
843     XDestroyWindow(dpy,Scr.Pager_w);
844 #endif
845 
846   InstallWindowColormaps (&Scr.ASRoot);	/* force reinstall */
847   Reborder_unmap(Scr.ASRoot.next);
848 
849   for(i=0;i<PopupCount;i++)
850     if(PopupTable[i]->w != None)
851       XDestroyWindow(dpy,PopupTable[i]->w);
852   XUngrabServer (dpy);
853   XSetInputFocus (dpy, PointerRoot, RevertToPointerRoot,CurrentTime);
854   XSync(dpy,0);
855 
856 }
857 
858 /***********************************************************************
859  *
860  *  Procedure: NoisyExit
861  *	Print error messages and die. (segmentation violation)
862  *
863  **********************************************************************/
NoisyExit(int nonsense)864 void NoisyExit(int nonsense)
865 {
866   XErrorEvent event;
867 
868   afterstep_err("Seg Fault",NULL,NULL,NULL);
869   event.error_code = 0;
870   event.request_code = 0;
871   ASErrorHandler(dpy, &event);
872 
873   /* Attempt to do a re-start of afterstep */
874   Done(0,NULL);
875 }
876 
877 
878 
879 
880 
881 /***********************************************************************
882  *
883  *  Procedure:
884  *	Done - cleanup and exit afterstep
885  *
886  ***********************************************************************
887  */
SigDone(int nonsense)888 void SigDone(int nonsense)
889 {
890   Done(0, NULL);
891   SIGNAL_RETURN;
892 }
893 
Done(int restart,char * command)894 void Done(int restart, char *command)
895 {
896 #ifndef NON_VIRTUAL
897   MoveViewport(0,0,False);
898 #endif
899 
900   /* Close all my pipes */
901   ClosePipes();
902 
903   Reborder ();
904 
905 #ifdef M4
906   if (m4_enable)
907     {
908       extern char *afterstep_file;
909 
910       /* With m4 processing, a temporary file was created to hold the
911          processed file.  Delete the file now because we don't need it
912 	 any more.  It will be created again during restart. */
913       unlink(afterstep_file);
914     }
915 #endif
916 
917   if(restart)
918     {
919       SaveDesktopState();		/* I wonder why ... */
920 
921       /* Really make sure that the connection is closed and cleared! */
922       XSelectInput(dpy, Scr.Root, 0 );
923       XSync(dpy, 0);
924       XCloseDisplay(dpy);
925 
926       {
927 	char *my_argv[10];
928 	int i,done,j;
929 
930 	i=0;
931 	j=0;
932 	done = 0;
933 	while((g_argv[j] != NULL)&&(i<8))
934 	  {
935 	    if(strcmp(g_argv[j],"-s")!=0)
936 	      {
937 		my_argv[i] = g_argv[j];
938 		i++;
939 		j++;
940 	      }
941 	    else
942 	      j++;
943 	  }
944 	if(strstr(command,"afterstep")!= NULL)
945 	   my_argv[i++] = "-s";
946 	while(i<10)
947 	  my_argv[i++] = NULL;
948 
949 	/* really need to destroy all windows, explicitly,
950 	 * not sleep, but this is adequate for now */
951 	sleep(1);
952 	ReapChildren();
953 	execvp(command,my_argv);
954       }
955       fprintf(stderr, "AfterStep: Call of '%s' failed!!!!\n",command);
956       execvp(g_argv[0], g_argv);    /* that _should_ work */
957       fprintf(stderr, "AfterStep: Call of '%s' failed!!!!\n", g_argv[0]);
958     }
959   else
960     {
961       XCloseDisplay(dpy);
962       exit(0);
963     }
964 }
965 
966 /***********************************************************************
967  *
968  *  Procedure:
969  *	CatchRedirectError - Figures out if there's another WM running
970  *
971  ************************************************************************/
CatchRedirectError(Display * dpy,XErrorEvent * event)972 XErrorHandler CatchRedirectError(Display *dpy, XErrorEvent *event)
973 {
974   afterstep_err("another WM is running",NULL,NULL,NULL);
975   exit(1);
976 }
977 
978 
979 /***********************************************************************
980  *
981  *  Procedure:
982  *	ASErrorHandler - displays info on internal errors
983  *
984  ************************************************************************/
ASErrorHandler(Display * dpy,XErrorEvent * event)985 XErrorHandler ASErrorHandler(Display *dpy, XErrorEvent *event)
986 {
987   extern int last_event_type;
988 
989   /* some errors are acceptable, mostly they're caused by
990    * trying to update a lost  window */
991   if((event->error_code == BadWindow)||(event->request_code == X_GetGeometry)||
992      (event->error_code==BadDrawable)||(event->request_code==X_SetInputFocus)||
993      (event->request_code==X_GrabButton)||
994      (event->request_code==X_ChangeWindowAttributes)||
995      (event->request_code == X_InstallColormap))
996     return 0 ;
997 
998 
999   afterstep_err("internal error",NULL,NULL,NULL);
1000   fprintf(stderr,"      Request %d, Error %d\n", event->request_code,
1001 	  event->error_code);
1002   fprintf(stderr,"      EventType: %d",last_event_type);
1003   fprintf(stderr,"\n");
1004   return 0;
1005 }
1006 
afterstep_err(char * message,char * arg1,char * arg2,char * arg3)1007 void afterstep_err(char *message, char *arg1, char *arg2, char *arg3)
1008 {
1009   fprintf(stderr,"AfterStep: ");
1010   fprintf(stderr,message,arg1,arg2,arg3);
1011   fprintf(stderr,"\n");
1012 }
1013 
usage(void)1014 void usage(void)
1015 {
1016 #ifdef M4
1017 #define USAGE "AfterStep Ver %s\n\nusage: afterstep [-d dpy] [-debug] [-f config_file] [-s] [-no-m4] [-m4-prefix] [-m4opt option] [-m4-squote squote] [-m4-equote equote] [-m4prog m4prog]\n"
1018 #else
1019 #define USAGE "AfterStep Ver %s\n\nusage: afterstep [-d dpy] [-debug] [-f config_file] [-s]\n"
1020 #endif
1021 
1022   fprintf(stderr,USAGE,VERSION);
1023 
1024 }
1025 
1026 
1027 
1028 
1029 #ifndef NON_VIRTUAL
1030 /* the root window is surrounded by four window slices, which are InputOnly.
1031  * So you can see 'through' them, but they eat the input. An EnterEvent in
1032  * one of these windows causes a Paging. The windows have the according cursor
1033  * pointing in the pan direction or are hidden if there is no more panning
1034  * in that direction. This is mostly intended to get a panning even atop
1035  * of Motif applictions, which does not work yet. It seems Motif windows
1036  * eat all mouse events.
1037  *
1038  * Hermann Dunkel, HEDU, dunkel@cul-ipn.uni-kiel.de 1/94
1039 */
1040 
1041 /***************************************************************************
1042  * checkPanFrames hides PanFrames if they are on the very border of the
1043  * VIRTUELL screen and EdgeWrap for that direction is off.
1044  * (A special cursor for the EdgeWrap border could be nice) HEDU
1045  ****************************************************************************/
checkPanFrames(void)1046 void checkPanFrames(void)
1047 {
1048   extern Bool DoHandlePageing;
1049   int wrapX = (Scr.flags & EdgeWrapX);
1050   int wrapY = (Scr.flags & EdgeWrapY);
1051 
1052   /* Remove Pan frames if paging by edge-scroll is permanently or
1053    * temporarily disabled */
1054   if((Scr.EdgeScrollY == 0)||(!DoHandlePageing))
1055     {
1056       XUnmapWindow(dpy,Scr.PanFrameTop.win);
1057       Scr.PanFrameTop.isMapped=False;
1058       XUnmapWindow (dpy,Scr.PanFrameBottom.win);
1059       Scr.PanFrameBottom.isMapped=False;
1060     }
1061   if((Scr.EdgeScrollX == 0)||(!DoHandlePageing))
1062     {
1063       XUnmapWindow(dpy,Scr.PanFrameLeft.win);
1064       Scr.PanFrameLeft.isMapped=False;
1065       XUnmapWindow (dpy,Scr.PanFrameRight.win);
1066       Scr.PanFrameRight.isMapped=False;
1067     }
1068   if(((Scr.EdgeScrollX == 0)&&(Scr.EdgeScrollY == 0))||(!DoHandlePageing))
1069     return;
1070 
1071   /* LEFT, hide only if EdgeWrap is off */
1072   if (Scr.Vx==0 && Scr.PanFrameLeft.isMapped && (!wrapX))
1073     {
1074       XUnmapWindow(dpy,Scr.PanFrameLeft.win);
1075       Scr.PanFrameLeft.isMapped=False;
1076     }
1077   else if (Scr.Vx > 0 && Scr.PanFrameLeft.isMapped==False)
1078     {
1079       XMapRaised(dpy,Scr.PanFrameLeft.win);
1080       Scr.PanFrameLeft.isMapped=True;
1081     }
1082   /* RIGHT, hide only if EdgeWrap is off */
1083   if (Scr.Vx == Scr.VxMax && Scr.PanFrameRight.isMapped && (!wrapX))
1084     {
1085       XUnmapWindow (dpy,Scr.PanFrameRight.win);
1086       Scr.PanFrameRight.isMapped=False;
1087     }
1088   else if (Scr.Vx < Scr.VxMax && Scr.PanFrameRight.isMapped==False)
1089     {
1090       XMapRaised(dpy,Scr.PanFrameRight.win);
1091       Scr.PanFrameRight.isMapped=True;
1092     }
1093   /* TOP, hide only if EdgeWrap is off */
1094   if (Scr.Vy==0 && Scr.PanFrameTop.isMapped && (!wrapY))
1095     {
1096       XUnmapWindow(dpy,Scr.PanFrameTop.win);
1097       Scr.PanFrameTop.isMapped=False;
1098     }
1099   else if (Scr.Vy > 0 && Scr.PanFrameTop.isMapped==False)
1100     {
1101       XMapRaised(dpy,Scr.PanFrameTop.win);
1102       Scr.PanFrameTop.isMapped=True;
1103     }
1104   /* BOTTOM, hide only if EdgeWrap is off */
1105   if (Scr.Vy == Scr.VyMax && Scr.PanFrameBottom.isMapped && (!wrapY))
1106     {
1107       XUnmapWindow (dpy,Scr.PanFrameBottom.win);
1108       Scr.PanFrameBottom.isMapped=False;
1109     }
1110   else if (Scr.Vy < Scr.VyMax && Scr.PanFrameBottom.isMapped==False)
1111     {
1112       XMapRaised(dpy,Scr.PanFrameBottom.win);
1113       Scr.PanFrameBottom.isMapped=True;
1114     }
1115 }
1116 
1117 /****************************************************************************
1118  *
1119  * Gotta make sure these things are on top of everything else, or they
1120  * don't work!
1121  *
1122  ***************************************************************************/
raisePanFrames(void)1123 void raisePanFrames(void)
1124 {
1125   if (Scr.PanFrameTop.isMapped) XRaiseWindow(dpy,Scr.PanFrameTop.win);
1126   if (Scr.PanFrameLeft.isMapped) XRaiseWindow(dpy,Scr.PanFrameLeft.win);
1127   if (Scr.PanFrameRight.isMapped) XRaiseWindow(dpy,Scr.PanFrameRight.win);
1128   if (Scr.PanFrameBottom.isMapped) XRaiseWindow(dpy,Scr.PanFrameBottom.win);
1129 }
1130 
1131 /****************************************************************************
1132  *
1133  * Creates the windows for edge-scrolling
1134  *
1135  ****************************************************************************/
initPanFrames()1136 void initPanFrames()
1137 {
1138   XSetWindowAttributes attributes;    /* attributes for create */
1139   unsigned long valuemask;
1140 
1141   attributes.event_mask =  (EnterWindowMask | LeaveWindowMask |
1142 			    VisibilityChangeMask);
1143   valuemask=  (CWEventMask | CWCursor );
1144 
1145   attributes.cursor = Scr.ASCursors[TOP];
1146   Scr.PanFrameTop.win =
1147     XCreateWindow (dpy, Scr.Root,
1148 		   0,0,
1149 		   Scr.MyDisplayWidth,PAN_FRAME_THICKNESS,
1150 		   0,	/* no border */
1151 		   CopyFromParent, InputOnly,
1152 		   CopyFromParent,
1153 		   valuemask, &attributes);
1154   attributes.cursor = Scr.ASCursors[LEFT];
1155   Scr.PanFrameLeft.win =
1156     XCreateWindow (dpy, Scr.Root,
1157 		   0,PAN_FRAME_THICKNESS,
1158 		   PAN_FRAME_THICKNESS,
1159 		   Scr.MyDisplayHeight-2*PAN_FRAME_THICKNESS,
1160 		   0,	/* no border */
1161 		   CopyFromParent, InputOnly, CopyFromParent,
1162 		   valuemask, &attributes);
1163   attributes.cursor = Scr.ASCursors[RIGHT];
1164   Scr.PanFrameRight.win =
1165     XCreateWindow (dpy, Scr.Root,
1166 		   Scr.MyDisplayWidth-PAN_FRAME_THICKNESS,PAN_FRAME_THICKNESS,
1167 		   PAN_FRAME_THICKNESS,
1168 		   Scr.MyDisplayHeight-2*PAN_FRAME_THICKNESS,
1169 		   0,	/* no border */
1170 		   CopyFromParent, InputOnly, CopyFromParent,
1171 		   valuemask, &attributes);
1172   attributes.cursor = Scr.ASCursors[BOTTOM];
1173   Scr.PanFrameBottom.win =
1174     XCreateWindow (dpy, Scr.Root,
1175 		   0,Scr.MyDisplayHeight-PAN_FRAME_THICKNESS,
1176 		   Scr.MyDisplayWidth,PAN_FRAME_THICKNESS,
1177 		   0,	/* no border */
1178 		   CopyFromParent, InputOnly, CopyFromParent,
1179 		   valuemask, &attributes);
1180   Scr.PanFrameTop.isMapped=Scr.PanFrameLeft.isMapped=
1181     Scr.PanFrameRight.isMapped= Scr.PanFrameBottom.isMapped=False;
1182 
1183   Scr.usePanFrames=True;
1184 
1185 }
1186 #endif /* NON_VIRTUAL */
1187 
1188 /****************************************************************************
1189  *
1190  * Save Desktop State
1191  *
1192  ****************************************************************************/
SaveDesktopState()1193 void SaveDesktopState()
1194 {
1195   ASWindow *t;
1196   unsigned long data[1];
1197 
1198   for (t = Scr.ASRoot.next; t != NULL; t = t->next)
1199     {
1200       data[0] = (unsigned long) t->Desk;
1201       XChangeProperty (dpy, t->w, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
1202 		       PropModeReplace, (unsigned char *) data, 1);
1203     }
1204 
1205   data[0] = (unsigned long) Scr.CurrentDesk;
1206   XChangeProperty (dpy, Scr.Root, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
1207 		   PropModeReplace, (unsigned char *) data, 1);
1208 
1209   XSync(dpy, 0);
1210 }
1211 
1212 
1213