1 /* -*-c-*- */
2 /* FvwmForm is original work of Thomas Zuwei Feng.
3  *
4  * Copyright Feb 1995, Thomas Zuwei Feng.  No guarantees or warantees are
5  * provided or implied in any way whatsoever.  Use this program at your own
6  * risk.  Permission to use, modify, and redistribute this program is hereby
7  * given, provided that this copyright is kept intact. */
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see: <http://www.gnu.org/licenses/>
21  */
22 
23 #include "config.h"
24 
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <signal.h>
28 
29 #include "libs/ftime.h"
30 #include <fcntl.h>
31 
32 #include <X11/Xlib.h>
33 #include <X11/X.h>
34 #include <X11/Xutil.h>
35 #include <X11/cursorfont.h>
36 #define XK_MISCELLANY
37 #include <X11/keysymdef.h>
38 
39 #include "libs/Module.h"		/* for headersize, etc. */
40 #include "libs/fvwmlib.h"
41 #include "libs/fvwmsignal.h"
42 #include "libs/ColorUtils.h"
43 #include "libs/Cursor.h"
44 #include "libs/envvar.h"
45 #include "libs/Graphics.h"
46 #include "libs/Parse.h"
47 #include "libs/Strings.h"
48 #include "libs/System.h"
49 #include "libs/XError.h"
50 
51 #include "libs/PictureBase.h"		 /* for PictureInitCMap */
52 #include "libs/Colorset.h"
53 #include "libs/FScreen.h"
54 #include "libs/FShape.h"
55 #include "libs/FRenderInit.h"
56 #include "libs/Rectangles.h"
57 
58 #include "FvwmForm.h"			/* common FvwmForm stuff */
59 
60 /* globals that are exported, keep in sync with externs in FvwmForm.h */
61 Form cur_form;			 /* current form */
62 
63 /* Link list roots */
64 Item *root_item_ptr;		 /* pointer to root of item list */
65 Line root_line = {&root_line,	 /* ->next */
66 		  0,		 /* number of items */
67 		  L_CENTER,0,0,	 /* justify, size x/y */
68 		  0,		 /* current array size */
69 		  0};		 /* items ptr */
70 Line *cur_line = &root_line;		/* curr line in parse rtns */
71 char preload_yorn='n';		 /* init to non-preload */
72 Item *item;				/* current during parse */
73 Item *cur_sel, *cur_button;		/* current during parse */
74 Item *timer = NULL;			/* timeout tracking */
75 Display *dpy;
76 Atom wm_del_win;
77 int fd_x;		   /* fd for X connection */
78 Window root, ref;
79 int screen;
80 char *color_names[4];
81 char bg_state = 'd';			/* in default state */
82   /* d = default state */
83   /* s = set by command (must be in "d" state for first "back" cmd to set it) */
84   /* u = used (color allocated, too late to accept "back") */
85 char endDefaultsRead = 'n';
86 char *font_names[4];
87 char *screen_background_color;
88 static ModuleArgs *module;
89 int Channel[2];
90 Bool Swallowed = False;
91 
92 /* Font/color stuff
93    The colors are:
94    . defaults are changed by commands during parse
95    . copied into items during parse
96    . displayed in the customization dialog
97    */
98 int colorset = -1;
99 int itemcolorset = 0;
100 
101 /* prototypes */
102 static void RedrawSeparator(Item *item);
103 static void AssignDrawTable(Item *);
104 static void AddItem(void);
105 static void PutDataInForm(char *);
106 static void ReadFormData(void);
107 static void FormVarsCheck(char **);
108 static RETSIGTYPE TimerHandler(int);
109 static int ErrorHandler(Display *dpy, XErrorEvent *error_event);
SetupTimer(void)110 static void SetupTimer(void)
111 {
112 #ifdef HAVE_SIGACTION
113   {
114     struct sigaction  sigact;
115 
116 #ifdef SA_INTERRUPT
117     sigact.sa_flags = SA_INTERRUPT;
118 #else
119     sigact.sa_flags = 0;
120 #endif
121     sigemptyset(&sigact.sa_mask);
122     sigaddset(&sigact.sa_mask, SIGALRM);
123     sigact.sa_handler = TimerHandler;
124 
125     sigaction(SIGALRM, &sigact, NULL);
126   }
127 #else
128 #ifdef USE_BSD_SIGNALS
129   fvwmSetSignalMask( sigmask(SIGALRM) );
130 #endif
131   signal(SIGALRM, TimerHandler);  /* Dead pipe == Fvwm died */
132 #ifdef HAVE_SIGINTERRUPT
133   siginterrupt(SIGALRM, 1);
134 #endif
135 #endif
136 
137   alarm(1);
138 }
139 
140 /* copy a string until '"', or '\n', or '\0' */
CopyQuotedString(char * cp)141 static char *CopyQuotedString (char *cp)
142 {
143   char *dp, *bp, c;
144   bp = dp = (char *)safemalloc(strlen(cp) + 1);
145   while (1) {
146     switch (c = *(cp++)) {
147     case '\\':
148       *(dp++) = *(cp++);
149       break;
150     case '\"':
151     case '\n':
152     case '\0':
153       *dp = '\0';
154       return bp;
155       break;
156     default:
157       *(dp++) = c;
158       break;
159     }
160   }
161 }
162 
163 /* copy a string until the first space */
CopySolidString(char * cp)164 static char *CopySolidString (char *cp)
165 {
166   char *dp, *bp, c;
167   bp = dp = (char *)safemalloc(strlen(cp) + 1);
168   while (1) {
169     c = *(cp++);
170     if (c == '\\') {
171       *(dp++) = '\\';
172       *(dp++) = *(cp++);
173     } else if (isspace((unsigned char)c) || c == '\0') {
174       *dp = '\0';
175       return bp;
176     } else
177       *(dp++) = c;
178   }
179 }
180 
181 /* Command parsing section */
182 
183 /* FvwmAnimate++ type command table */
184 typedef struct CommandTable
185 {
186   char *name;
187   void (*function)(char *);
188 } ct;
189 static void ct_ActivateOnPress(char *);
190 static void ct_Back(char *);
191 static void ct_Button(char *);
192 static void ct_ButtonFont(char *);
193 static void ct_ButtonInPointer(char *);
194 static void ct_ButtonInPointerBack(char *);
195 static void ct_ButtonInPointerFore(char *);
196 static void ct_ButtonPointer(char *);
197 static void ct_ButtonPointerBack(char *);
198 static void ct_ButtonPointerFore(char *);
199 static void ct_Choice(char *);
200 static void ct_Command(char *);
201 static void ct_Font(char *);
202 static void ct_Fore(char *);
203 static void ct_GrabServer(char *);
204 static void ct_Input(char *);
205 static void ct_InputFont(char *);
206 static void ct_ItemBack(char *);
207 static void ct_ItemFore(char *);
208 static void ct_Line(char *);
209 static void ct_Message(char *);
210 static void ct_Geometry(char *);
211 static void ct_Position(char *);
212 static void ct_Read(char *);
213 static void ct_Selection(char *);
214 static void ct_Separator(char *);
215 static void ct_Text(char *);
216 static void ct_InputPointer(char *);
217 static void ct_InputPointerBack(char *);
218 static void ct_InputPointerFore(char *);
219 static void ct_Timeout(char *);
220 static void ct_TimeoutFont(char *);
221 static void ct_Title(char *);
222 static void ct_UseData(char *);
223 static void ct_padVText(char *);
224 static void ct_WarpPointer(char *);
225 static void ct_Colorset(char *);
226 static void ct_ItemColorset(char *);
227 
228 /* Must be in Alphabetic order (caseless) */
229 static struct CommandTable ct_table[] =
230 {
231   {"ActivateOnPress",ct_ActivateOnPress},
232   {"Back",ct_Back},
233   {"Button",ct_Button},
234   {"ButtonFont",ct_ButtonFont},
235   {"ButtonInPointer",ct_ButtonInPointer},
236   {"ButtonInPointerBack",ct_ButtonInPointerBack},
237   {"ButtonInPointerFore",ct_ButtonInPointerFore},
238   {"ButtonPointer",ct_ButtonPointer},
239   {"ButtonPointerBack",ct_ButtonPointerBack},
240   {"ButtonPointerFore",ct_ButtonPointerFore},
241   {"Choice",ct_Choice},
242   {"Colorset",ct_Colorset},
243   {"Command",ct_Command},
244   {"Font",ct_Font},
245   {"Fore",ct_Fore},
246   {"Geometry",ct_Geometry},
247   {"GrabServer",ct_GrabServer},
248   {"Input",ct_Input},
249   {"InputFont",ct_InputFont},
250   {"InputPointer",ct_InputPointer},
251   {"InputPointerBack",ct_InputPointerBack},
252   {"InputPointerFore",ct_InputPointerFore},
253   {"ItemBack",ct_ItemBack},
254   {"ItemColorset",ct_ItemColorset},
255   {"ItemFore",ct_ItemFore},
256   {"Line",ct_Line},
257   {"Message",ct_Message},
258   {"PadVText",ct_padVText},
259   {"Position",ct_Position},
260   {"Selection",ct_Selection},
261   {"Separator",ct_Separator},
262   {"Text",ct_Text},
263   {"Timeout",ct_Timeout},
264   {"TimeoutFont",ct_TimeoutFont},
265   {"Title",ct_Title},
266   {"UseData",ct_UseData},
267   {"WarpPointer",ct_WarpPointer}
268 };
269 /* These commands are the default setting commands,
270    read before the other form defining commands. */
271 static struct CommandTable def_table[] =
272 {
273   {"ActivateOnPress",ct_ActivateOnPress},
274   {"Back",ct_Back},
275   {"ButtonFont",ct_ButtonFont},
276   {"ButtonInPointer",ct_ButtonInPointer},
277   {"ButtonInPointerBack",ct_ButtonInPointerBack},
278   {"ButtonInPointerFore",ct_ButtonInPointerFore},
279   {"ButtonPointer",ct_ButtonPointer},
280   {"ButtonPointerBack",ct_ButtonPointerBack},
281   {"ButtonPointerFore",ct_ButtonPointerFore},
282   {"Colorset",ct_Colorset},
283   {"Font",ct_Font},
284   {"Fore",ct_Fore},
285   {"InputFont",ct_InputFont},
286   {"InputPointer",ct_InputPointer},
287   {"InputPointerBack",ct_InputPointerBack},
288   {"InputPointerFore",ct_InputPointerFore},
289   {"ItemBack",ct_ItemBack},
290   {"ItemColorset",ct_ItemColorset},
291   {"ItemFore",ct_ItemFore},
292   {"Read",ct_Read},
293   {"TimeoutFont",ct_TimeoutFont}
294 };
295 
296 /* If there were vars on the command line, do env var sustitution on
297    all input. */
FormVarsCheck(char ** p)298 static void FormVarsCheck(char **p)
299 {
300   if (CF.have_env_var) {		/* if cmd line vars */
301     if (strlen(*p) + 200 > CF.expand_buffer_size) { /* fast and loose */
302       CF.expand_buffer_size = strlen(*p) + 2000; /* new size */
303       if (CF.expand_buffer) {		/* already have one */
304 	CF.expand_buffer = saferealloc(CF.expand_buffer, CF.expand_buffer_size);
305       } else {				/* first time */
306 	CF.expand_buffer = safemalloc(CF.expand_buffer_size);
307       }
308     }
309     strcpy(CF.expand_buffer,*p);
310     *p = CF.expand_buffer;
311     envExpand(*p,CF.expand_buffer_size); /* expand the command in place */
312   }
313 }
314 
ParseDefaults(char * buf)315 static void ParseDefaults(char *buf)
316 {
317   char *p;
318   struct CommandTable *e;
319   if (buf[strlen(buf)-1] == '\n') {	/* if line ends with newline */
320     buf[strlen(buf)-1] = '\0';	/* strip off \n */
321   }
322   /* Accept commands beginning with "*FvwmFormDefault".
323      This is to make sure defaults are read first.
324      Note the hack w. bg_state. */
325   if (strncasecmp(buf, "*FvwmFormDefault", 16) == 0) {
326     p=buf+16;
327     FormVarsCheck(&p);			 /* do var substitution if called for */
328     e = FindToken(p,def_table,struct CommandTable);/* find cmd in table */
329     if (e != 0) {			/* if its valid */
330       p=p+strlen(e->name);		/* skip over name */
331       while (isspace((unsigned char)*p)) p++;	       /* skip whitespace */
332       e->function(p);			/* call cmd processor */
333       bg_state = 'd';			/* stay in default state */
334     }
335   }
336 } /* end function */
337 
338 
ParseConfigLine(char * buf)339 static void ParseConfigLine(char *buf)
340 {
341   char *p;
342   struct CommandTable *e;
343   if (buf[strlen(buf)-1] == '\n') {	/* if line ends with newline */
344     buf[strlen(buf)-1] = '\0';	/* strip off \n */
345   }
346 
347   if (strncasecmp(buf, XINERAMA_CONFIG_STRING,
348 		  sizeof(XINERAMA_CONFIG_STRING)-1) == 0)
349   {
350     FScreenConfigureModule(buf + sizeof(XINERAMA_CONFIG_STRING)-1);
351     return;
352   }
353   if (strncasecmp(buf, "Colorset", 8) == 0) {
354     LoadColorset(&buf[8]);
355     return;
356   }
357   if (strncasecmp(buf, CatString3("*",module->name,0), module->namelen+1) != 0) {/* If its not for me */
358     return;
359   } /* Now I know its for me. */
360   p = buf+module->namelen+1;		      /* jump to end of my name */
361   /* at this point we have recognized "*FvwmForm" */
362   FormVarsCheck(&p);
363   e = FindToken(p,ct_table,struct CommandTable);/* find cmd in table */
364   if (e == 0) {			      /* if no match */
365     fprintf(stderr,"%s: unknown command: %s\n",module->name,buf);
366     return;
367   }
368 
369   p=p+strlen(e->name);			/* skip over name */
370   while (isspace((unsigned char)*p)) p++;	       /* skip whitespace */
371 
372   FormVarsCheck(&p);			 /* do var substitution if called for */
373 
374   e->function(p);			/* call cmd processor */
375   return;
376 } /* end function */
377 
378 /* Expands item array */
ExpandArray(Line * this_line)379 static void ExpandArray(Line *this_line)
380 {
381   if (this_line->n + 1 >= this_line->item_array_size) { /* no empty space */
382     this_line->item_array_size += ITEMS_PER_EXPANSION; /* get bigger */
383     this_line->items =
384       (Item **)saferealloc((void *)this_line->items,
385 			  (sizeof(Item *) * this_line->item_array_size));
386   } /* end array full */
387 }
388 
389 /* Function to add an item to the current line */
AddToLine(Item * newItem)390 static void AddToLine(Item *newItem)
391 {
392   ExpandArray(cur_line);		/* expand item array if needed */
393   cur_line->items[cur_line->n++] = newItem; /* add to lines item array */
394   cur_line->size_x += newItem->header.size_x; /* incr lines width */
395   if (cur_line->size_y < newItem->header.size_y) { /* new item bigger */
396     cur_line->size_y = newItem->header.size_y; /* then line is bigger */
397   }
398 }
399 
400 
401 /* All the functions starting with "ct_" (command table) are called thru
402    their function pointers. Arg 1 is always the rest of the command. */
ct_ActivateOnPress(char * cp)403 static void ct_ActivateOnPress(char *cp)
404 {
405   /* arg can be "on", "off", "0", 1", "true", "false", "" */
406   char option[6];
407   int i,j;
408   if (strlen(cp) > 5) {
409     fprintf(stderr,"%s: arg for ActivateOnPress (%s) too long\n",
410 	    module->name,cp);
411     return;
412   }
413   for (i=0,j=0;i<strlen(cp);i++) {
414     if (cp[i] != ' ') {			  /* remove any spaces */
415       option[j++]=tolower(cp[i]);
416     }
417   }
418   option[j]=0;
419   if (strcmp(option,"on") == 0
420       || strcmp(option,"true") == 0
421       || strcmp(option,"1") == 0
422       || (cp == 0 || strcmp(option,"")) == 0) {
423     CF.activate_on_press = 1;
424   } else if (strcmp(option,"off") == 0
425 	     || strcmp(option,"false") == 0
426 	     || strcmp(option,"0") == 0) {
427     CF.activate_on_press = 0;
428   } else {
429     fprintf(stderr,"%s: arg for ActivateOnPress (%s/%s) invalid\n",
430 	    module->name,option,cp);
431   }
432 }
ct_GrabServer(char * cp)433 static void ct_GrabServer(char *cp)
434 {
435   CF.grab_server = 1;
436 }
ct_WarpPointer(char * cp)437 static void ct_WarpPointer(char *cp)
438 {
439   CF.warp_pointer = 1;
440 }
ct_Position(char * cp)441 static void ct_Position(char *cp)
442 {
443   CF.have_geom = 1;
444   CF.gx = atoi(cp);
445   CF.xneg = 0;
446   if (CF.gx < 0)
447   {
448     CF.gx = -1 - CF.gx;
449     CF.xneg = 1;
450   }
451   while (!isspace((unsigned char)*cp)) cp++;
452   while (isspace((unsigned char)*cp)) cp++;
453   CF.gy = atoi(cp);
454   CF.yneg = 0;
455   if (CF.gy < 0)
456   {
457     CF.gy = -1 - CF.gy;
458     CF.yneg = 1;
459   }
460   myfprintf((stderr, "Position @ (%c%d%c%d)\n",
461 	     (CF.xneg)?'-':'+',CF.gx, (CF.yneg)?'-':'+',CF.gy));
462 }
ct_Geometry(char * cp)463 static void ct_Geometry(char *cp)
464 {
465   int x = 0;
466   int y = 0;
467   int flags;
468   unsigned int dummy;
469 
470   CF.gx = 0;
471   CF.gy = 0;
472   CF.xneg = 0;
473   CF.yneg = 0;
474   while (isspace(*cp))
475     cp++;
476   flags = FScreenParseGeometry(cp, &x, &y, &dummy, &dummy);
477   if (flags&XValue)
478   {
479     CF.have_geom = 1;
480     CF.gx = x;
481     CF.xneg = (flags&XNegative);
482   }
483   if (flags&YValue)
484   {
485     CF.have_geom = 1;
486     CF.gy = y;
487     CF.yneg = (flags&YNegative);
488   }
489   myfprintf((stderr, "Geometry @ (%c%d%c%d)\n",
490 	     (CF.xneg)?'-':'+',CF.gx, (CF.yneg)?'-':'+',CF.gy));
491 }
ct_Fore(char * cp)492 static void ct_Fore(char *cp)
493 {
494   if (color_names[c_fg])
495     free(color_names[c_fg]);
496   color_names[c_fg] = safestrdup(cp);
497   colorset = -1;
498   myfprintf((stderr, "ColorFore: %s\n", color_names[c_fg]));
499 }
ct_Back(char * cp)500 static void ct_Back(char *cp)
501 {
502   if (color_names[c_bg])
503     free(color_names[c_bg]);
504   color_names[c_bg] = safestrdup(cp);
505   if (bg_state == 'd')
506   {
507     if (screen_background_color)
508       free(screen_background_color);
509     screen_background_color = safestrdup(color_names[c_bg]);
510     bg_state = 's';			/* indicate set by command */
511   }
512   colorset = -1;
513   myfprintf((stderr, "ColorBack: %s, screen background %s, bg_state %c\n",
514 	     color_names[c_bg],screen_background_color,(int)bg_state));
515 }
ct_Colorset(char * cp)516 static void ct_Colorset(char *cp)
517 {
518   sscanf(cp, "%d", &colorset);
519   AllocColorset(colorset);
520 }
ct_ItemFore(char * cp)521 static void ct_ItemFore(char *cp)
522 {
523   if (color_names[c_item_fg])
524     free(color_names[c_item_fg]);
525   color_names[c_item_fg] = safestrdup(cp);
526   itemcolorset = -1;
527   myfprintf((stderr, "ColorItemFore: %s\n", color_names[c_item_fg]));
528 }
ct_ItemBack(char * cp)529 static void ct_ItemBack(char *cp)
530 {
531   if (color_names[c_item_bg])
532     free(color_names[c_item_bg]);
533   color_names[c_item_bg] = safestrdup(cp);
534   itemcolorset = -1;
535   myfprintf((stderr, "ColorItemBack: %s\n", color_names[c_item_bg]));
536 }
ct_ItemColorset(char * cp)537 static void ct_ItemColorset(char *cp)
538 {
539   sscanf(cp, "%d", &itemcolorset);
540   AllocColorset(itemcolorset);
541 }
ct_Font(char * cp)542 static void ct_Font(char *cp)
543 {
544   if (font_names[f_text])
545     free(font_names[f_text]);
546   CopyStringWithQuotes(&font_names[f_text], cp);
547   myfprintf((stderr, "Font: %s\n", font_names[f_text]));
548 }
ct_TimeoutFont(char * cp)549 static void ct_TimeoutFont(char *cp)
550 {
551   if (font_names[f_timeout])
552     free(font_names[f_timeout]);
553   CopyStringWithQuotes(&font_names[f_timeout], cp);
554   myfprintf((stderr, "TimeoutFont: %s\n", font_names[f_timeout]));
555 }
ct_ButtonFont(char * cp)556 static void ct_ButtonFont(char *cp)
557 {
558   if (font_names[f_button])
559     free(font_names[f_button]);
560   CopyStringWithQuotes(&font_names[f_button], cp);
561   myfprintf((stderr, "ButtonFont: %s\n", font_names[f_button]));
562 }
ct_ButtonInPointer(char * cp)563 static void ct_ButtonInPointer(char *cp)
564 {
565   int cursor;
566   cursor=fvwmCursorNameToIndex(cp);
567   if (cursor == -1) {
568     fprintf(stderr,"ButtonInPointer: invalid cursor name %s\n",cp);
569   } else {
570     CF.pointer[button_in_pointer] = XCreateFontCursor(dpy,cursor);
571   }
572 }
ct_ButtonPointer(char * cp)573 static void ct_ButtonPointer(char *cp)
574 {
575   int cursor;
576   cursor=fvwmCursorNameToIndex(cp);
577   if (cursor == -1) {
578     fprintf(stderr,"ButtonPointer: invalid cursor name %s\n",cp);
579   } else {
580     CF.pointer[button_pointer] = XCreateFontCursor(dpy,cursor);
581   }
582 }
ct_InputFont(char * cp)583 static void ct_InputFont(char *cp)
584 {
585   if (font_names[f_input])
586     free(font_names[f_input]);
587   CopyStringWithQuotes(&font_names[f_input], cp);
588   myfprintf((stderr, "InputFont: %s\n", font_names[f_input]));
589 }
ct_InputPointer(char * cp)590 static void ct_InputPointer(char *cp)
591 {
592   int cursor;
593   cursor=fvwmCursorNameToIndex(cp);
594   if (cursor == -1) {
595     fprintf(stderr,"InputPointer: invalid cursor name %s\n",cp);
596   } else {
597     CF.pointer[input_pointer] = XCreateFontCursor(dpy,cursor);
598   }
599 }
600 /* process buttons using an index to the ___ array */
601 static void color_arg(Ptr_color *color_cell, char *color_name);
color_arg(Ptr_color * color_cell,char * color_name)602 static void color_arg(Ptr_color *color_cell, char *color_name) {
603   if (color_name && *color_name) {
604     color_cell->pointer_color.pixel = GetColor(color_name);
605     color_cell->used=1;
606   }
607 }
608 
609 /* arg is a color name, alloc the color */
ct_ButtonInPointerBack(char * cp)610 static void ct_ButtonInPointerBack(char *cp) {
611   color_arg(&CF.p_c[button_in_back],cp);
612 }
ct_ButtonInPointerFore(char * cp)613 static void ct_ButtonInPointerFore(char *cp) {
614   color_arg(&CF.p_c[button_in_fore],cp);
615 }
ct_ButtonPointerBack(char * cp)616 static void ct_ButtonPointerBack(char *cp) {
617   color_arg(&CF.p_c[button_back],cp);
618 }
ct_ButtonPointerFore(char * cp)619 static void ct_ButtonPointerFore(char *cp) {
620   color_arg(&CF.p_c[button_fore],cp);
621 }
ct_InputPointerBack(char * cp)622 static void ct_InputPointerBack(char *cp) {
623   color_arg(&CF.p_c[input_back],cp);
624 }
ct_InputPointerFore(char * cp)625 static void ct_InputPointerFore(char *cp) {
626   color_arg(&CF.p_c[input_fore],cp);
627 }
628 
ct_Line(char * cp)629 static void ct_Line(char *cp)
630 {
631   /* malloc new line */
632   cur_line->next = (struct _line *)safemalloc(sizeof(struct _line));
633   memset(cur_line->next, 0, sizeof(struct _line));
634   cur_line = cur_line->next;		/* new current line */
635   cur_line->next = &root_line;		/* new next ptr, (actually root) */
636 
637   if (strncasecmp(cp, "left", 4) == 0)
638     cur_line->justify = L_LEFT;
639   else if (strncasecmp(cp, "right", 5) == 0)
640     cur_line->justify = L_RIGHT;
641   else if (strncasecmp(cp, "center", 6) == 0)
642     cur_line->justify = L_CENTER;
643   else
644     cur_line->justify = L_LEFTRIGHT;
645 }
646 /* Define an area on the form that contains the current
647    fvwm error message.
648    syntax: *FFMessage Length nn, Font fn, Fore color, Back color
649    len default is 70
650    font,fg.bg default to text.
651   */
652 
ct_Message(char * cp)653 static void ct_Message(char *cp)
654 {
655   AddItem();
656   bg_state = 'u';			/* indicate b/g color now used. */
657   item->type = I_TEXT;
658   /* Item now added to list of items, now it needs a pointer
659      to the correct DrawTable. */
660   AssignDrawTable(item);
661   item->header.name = "FvwmMessage";	/* No purpose to this? dje */
662   item->text.value = safemalloc(80);	/* point at last error recvd */
663   item->text.n = 80;
664   strcpy(item->text.value,"A mix of chars. MM20"); /* 20 mixed width chars */
665   item->header.size_x = (FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
666 					  item->text.value,
667 					  item->text.n/4) * 4) + 2 * TEXT_SPC;
668   item->header.size_y = item->header.dt_ptr->dt_Ffont->height
669     + CF.padVText;
670   memset(item->text.value,' ',80);	/* Clear it out */
671   myfprintf((stderr, "Message area [%d, %d]\n",
672 	     item->header.size_x, item->header.size_y));
673   AddToLine(item);
674   CF.last_error = item;			/* save location of message item */
675 }
676 
677 /* allocate colors and fonts needed */
CheckAlloc(Item * this_item,DrawTable * dt)678 static void CheckAlloc(Item *this_item,DrawTable *dt)
679 {
680   XGCValues xgcv;
681   int xgcv_mask = GCForeground;
682 
683   if (dt->dt_used == 2) {		/* fonts colors shadows */
684     return;
685   }
686   if (dt->dt_used == 0) {		/* if nothing allocated */
687     dt->dt_colors[c_fg] = (colorset < 0)
688       ? GetColor(dt->dt_color_names[c_fg])
689       : Colorset[colorset].fg;
690     dt->dt_colors[c_bg] = (colorset < 0)
691       ? GetColor(dt->dt_color_names[c_bg])
692       : Colorset[colorset].bg;
693     xgcv.foreground = dt->dt_colors[c_fg];
694     xgcv.background = dt->dt_colors[c_bg];
695     if (dt->dt_Ffont->font != NULL)
696     {
697       xgcv_mask |= GCFont;
698       xgcv.font = dt->dt_Ffont->font->fid;
699     }
700     xgcv_mask |= GCBackground;
701     dt->dt_GC = fvwmlib_XCreateGC(dpy, CF.frame, xgcv_mask, &xgcv);
702 
703     dt->dt_used = 1;			/* fore/back font allocated */
704   }
705   if (this_item->type == I_TEXT
706       || this_item->type == I_TIMEOUT) {      /* If no shadows needed */
707     return;
708   }
709   dt->dt_colors[c_item_fg] = (itemcolorset < 0)
710     ? GetColor(dt->dt_color_names[c_item_fg])
711     : Colorset[itemcolorset].fg;
712   dt->dt_colors[c_item_bg] = (itemcolorset < 0)
713     ? GetColor(dt->dt_color_names[c_item_bg])
714     : Colorset[itemcolorset].bg;
715   xgcv.foreground = dt->dt_colors[c_item_fg];
716   xgcv.background = dt->dt_colors[c_item_bg];
717   xgcv_mask = GCForeground;
718   if (dt->dt_Ffont->font != NULL)
719   {
720     xgcv_mask |= GCFont;
721     xgcv.font = dt->dt_Ffont->font->fid;
722   }
723   dt->dt_item_GC = fvwmlib_XCreateGC(dpy, CF.frame, xgcv_mask, &xgcv);
724   if (Pdepth < 2) {
725     dt->dt_colors[c_itemlo] = PictureBlackPixel();
726     dt->dt_colors[c_itemhi] = PictureWhitePixel();
727   } else {
728     dt->dt_colors[c_itemlo] = (itemcolorset < 0)
729       ? GetShadow(dt->dt_colors[c_item_bg])
730       : Colorset[itemcolorset].shadow;
731     dt->dt_colors[c_itemhi] = (itemcolorset < 0)
732       ? GetHilite(dt->dt_colors[c_item_bg])
733       : Colorset[itemcolorset].hilite;
734   }
735   dt->dt_used = 2;		       /* fully allocated */
736 }
737 
738 
739 /* Input is the current item.  Assign a drawTable entry to it. */
AssignDrawTable(Item * adt_item)740 static void AssignDrawTable(Item *adt_item)
741 {
742   DrawTable *find_dt, *last_dt;
743   char *match_text_fore;
744   char *match_text_back;
745   char *match_item_fore;
746   char *match_item_back;
747   char *match_font;
748   DrawTable *new_dt;			/* pointer to a new one */
749 
750   match_text_fore = color_names[c_fg];
751   match_text_back = color_names[c_bg];
752   match_item_fore = color_names[c_item_fg];
753   match_item_back = color_names[c_item_bg];
754 
755   switch(adt_item->type) {
756   case I_TEXT:
757   case I_SEPARATOR:			/* hack */
758     match_font = font_names[f_text];
759     break;
760   case	I_TIMEOUT:
761     match_font = font_names[f_timeout];
762     break;
763   case I_INPUT:
764     match_font = font_names[f_input];
765     break;
766   default:
767     match_font = font_names[f_button]; /* choices same as buttons */
768     break;
769   }
770   last_dt = 0;
771   for (find_dt = CF.roots_dt;		   /* start at front */
772        find_dt != 0;			/* until end */
773        find_dt = find_dt->dt_next) {	/* follow next pointer */
774     last_dt = find_dt;
775     if ((strcasecmp(match_text_fore,find_dt->dt_color_names[c_fg]) == 0) &&
776 	(strcasecmp(match_text_back,find_dt->dt_color_names[c_bg]) == 0) &&
777 	(strcasecmp(match_item_fore,find_dt->dt_color_names[c_item_fg]) == 0) &&
778 	(strcasecmp(match_item_back,find_dt->dt_color_names[c_item_bg]) == 0) &&
779 	(strcasecmp(match_font,find_dt->dt_font_name) == 0)) { /* Match */
780       adt_item->header.dt_ptr = find_dt;       /* point item to drawtable */
781       return;				/* done */
782     } /* end match */
783   } /* end all drawtables checked, no match */
784 
785   /* Time to add a DrawTable */
786   /* get one */
787   new_dt = (struct _drawtable *)safemalloc(sizeof(struct _drawtable));
788   memset(new_dt, 0, sizeof(struct _drawtable));
789   new_dt->dt_next = 0;			/* new end of list */
790   if (CF.roots_dt == 0) {		   /* If first entry in list */
791     CF.roots_dt = new_dt;		   /* set root pointer */
792   } else {				/* not first entry */
793     last_dt->dt_next = new_dt;		/* link old to new */
794   }
795 
796   new_dt->dt_font_name = safestrdup(match_font);
797   new_dt->dt_color_names[c_fg]	    = safestrdup(match_text_fore);
798   new_dt->dt_color_names[c_bg]	    = safestrdup(match_text_back);
799   new_dt->dt_color_names[c_item_fg] = safestrdup(match_item_fore);
800   new_dt->dt_color_names[c_item_bg] = safestrdup(match_item_back);
801   new_dt->dt_used = 0;			/* show nothing allocated */
802   new_dt->dt_Ffont = FlocaleLoadFont(dpy, new_dt->dt_font_name, module->name);
803   FlocaleAllocateWinString(&new_dt->dt_Fstr);
804 
805   myfprintf((stderr,"Created drawtable with %s %s %s %s %s\n",
806 	     new_dt->dt_color_names[c_fg], new_dt->dt_color_names[c_bg],
807 	     new_dt->dt_color_names[c_item_fg],
808 	     new_dt->dt_color_names[c_item_bg],
809 	     new_dt->dt_font_name));
810   adt_item->header.dt_ptr = new_dt;	       /* point item to new drawtable */
811 }
812 
813 /* input/output is global "item" - currently allocated last item */
AddItem(void)814 static void AddItem(void)
815 {
816   Item *save_item;
817   save_item = (Item *)item;		/* save current item */
818   item = (Item *)safecalloc(sizeof(Item),1); /* get a new item */
819   if (save_item == 0) {			/* if first item */
820     root_item_ptr = item;		/* save root item */
821   } else {				/* else not first item */
822     save_item->header.next = item;	/* link prior to new */
823   }
824 }
825 
ct_Separator(char * cp)826 static void ct_Separator(char *cp)
827 {
828   AddItem();
829   item->header.name = "";		/* null = create no variables in form */
830   item->type = I_SEPARATOR;
831   item->header.size_y = 2;		/* 2 pixels high */
832   AssignDrawTable(item);
833   AddToLine(item);
834   myfprintf((stderr, "ct_separator command set max width %d\n",(int)item->header.size_x));
835 }
836 
ct_Text(char * cp)837 static void ct_Text(char *cp)
838 {
839   /* syntax: *FFText "<text>" */
840   AddItem();
841   bg_state = 'u';			/* indicate b/g color now used. */
842   item->type = I_TEXT;
843   /* Item now added to list of items, now it needs a pointer
844      to the correct DrawTable. */
845   AssignDrawTable(item);
846   item->header.name = "";
847   if (*cp == '\"')
848     item->text.value = CopyQuotedString(++cp);
849   else
850     item->text.value = "";
851   item->text.n = strlen(item->text.value);
852 
853   item->header.size_x = FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
854 				   item->text.value,
855 				   item->text.n) + 2 * TEXT_SPC;
856   item->header.size_y = item->header.dt_ptr->dt_Ffont->height
857      + CF.padVText;
858   myfprintf((stderr, "Text \"%s\" [%d, %d]\n", item->text.value,
859 	     item->header.size_x, item->header.size_y));
860   AddToLine(item);
861 }
862 /* Set the form's title.
863    The default is the aliasname.
864    If there is no quoted string, create a blank title. */
ct_Title(char * cp)865 static void ct_Title(char *cp)
866 {
867   /* syntax: *FFTitle "<text>" */
868   if (*cp == '\"')
869     CF.title = CopyQuotedString(++cp);
870   else
871     CF.title = "";
872   myfprintf((stderr, "Title \"%s\"\n", CF.title));
873 }
ct_Timeout(char * cp)874 static void ct_Timeout(char *cp)
875 {
876   /* syntax: *FFTimeout seconds <Command> "Text" */
877   char *tmpcp, *tmpbuf;
878 
879   if (timer != NULL) {
880     fprintf(stderr,"Only one timeout per form allowed, skipped %s.\n",cp);
881     return;
882   }
883   AddItem();
884   bg_state = 'u';			/* indicate b/g color now used. */
885   item->type = I_TIMEOUT;
886   /* Item now added to list of items, now it needs a pointer
887      to the correct DrawTable. */
888   AssignDrawTable(item);
889   item->header.name = "";
890 
891   item->timeout.timeleft = atoi(cp);
892   if (item->timeout.timeleft < 0)
893   {
894     item->timeout.timeleft = 0;
895   }
896   else if (item->timeout.timeleft > 99999)
897   {
898     item->timeout.timeleft = 99999;
899   }
900   timer = item;
901 
902   while (*cp && *cp != '\n' && !isspace((unsigned char)*cp)) cp++;
903 							/* skip timeout */
904   while (*cp && *cp != '\n' && isspace((unsigned char)*cp)) cp++;
905 							/* move up to command */
906 
907   if (!*cp || *cp == '\n') {
908     fprintf(stderr,"Improper arguments specified for FvwmForm Timeout.\n");
909     return;
910   }
911 
912   if (*cp == '\"') {
913     item->timeout.command = CopyQuotedString(++cp);
914     /* skip over the whole quoted string to continue parsing */
915     cp += strlen(item->timeout.command) + 1;
916   }
917   else {
918     tmpbuf = safestrdup(cp);
919     tmpcp = tmpbuf;
920     while (!isspace((unsigned char)*tmpcp)) tmpcp++;
921     *tmpcp = '\0';			  /* cutoff command at first word */
922     item->timeout.command = safestrdup(tmpbuf);
923     free(tmpbuf);
924     while (!isspace((unsigned char)*cp)) cp++; /* move past command again */
925   }
926 
927   while (*cp && *cp != '\n' && isspace((unsigned char)*cp)) cp++;
928 						/* move up to next arg */
929 
930   if (!*cp || *cp == '\n') {
931     fprintf(stderr,"Improper arguments specified for FvwmForm Timeout.\n");
932     return;
933   }
934 
935   if (*cp == '\"') {
936     item->timeout.text = CopyQuotedString(++cp);
937   } else
938     item->timeout.text = "";
939   item->timeout.len = strlen(item->timeout.text);
940 
941   item->header.size_x = FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
942 				   item->timeout.text,
943 				   item->timeout.len) + 2 * TEXT_SPC;
944   item->header.size_y = item->header.dt_ptr->dt_Ffont->height
945      + CF.padVText;
946   myfprintf((stderr, "Timeout %d \"%s\" [%d, %d]\n", item->timeout.timeleft,
947 		item->timeout.text, item->header.size_x, item->header.size_y));
948   AddToLine(item);
949 }
ct_padVText(char * cp)950 static void ct_padVText(char *cp)
951 {
952   /* syntax: *FFText "<padVText pixels>" */
953   CF.padVText = atoi(cp);
954   myfprintf((stderr, "Text Vertical Padding %d\n", CF.padVText));
955 }
ct_Input(char * cp)956 static void ct_Input(char *cp)
957 {
958   int j;
959   /* syntax: *FFInput <name> <size> "<init_value>" */
960   AddItem();
961   item->type = I_INPUT;
962   AssignDrawTable(item);
963   item->header.name = CopySolidString(cp);
964   cp += strlen(item->header.name);
965   while (isspace((unsigned char)*cp)) cp++;
966   item->input.size = atoi(cp);
967   while (!isspace((unsigned char)*cp)) cp++;
968   while (isspace((unsigned char)*cp)) cp++;
969   item->input.init_value = safestrdup("");	    /* init */
970   if (*cp == '\"') {
971     free(item->input.init_value);
972     item->input.init_value = CopyQuotedString(++cp);
973   }
974   item->input.blanks = (char *)safemalloc(item->input.size);
975   for (j = 0; j < item->input.size; j++)
976     item->input.blanks[j] = ' ';
977   item->input.buf = strlen(item->input.init_value) + 1;
978   item->input.value = (char *)safemalloc(item->input.buf);
979   item->input.value[0] = 0;		/* avoid reading unitialized data */
980 
981   item->header.size_x = item->header.dt_ptr->dt_Ffont->max_char_width
982     * item->input.size + 2 * TEXT_SPC + 2 * BOX_SPC;
983   item->header.size_y = item->header.dt_ptr->dt_Ffont->height
984     + 3 * TEXT_SPC + 2 * BOX_SPC;
985   myfprintf((stderr,"Input size_y is %d\n",item->header.size_y));
986 
987   if (CF.cur_input == 0) {		   /* first input field */
988     item->input.next_input = item;	/* ring, next field is first field */
989     item->input.prev_input = item;	/* ring, prev field is first field */
990     CF.first_input = item;		   /* save loc of first item */
991   } else {				/* not first field */
992     CF.cur_input->input.next_input = item; /* old next ptr point to this item */
993     item->input.prev_input = CF.cur_input; /* current items prev is old item */
994     item->input.next_input = CF.first_input; /* next is first item */
995     CF.first_input->input.prev_input = item; /* prev in first is this item */
996   }
997   CF.cur_input = item;			   /* new current input item */
998   myfprintf((stderr, "Input, %s, [%d], \"%s\"\n", item->header.name,
999 	  item->input.size, item->input.init_value));
1000   AddToLine(item);
1001 }
ct_Read(char * cp)1002 static void ct_Read(char *cp)
1003 {
1004   /* syntax: *FFRead 0 | 1 */
1005   myfprintf((stderr,"Got read command, char is %c\n",(int)*cp));
1006   endDefaultsRead = *cp;		/* copy whatever it is */
1007 }
1008 /* read and save vars from a file for later use in form
1009    painting.
1010 */
ct_UseData(char * cp)1011 static void ct_UseData(char *cp)
1012 {
1013   /* syntax: *FFUseData filename cmd_prefix */
1014   CF.file_to_read = CopySolidString(cp);
1015   if (*CF.file_to_read == 0) {
1016     fprintf(stderr,"UseData command missing first arg, File to read\n");
1017     return;
1018   }
1019   cp += strlen(CF.file_to_read);
1020   while (isspace((unsigned char)*cp)) cp++;
1021   CF.leading = CopySolidString(cp);
1022   if (*CF.leading == 0) {
1023     fprintf(stderr,"UseData command missing second arg, Leading\n");
1024     CF.file_to_read = 0;		   /* stop read */
1025     return;
1026   }
1027   /* Cant do the actual reading of the data file here,
1028      we are already in a readconfig loop. */
1029 }
ReadFormData(void)1030 static void ReadFormData(void)
1031 {
1032   int leading_len;
1033   char *line_buf;			/* ptr to curr config line */
1034   char cmd_buffer[200];
1035   sprintf(cmd_buffer,"read %s Quiet",CF.file_to_read);
1036   SendText(Channel,cmd_buffer,0);	/* read data */
1037   leading_len = strlen(CF.leading);
1038   InitGetConfigLine(Channel, CF.leading); /* ask for certain lines */
1039   while (GetConfigLine(Channel,&line_buf),line_buf) { /* while there is some */
1040     if (strncasecmp(line_buf, CF.leading, leading_len) == 0) { /* leading = */
1041       if (line_buf[strlen(line_buf)-1] == '\n') { /* if line ends with nl */
1042 	line_buf[strlen(line_buf)-1] = '\0'; /* strip off \n */
1043       }
1044       PutDataInForm(line_buf+leading_len); /* pass arg, space, value */
1045     } /* end match on arg 2, "leading" */
1046   } /* end while there is config data */
1047   free(CF.file_to_read);		/* dont need it anymore */
1048   free(CF.leading);
1049   CF.file_to_read = 0;
1050   CF.leading = 0;
1051 }
1052 /*
1053   Input is a line with varname space value.
1054   Search form for matching input fields and set values.
1055   If you don't get a match on an input field, try a choice.
1056 */
PutDataInForm(char * cp)1057 static void PutDataInForm(char *cp)
1058 {
1059   char *var_name;
1060   char *var_value;
1061   int var_len, i;
1062   Item *item;
1063   Line *line;
1064 
1065   var_name = CopySolidString(cp);	/* var */
1066   if (*var_name == 0) {
1067     return;
1068   }
1069   cp += strlen(var_name);
1070   while (isspace((unsigned char)*cp)) cp++;
1071   var_value = cp;
1072   if (CF.cur_input != 0) {
1073     item = CF.cur_input;
1074     do {
1075       if (strcasecmp(var_name,item->header.name) == 0) {
1076 	var_len = strlen(cp);
1077 	if (item->input.init_value)
1078 	  free(item->input.init_value);
1079 	item->input.init_value = safemalloc(var_len+1);
1080 	strcpy(item->input.init_value,cp); /* new initial value in field */
1081 	if (item->input.value)
1082 	  free(item->input.value);
1083 	item->input.buf = var_len+1;
1084 	item->input.value = safemalloc(item->input.buf);
1085 	strcpy(item->input.value,cp);	  /* new value in field */
1086 	free(var_name);			/* goto's have their uses */
1087 	return;
1088       }
1089       item=item->input.next_input;	  /* next input field */
1090     } while (item != CF.cur_input);	  /* while not end of ring */
1091   }
1092   /* You have a matching line, but it doesn't match an input
1093      field.  What to do?  I know, try a choice. */
1094   line = &root_line;			 /* start at first line */
1095   do {					/* for all lines */
1096     for (i = 0; i < line->n; i++) {	/* all items on line */
1097       item = line->items[i];
1098       if (item->type == I_CHOICE) {	/* choice is good */
1099 	if (strcasecmp(var_name,item->header.name) == 0) { /* match */
1100 	  item->choice.init_on = 0;
1101 	  if (strncasecmp(cp, "on", 2) == 0) {
1102 	    item->choice.init_on = 1;	/* set default state */
1103 	    free(var_name);		/* goto's have their uses */
1104 	    return;
1105 	  }
1106 	}
1107       }
1108     } /* end all items in line */
1109     line = line->next;			/* go to next line */
1110   } while (line != &root_line);		/* do all lines */
1111   /* You have a matching line, it didn't match an input field,
1112      and it didn't match a choice.  I've got it, it may match a
1113      selection, in which case we should use the value to
1114      set the choices! */
1115   for (item = root_item_ptr; item != 0;
1116        item = item->header.next) {/* all items */
1117     if (item->type == I_SELECT) {     /* selection is good */
1118       if (strcasecmp(var_name,item->header.name) == 0) { /* match */
1119 	/* Now find the choices for this selection... */
1120 	Item *choice_ptr;
1121 	for (i = 0;
1122 	     i<item->selection.n;
1123 	     i++) {
1124 	  choice_ptr=item->selection.choices[i];
1125 	  /* if the choice value matches the selection */
1126 	  if (strcmp(choice_ptr->choice.value,var_value) == 0) {
1127 	    choice_ptr->choice.init_on = 1;
1128 	  } else {
1129 	    choice_ptr->choice.init_on = 0;
1130 	  }
1131 	} /* end all choices */
1132       } /* end match */
1133     } /* end selection */
1134   } /* end all items */
1135   free(var_name);			/* not needed now */
1136 }
ct_Selection(char * cp)1137 static void ct_Selection(char *cp)
1138 {
1139   /* syntax: *FFSelection <name> single | multiple */
1140   AddItem();
1141   cur_sel = item;			/* save ptr as cur_sel */
1142   cur_sel->type = I_SELECT;
1143   cur_sel->header.name = CopySolidString(cp);
1144   cp += strlen(cur_sel->header.name);
1145   while (isspace((unsigned char)*cp)) cp++;
1146   if (strncasecmp(cp, "multiple", 8) == 0)
1147     cur_sel->selection.key = IS_MULTIPLE;
1148   else
1149     cur_sel->selection.key = IS_SINGLE;
1150 }
ct_Choice(char * cp)1151 static void ct_Choice(char *cp)
1152 {
1153   /* syntax: *FFChoice <name> <value> [on | _off_] ["<text>"] */
1154   /* This next edit is a liitle weak, the selection should be right
1155      before the choice. At least a core dump is avoided. */
1156   if (cur_sel == 0) {			/* need selection for a choice */
1157     fprintf(stderr,"%s: Need selection for choice %s\n",
1158 	    module->name, cp);
1159     return;
1160   }
1161   bg_state = 'u';			/* indicate b/g color now used. */
1162   AddItem();
1163   item->type = I_CHOICE;
1164   AssignDrawTable(item);
1165   item->header.name = CopySolidString(cp);
1166   cp += strlen(item->header.name);
1167   while (isspace((unsigned char)*cp)) cp++;
1168   item->choice.value = CopySolidString(cp);
1169   cp += strlen(item->choice.value);
1170   while (isspace((unsigned char)*cp)) cp++;
1171   if (strncasecmp(cp, "on", 2) == 0)
1172     item->choice.init_on = 1;
1173   else
1174     item->choice.init_on = 0;
1175   while (!isspace((unsigned char)*cp)) cp++;
1176   while (isspace((unsigned char)*cp)) cp++;
1177   if (*cp == '\"')
1178     item->choice.text = CopyQuotedString(++cp);
1179   else
1180     item->choice.text = "";
1181   item->choice.n = strlen(item->choice.text);
1182   item->choice.sel = cur_sel;
1183 
1184   if (cur_sel->selection.choices_array_count
1185       <= cur_sel->selection.n) {	   /* no room */
1186     cur_sel->selection.choices_array_count += CHOICES_PER_SEL_EXPANSION;
1187     cur_sel->selection.choices =
1188       (Item **)saferealloc((void *)cur_sel->selection.choices,
1189 			   sizeof(Item *) *
1190 			   cur_sel->selection.choices_array_count); /* expand */
1191   }
1192 
1193   cur_sel->selection.choices[cur_sel->selection.n++] = item;
1194   item->header.size_y = item->header.dt_ptr->dt_Ffont->height
1195      + 2 * TEXT_SPC;
1196   /* this is weird, the x dimension is the sum of the height and width? */
1197   item->header.size_x = item->header.dt_ptr->dt_Ffont->height
1198      + 4 * TEXT_SPC +
1199      FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
1200        item->choice.text, item->choice.n);
1201   myfprintf((stderr, "Choice %s, \"%s\", [%d, %d]\n", item->header.name,
1202 	  item->choice.text, item->header.size_x, item->header.size_y));
1203   AddToLine(item);
1204 }
ct_Button(char * cp)1205 static void ct_Button(char *cp)
1206 {
1207   /* syntax: *FFButton continue | restart | quit "<text>" */
1208   AddItem();
1209   item->type = I_BUTTON;
1210   AssignDrawTable(item);
1211   item->header.name = "";
1212   if (strncasecmp(cp, "restart", 7) == 0)
1213     item->button.key = IB_RESTART;
1214   else if (strncasecmp(cp, "quit", 4) == 0)
1215     item->button.key = IB_QUIT;
1216   else
1217     item->button.key = IB_CONTINUE;
1218   while (!isspace((unsigned char)*cp)) cp++;
1219   while (isspace((unsigned char)*cp)) cp++;
1220   if (*cp == '\"') {
1221     item->button.text = CopyQuotedString(++cp);
1222     cp += strlen(item->button.text) + 1;
1223     while (isspace((unsigned char)*cp)) cp++;
1224   } else
1225     item->button.text = "";
1226   if (*cp == '^')
1227     item->button.keypress = *(++cp) - '@';
1228   else if (*cp == 'F')
1229     item->button.keypress = 256 + atoi(++cp);
1230   else
1231     item->button.keypress = -1;
1232   item->button.len = strlen(item->button.text);
1233   item->header.size_y = item->header.dt_ptr->dt_Ffont->height
1234      + 2 * TEXT_SPC + 2 * BOX_SPC;
1235   item->header.size_x = 2 * TEXT_SPC + 2 * BOX_SPC
1236   + FlocaleTextWidth(item->header.dt_ptr->dt_Ffont, item->button.text,
1237     item->button.len);
1238   AddToLine(item);
1239   cur_button = item;
1240   myfprintf((stderr,"Created button, fore %s, bg %s, text %s\n",
1241 	     item->header.dt_ptr->dt_color_names[c_item_fg],
1242 	     item->header.dt_ptr->dt_color_names[c_item_bg],
1243 	     item->button.text));
1244 }
ct_Command(char * cp)1245 static void ct_Command(char *cp)
1246 {
1247   /* syntax: *FFCommand <command> */
1248   if (cur_button->button.button_array_size <= cur_button->button.n)
1249   {
1250     cur_button->button.button_array_size += BUTTON_COMMAND_EXPANSION;
1251     cur_button->button.commands =
1252       (char **)saferealloc((void *)cur_button->button.commands,
1253 			   sizeof(char *) *
1254 			   cur_button->button.button_array_size);
1255   }
1256   cur_button->button.commands[cur_button->button.n++] = safestrdup(cp);
1257 }
1258 
1259 /* End of ct_ routines */
1260 
1261 /* Init constants with values that can be freed later. */
InitConstants(void)1262 static void InitConstants(void) {
1263   color_names[0]=safestrdup("Light Gray");
1264   color_names[1]=safestrdup("Black");
1265   color_names[2]=safestrdup("Gray50");
1266   color_names[3]=safestrdup("Wheat");
1267   font_names[0]=safestrdup("8x13bold");
1268   font_names[1]=safestrdup("8x13bold");
1269   font_names[2]=safestrdup("8x13bold");
1270   font_names[3]=safestrdup("8x13bold");
1271   screen_background_color=safestrdup("Light Gray");
1272   CF.p_c[input_fore].pointer_color.pixel = PictureWhitePixel();
1273   CF.p_c[input_back].pointer_color.pixel = PictureBlackPixel();
1274   CF.p_c[button_fore].pointer_color.pixel = PictureBlackPixel();
1275   CF.p_c[button_back].pointer_color.pixel = PictureWhitePixel();
1276   /* The in pointer is the reverse of the hover pointer. */
1277   CF.p_c[button_in_fore].pointer_color.pixel = PictureWhitePixel();
1278   CF.p_c[button_in_back].pointer_color.pixel = PictureBlackPixel();
1279 }
1280 
1281 /* read the configuration file */
ReadDefaults(void)1282 static void ReadDefaults(void)
1283 {
1284   char *line_buf;			/* ptr to curr config line */
1285 
1286   /* default button is for initial functions */
1287   cur_button = &CF.def_button;
1288   CF.def_button.button.key = IB_CONTINUE;
1289 
1290    /*
1291     * Reading .FvwmFormDefault for every form seems slow.
1292     *
1293     * This next bit puts a command at the end of the module command
1294     * queue in fvwm that indicates whether the file has to be read.
1295     *
1296     * Read defaults looking for "*FvwmFormDefaultRead"
1297     * if not there, send read,
1298     * then "*FvwmFormDefaultRead y",
1299     * then look thru defaults again.
1300     * The customization dialog sends "*FvwmFormDefaultRead n"
1301     * to start over.
1302     */
1303   InitGetConfigLine(Channel,"*FvwmFormDefault");
1304   while (GetConfigLine(Channel,&line_buf),line_buf) { /* get config from fvwm */
1305     ParseDefaults(line_buf);		 /* process default config lines 1st */
1306   }
1307   if (endDefaultsRead == 'y') {		/* defaults read already */
1308     myfprintf((stderr,"Defaults read, no need to read file.\n"));
1309     return;
1310   } /* end defaults read already */
1311   SendText(Channel,"read .FvwmForm Quiet",0); /* read default config */
1312   SendText(Channel,"*FvwmFormDefaultRead y",0); /* remember you read it */
1313 
1314   InitGetConfigLine(Channel,"*FvwmFormDefault");
1315   while (GetConfigLine(Channel,&line_buf),line_buf) { /* get config from fvwm */
1316     ParseDefaults(line_buf);		 /* process default config lines 1st */
1317   }
1318 } /* done */
1319 
ReadConfig(void)1320 static void ReadConfig(void)
1321 {
1322   char *line_buf;			/* ptr to curr config line */
1323 
1324   InitGetConfigLine(Channel,CatString3("*",module->name,0));
1325   while (GetConfigLine(Channel,&line_buf),line_buf) { /* get config from fvwm */
1326     ParseConfigLine(line_buf);		/* process config lines */
1327   }
1328 } /* done */
1329 
1330 /* After this config is read, figure it out */
MassageConfig(void)1331 static void MassageConfig(void)
1332 {
1333   int i, extra;
1334   Line *line;				/* for scanning form lines */
1335 
1336   if (CF.file_to_read) {		/* if theres a data file to read */
1337     ReadFormData();			/* go read it */
1338   }
1339   /* get the geometry right */
1340   CF.max_width = 0;
1341   CF.total_height = ITEM_VSPC;
1342   line = &root_line;			 /* start at first line */
1343   do {					/* for all lines */
1344     for (i = 0; i < line->n; i++) {
1345       line->items[i]->header.pos_y = CF.total_height;
1346       if (line->items[i]->header.size_y < line->size_y)
1347 	line->items[i]->header.pos_y +=
1348 	  (line->size_y - line->items[i]->header.size_y) / 2 + 1 ;
1349     } /* end all items in line */
1350     CF.total_height += ITEM_VSPC + line->size_y;
1351     line->size_x += (line->n + 1) * ITEM_HSPC;
1352     if (line->size_x > CF.max_width)
1353       CF.max_width = line->size_x;
1354     line = line->next;			/* go to next line */
1355   } while (line != &root_line);		/* do all lines */
1356   do {					/* note, already back at root_line */
1357     int width;
1358     switch (line->justify) {
1359     case L_LEFT:
1360       width = ITEM_HSPC;
1361       for (i = 0; i < line->n; i++) {
1362 	line->items[i]->header.pos_x = width;
1363 	width += ITEM_HSPC + line->items[i]->header.size_x;
1364       }
1365       break;
1366     case L_RIGHT:
1367       width = CF.max_width - line->size_x + ITEM_HSPC;
1368       for (i = 0; i < line->n; i++) {
1369 	line->items[i]->header.pos_x = width;
1370 	width += ITEM_HSPC + line->items[i]->header.size_x;
1371       }
1372       break;
1373     case L_CENTER:
1374       width = (CF.max_width - line->size_x) / 2 + ITEM_HSPC;
1375       for (i = 0; i < line->n; i++) {
1376 	line->items[i]->header.pos_x = width;
1377 	myfprintf((stderr, "Line Item[%d] @ (%d, %d)\n", i,
1378 	       line->items[i]->header.pos_x, line->items[i]->header.pos_y));
1379 	width += ITEM_HSPC + line->items[i]->header.size_x;
1380       }
1381       break;
1382     case L_LEFTRIGHT:
1383     /* count the number of inputs on the line - the extra space will be
1384      * shared amongst these if there are any, otherwise it will be added
1385      * as space in between the elements
1386      */
1387       extra = 0 ;
1388       for (i = 0 ; i < line->n ; i++) {
1389 	if (line->items[i]->type == I_INPUT)
1390 	  extra++ ;
1391       }
1392       if (extra == 0) {
1393 	if (line->n < 2) {  /* same as L_CENTER */
1394 	  width = (CF.max_width - line->size_x) / 2 + ITEM_HSPC;
1395 	  for (i = 0; i < line->n; i++) {
1396 	    line->items[i]->header.pos_x = width;
1397 	    width += ITEM_HSPC + line->items[i]->header.size_x;
1398 	  }
1399 	} else {
1400 	  extra = (CF.max_width - line->size_x) / (line->n - 1);
1401 	  width = ITEM_HSPC;
1402 	  for (i = 0; i < line->n; i++) {
1403 	    line->items[i]->header.pos_x = width;
1404 	    width += ITEM_HSPC + line->items[i]->header.size_x + extra;
1405 	  }
1406 	}
1407       } else {
1408 	extra = (CF.max_width - line->size_x) / extra ;
1409 	width = ITEM_HSPC ;
1410 	for (i = 0 ; i < line->n ; i++) {
1411 	  line->items[i]->header.pos_x = width ;
1412 	  if (line->items[i]->type == I_INPUT)
1413 	    line->items[i]->header.size_x += extra ;
1414 	  width += ITEM_HSPC + line->items[i]->header.size_x ;
1415 	}
1416       }
1417       break;
1418     }
1419     line = line->next;			/* go to next line */
1420   } while (line != &root_line);		 /* do all lines */
1421 }
1422 
1423 /* reset all the values (also done on first display) */
Restart(void)1424 static void Restart(void)
1425 {
1426   Item *item;
1427 
1428   CF.cur_input = NULL;
1429   CF.abs_cursor = CF.rel_cursor = 0;
1430   for (item = root_item_ptr; item != 0;
1431        item = item->header.next) {/* all items */
1432     switch (item->type) {
1433     case I_INPUT:
1434       if (!CF.cur_input)
1435 	CF.cur_input = item;
1436 
1437       /* save old input values in a recall ring. */
1438       if (item->input.value && item->input.value[0] != 0) { /* ? to save */
1439 	if (item->input.value_history_ptr == 0) {  /* no history yet */
1440 	  item->input.value_history_ptr =
1441 	    (char **)safecalloc(sizeof(char *), 50);
1442 	  item->input.value_history_ptr[0] = safestrdup(item->input.value);
1443 	  item->input.value_history_count = 1; /* next insertion point */
1444 	  myfprintf((stderr,"Initial save of %s in slot 0\n",
1445 		     item->input.value_history_ptr[0]));
1446 	} else {			/* we have a history */
1447 	  int prior;
1448 	  prior = item->input.value_history_count - 1;
1449 	  if (prior < 0) {
1450 	    for (prior = VH_SIZE - 1;
1451 		 CF.cur_input->input.value_history_ptr[prior] == 0;
1452 		 --prior);		/* find last used slot */
1453 	  }
1454 	  myfprintf((stderr,"Prior is %d, compare %s to %s\n",
1455 		     prior, item->input.value,
1456 		     item->input.value_history_ptr[prior]));
1457 
1458 	  if ( strcmp(item->input.value, item->input.value_history_ptr[prior])
1459 	       != 0) {			/* different value */
1460 	    if (item->input.value_history_ptr[item->input.value_history_count])
1461 	    {
1462 	      free(item->input.value_history_ptr[
1463 			   item->input.value_history_count]);
1464 	      myfprintf((stderr,"Freeing old item in slot %d\n",
1465 			 item->input.value_history_count));
1466 	    }
1467 	    item->input.value_history_ptr[item->input.value_history_count] =
1468 	      safestrdup(item->input.value); /* save value ptr in array */
1469 	    myfprintf((stderr,"Save of %s in slot %d\n",
1470 		       item->input.value,
1471 		       item->input.value_history_count));
1472 
1473 	    /* leave count pointing at the next insertion point. */
1474 	    if (item->input.value_history_count < VH_SIZE - 1) { /* not full */
1475 	      ++item->input.value_history_count;    /* next slot */
1476 	    } else {
1477 	      item->input.value_history_count = 0;  /* wrap around */
1478 	    }
1479 	  } /* end something different */
1480 	} /* end have a history */
1481 	myfprintf((stderr,"New history yankat %d\n",
1482 		   item->input.value_history_yankat));
1483       } /* end something to save */
1484       item->input.value_history_yankat = item->input.value_history_count;
1485       item->input.n = strlen(item->input.init_value);
1486       strcpy(item->input.value, item->input.init_value);
1487       item->input.left = 0;
1488       break;
1489     case I_CHOICE:
1490       item->choice.on = item->choice.init_on;
1491       break;
1492     }
1493   }
1494 }
1495 
1496 /* redraw the frame */
RedrawFrame(XEvent * pev)1497 void RedrawFrame(XEvent *pev)
1498 {
1499 	Item *item;
1500 	Region region = None;
1501 
1502 	Bool clear = False;
1503 	if (FftSupport)
1504 	{
1505 		item = root_item_ptr;
1506 		while(item != 0 && !clear)
1507 		{
1508 			if ((item->type == I_TEXT || item->type == I_CHOICE) &&
1509 			    item->header.dt_ptr->dt_Ffont->fftf.fftfont != NULL)
1510 				clear = True;
1511 			item = item->header.next;
1512 		}
1513 		if (clear && pev)
1514 		{
1515 			XClearArea(
1516 				dpy, CF.frame,
1517 				pev->xexpose.x, pev->xexpose.y,
1518 				pev->xexpose.width, pev->xexpose.height,
1519 				False);
1520 		}
1521 		else
1522 		{
1523 			XClearWindow(dpy, CF.frame);
1524 		}
1525 	}
1526 	if (pev)
1527 	{
1528 		XRectangle r;
1529 
1530 		r.x = pev->xexpose.x;
1531 		r.y =  pev->xexpose.y;
1532 		r.width = pev->xexpose.width;
1533 		r.height = pev->xexpose.height;
1534 		region = XCreateRegion();
1535 		XUnionRectWithRegion (&r, region, region);
1536 	}
1537 	for (item = root_item_ptr; item != 0;
1538 	     item = item->header.next) {      /* all items */
1539 		DrawTable *dt_ptr = item->header.dt_ptr;
1540 
1541 		if (dt_ptr && dt_ptr->dt_Fstr)
1542 		{
1543 			if (region)
1544 			{
1545 				dt_ptr->dt_Fstr->flags.has_clip_region = True;
1546 				dt_ptr->dt_Fstr->clip_region = region;
1547 			}
1548 			else
1549 			{
1550 				dt_ptr->dt_Fstr->flags.has_clip_region = False;
1551 			}
1552 		}
1553 		switch (item->type) {
1554 		case I_TEXT:
1555 			RedrawText(item);
1556 			break;
1557 		case I_TIMEOUT:
1558 			RedrawTimeout(item);
1559 			break;
1560 		case I_CHOICE:
1561 			dt_ptr->dt_Fstr->win = CF.frame;
1562 			dt_ptr->dt_Fstr->gc  = dt_ptr->dt_GC;
1563 			dt_ptr->dt_Fstr->str = item->choice.text;
1564 			dt_ptr->dt_Fstr->x   = item->header.pos_x
1565 				+ TEXT_SPC + item->header.size_y;
1566 			dt_ptr->dt_Fstr->y   = item->header.pos_y
1567 				+ TEXT_SPC + dt_ptr->dt_Ffont->ascent;
1568 			dt_ptr->dt_Fstr->len = item->choice.n;
1569 			dt_ptr->dt_Fstr->flags.has_colorset = False;
1570 			if (itemcolorset >= 0)
1571 			{
1572 				dt_ptr->dt_Fstr->colorset =
1573 					&Colorset[itemcolorset];
1574 				dt_ptr->dt_Fstr->flags.has_colorset
1575 					= True;
1576 			}
1577 			FlocaleDrawString(dpy,
1578 					  dt_ptr->dt_Ffont,
1579 					  dt_ptr->dt_Fstr,
1580 					  FWS_HAVE_LENGTH);
1581 			break;
1582 		case I_SEPARATOR:
1583 			myfprintf((stderr, "redraw_frame calling RedrawSeparator\n"));
1584 			RedrawSeparator(item);
1585 			break;
1586 		}
1587 		if (dt_ptr && dt_ptr->dt_Fstr)
1588 		{
1589 			dt_ptr->dt_Fstr->flags.has_clip_region = False;
1590 		}
1591 	}
1592 	if (region)
1593 	{
1594 		XDestroyRegion(region);
1595 	}
1596 }
1597 
RedrawText(Item * item)1598 void RedrawText(Item *item)
1599 {
1600   char *p;
1601 
1602   CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
1603   item->header.dt_ptr->dt_Fstr->len = item->text.n;
1604   if ((p = memchr(item->text.value, '\0', item->header.dt_ptr->dt_Fstr->len))
1605       != NULL)
1606     item->header.dt_ptr->dt_Fstr->len = p - item->text.value;
1607   item->header.dt_ptr->dt_Fstr->win = CF.frame;
1608   item->header.dt_ptr->dt_Fstr->gc  = item->header.dt_ptr->dt_GC;
1609   item->header.dt_ptr->dt_Fstr->str = item->text.value;
1610   item->header.dt_ptr->dt_Fstr->x   = item->header.pos_x + TEXT_SPC;
1611   item->header.dt_ptr->dt_Fstr->y   = item->header.pos_y + ( CF.padVText / 2 ) +
1612     item->header.dt_ptr->dt_Ffont->ascent;
1613   item->header.dt_ptr->dt_Fstr->flags.has_colorset = False;
1614   if (colorset >= 0)
1615   {
1616     item->header.dt_ptr->dt_Fstr->colorset = &Colorset[colorset];
1617     item->header.dt_ptr->dt_Fstr->flags.has_colorset = True;
1618   }
1619   FlocaleDrawString(dpy,
1620 		    item->header.dt_ptr->dt_Ffont,
1621 		    item->header.dt_ptr->dt_Fstr, FWS_HAVE_LENGTH);
1622   return;
1623 }
1624 
1625 /*
1626  *  Draw horizontal lines to form a separator
1627  *
1628  */
RedrawSeparator(Item * item)1629 static void RedrawSeparator(Item *item)
1630 {
1631 
1632   item->header.size_x = CF.max_width - 6;
1633   CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
1634   if ( item->header.dt_ptr && item->header.dt_ptr->dt_colors[c_itemlo] ) { /* safety */
1635     XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,item->header.dt_ptr->dt_colors[c_itemlo]);
1636     XDrawLine(dpy, item->header.win,item->header.dt_ptr->dt_item_GC, 0, 0, item->header.size_x, 0);
1637     XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,item->header.dt_ptr->dt_colors[c_itemhi]);
1638     XDrawLine(dpy, item->header.win,item->header.dt_ptr->dt_item_GC, 0, 1, item->header.size_x, 1);
1639 
1640   } else {
1641     fprintf(stderr,"%s: Separators, no colors %p\n",module->name,item->header.dt_ptr);
1642   }
1643   return;
1644 }
1645 
RedrawTimeout(Item * item)1646 void RedrawTimeout(Item *item)
1647 {
1648   char *p;
1649   char *tmpbuf, *tmpptr, *tmpbptr;
1650   int reallen;
1651 
1652   XClearArea(dpy, CF.frame,
1653 	       item->header.pos_x, item->header.pos_y,
1654 	       item->header.size_x, item->header.size_y,
1655 	       False);
1656 
1657   tmpbuf = safemalloc(item->timeout.len + 6);
1658   tmpbptr = tmpbuf;
1659   for (tmpptr = item->timeout.text; *tmpptr != '\0' &&
1660 		!(tmpptr[0] == '%' && tmpptr[1] == '%'); tmpptr++) {
1661     *tmpbptr = *tmpptr;
1662     tmpbptr++;
1663   }
1664   if (tmpptr[0] == '%') {
1665     tmpptr++; tmpptr++;
1666     sprintf(tmpbptr, "%d", item->timeout.timeleft);
1667     tmpbptr += strlen(tmpbptr);
1668   }
1669   for (; *tmpptr != '\0'; tmpptr++) {
1670     *tmpbptr = *tmpptr;
1671     tmpbptr++;
1672   }
1673   *tmpbptr = '\0';
1674 
1675   reallen = strlen(tmpbuf);
1676   item->header.size_x = FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
1677 				   tmpbuf, reallen) + 2 * TEXT_SPC;
1678   item->header.size_y = item->header.dt_ptr->dt_Ffont->height + CF.padVText;
1679 
1680   CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
1681   item->header.dt_ptr->dt_Fstr->len = reallen;
1682   if ((p = memchr(item->timeout.text, '\0', item->header.dt_ptr->dt_Fstr->len))
1683       != NULL)
1684     item->header.dt_ptr->dt_Fstr->len = p - tmpbuf;
1685   item->header.dt_ptr->dt_Fstr->win = CF.frame;
1686   item->header.dt_ptr->dt_Fstr->gc  = item->header.dt_ptr->dt_GC;
1687   item->header.dt_ptr->dt_Fstr->flags.has_colorset = False;
1688   if (colorset >= 0)
1689   {
1690     item->header.dt_ptr->dt_Fstr->colorset = &Colorset[colorset];
1691     item->header.dt_ptr->dt_Fstr->flags.has_colorset = True;
1692   }
1693   if (item->header.dt_ptr->dt_Fstr->str != NULL)
1694     free(item->header.dt_ptr->dt_Fstr->str);
1695   item->header.dt_ptr->dt_Fstr->str = safestrdup(tmpbuf);
1696   item->header.dt_ptr->dt_Fstr->x   = item->header.pos_x + TEXT_SPC;
1697   item->header.dt_ptr->dt_Fstr->y   = item->header.pos_y + ( CF.padVText / 2 ) +
1698     item->header.dt_ptr->dt_Ffont->ascent;
1699   FlocaleDrawString(dpy,
1700 		    item->header.dt_ptr->dt_Ffont,
1701 		    item->header.dt_ptr->dt_Fstr, FWS_HAVE_LENGTH);
1702   free(tmpbuf);
1703   return;
1704 }
1705 
1706 /* redraw an item */
RedrawItem(Item * item,int click,XEvent * pev)1707 void RedrawItem (Item *item, int click, XEvent *pev)
1708 {
1709   int dx, dy, len, x;
1710   static XSegment xsegs[4];
1711   XRectangle r,inter;
1712   Region region = None;
1713   Bool text_inter = True;
1714   DrawTable *dt_ptr = item->header.dt_ptr;
1715 
1716   /* Init intersection to size of the item. */
1717   inter.x = BOX_SPC + TEXT_SPC - 1;
1718   inter.y = BOX_SPC;
1719   inter.width = item->header.size_x - (2 * BOX_SPC) - 2 - TEXT_SPC;
1720   inter.height = (item->header.size_y - 1) - 2 * BOX_SPC + 1;
1721 
1722   myfprintf((stderr,"%s: RedrawItem expose x=%d/y=%d h=%d/w=%d\n",module->name,
1723 	     (int)pev->xexpose.x,
1724 	     (int)pev->xexpose.y,
1725 	     (int)pev->xexpose.width,
1726 	     (int)pev->xexpose.height));
1727 
1728   /* This is a slightly altered expose event from ReadXServer. */
1729   if (pev)
1730   {
1731 	  r.x = pev->xexpose.x;
1732 	  r.y =	 pev->xexpose.y;
1733 	  r.width = pev->xexpose.width;
1734 	  r.height = pev->xexpose.height;
1735 	  text_inter = frect_get_intersection(
1736 		  r.x, r.y, r.width, r.height,
1737 		  inter.x,inter.y,inter.width,inter.height,
1738 		  &inter);
1739   }
1740   else
1741   {
1742 	  /* If its not an expose event, the area assume the
1743 	     whole item is intersected. */
1744 	  r.x = inter.x;
1745 	  r.y = inter.y;
1746 	  r.width = inter.width;
1747 	  r.height = inter.height;
1748   }
1749   if (pev && text_inter && dt_ptr && dt_ptr->dt_Fstr)
1750   {
1751 	  region = XCreateRegion();
1752 	  XUnionRectWithRegion (&r, region, region);
1753 	  if (region)
1754 	  {
1755 		  dt_ptr->dt_Fstr->flags.has_clip_region = True;
1756 		  dt_ptr->dt_Fstr->clip_region = region;
1757 	  }
1758 	  else
1759 	  {
1760 		  dt_ptr->dt_Fstr->flags.has_clip_region = False;
1761 	  }
1762   }
1763 
1764   switch (item->type) {
1765   case I_INPUT:
1766     if (!text_inter)
1767     {
1768 	    return;
1769     }
1770     /* Create frame (pressed in): */
1771     dx = item->header.size_x - 1;
1772     dy = item->header.size_y - 1;
1773     /*fprintf(stderr,"GC: %lu\n", item->header.dt_ptr->dt_item_GC);*/
1774     XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1775 		   item->header.dt_ptr->dt_colors[c_itemlo]);
1776 
1777     /* around 12/26/99, added XClearArea to this function.
1778        this was done to deal with display corruption during
1779        multifield paste.  dje */
1780     if (frect_get_intersection(
1781 	    r.x, r.y, r.width, r.height,
1782 	    inter.x, inter.y, inter.width, inter.height,
1783 	    &inter))
1784     {
1785 	    XClearArea(
1786 		       dpy, item->header.win,
1787 		       inter.x, inter.y, inter.width, inter.height, False);
1788     }
1789 
1790     xsegs[0].x1 = 0, xsegs[0].y1 = 0;
1791     xsegs[0].x2 = 0, xsegs[0].y2 = dy;
1792     xsegs[1].x1 = 0, xsegs[1].y1 = 0;
1793     xsegs[1].x2 = dx, xsegs[1].y2 = 0;
1794     xsegs[2].x1 = 1, xsegs[2].y1 = 1;
1795     xsegs[2].x2 = 1, xsegs[2].y2 = dy - 1;
1796     xsegs[3].x1 = 1, xsegs[3].y1 = 1;
1797     xsegs[3].x2 = dx - 1, xsegs[3].y2 = 1;
1798     XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1799 		  xsegs, 4);
1800     XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1801 		   item->header.dt_ptr->dt_colors[c_itemhi]);
1802     xsegs[0].x1 = 1, xsegs[0].y1 = dy;
1803     xsegs[0].x2 = dx, xsegs[0].y2 = dy;
1804     xsegs[1].x1 = 2, xsegs[1].y1 = dy - 1;
1805     xsegs[1].x2 = dx, xsegs[1].y2 = dy - 1;
1806     xsegs[2].x1 = dx, xsegs[2].y1 = 1;
1807     xsegs[2].x2 = dx, xsegs[2].y2 = dy;
1808     xsegs[3].x1 = dx - 1, xsegs[3].y1 = 2;
1809     xsegs[3].x2 = dx - 1, xsegs[3].y2 = dy;
1810     XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1811 		  xsegs, 4);
1812 
1813     if (click) {
1814       x = BOX_SPC + TEXT_SPC +
1815 	      FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
1816 			       item->input.value, CF.abs_cursor) - 1;
1817       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1818 		     item->header.dt_ptr->dt_colors[c_item_bg]);
1819       XDrawLine(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1820 		x, BOX_SPC, x, dy - BOX_SPC);
1821       myfprintf((stderr,"Line %d/%d - %d/%d (first)\n",
1822 		     x, BOX_SPC, x, dy - BOX_SPC));
1823     }
1824     len = item->input.n - item->input.left;
1825     XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1826 		   item->header.dt_ptr->dt_colors[c_item_fg]);
1827     item->header.dt_ptr->dt_Fstr->win = item->header.win;
1828     item->header.dt_ptr->dt_Fstr->gc  = item->header.dt_ptr->dt_item_GC;
1829     item->header.dt_ptr->dt_Fstr->flags.has_colorset = False;
1830     if (itemcolorset >= 0)
1831     {
1832       item->header.dt_ptr->dt_Fstr->colorset = &Colorset[itemcolorset];
1833       item->header.dt_ptr->dt_Fstr->flags.has_colorset = True;
1834     }
1835     if (len > item->input.size)
1836       len = item->input.size;
1837     else
1838     {
1839       item->header.dt_ptr->dt_Fstr->str = item->input.blanks;
1840       item->header.dt_ptr->dt_Fstr->x  = BOX_SPC + TEXT_SPC +
1841 	      FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
1842 			       item->input.blanks, len);
1843       item->header.dt_ptr->dt_Fstr->y	= BOX_SPC + TEXT_SPC
1844 		  + item->header.dt_ptr->dt_Ffont->ascent;
1845       item->header.dt_ptr->dt_Fstr->len = item->input.size - len;
1846       FlocaleDrawString(dpy,
1847 			item->header.dt_ptr->dt_Ffont,
1848 			item->header.dt_ptr->dt_Fstr, FWS_HAVE_LENGTH);
1849     }
1850     item->header.dt_ptr->dt_Fstr->str = item->input.value;
1851     item->header.dt_ptr->dt_Fstr->x   = BOX_SPC + TEXT_SPC;
1852     item->header.dt_ptr->dt_Fstr->y   = BOX_SPC + TEXT_SPC
1853       + item->header.dt_ptr->dt_Ffont->ascent;
1854     item->header.dt_ptr->dt_Fstr->len = len;
1855     FlocaleDrawString(dpy,
1856 		      item->header.dt_ptr->dt_Ffont,
1857 		      item->header.dt_ptr->dt_Fstr, FWS_HAVE_LENGTH);
1858     if (item == CF.cur_input && !click) {
1859       x = BOX_SPC + TEXT_SPC +
1860 	FlocaleTextWidth(item->header.dt_ptr->dt_Ffont,
1861 			 item->input.value,CF.abs_cursor)
1862 	- 1;
1863       XDrawLine(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1864 		x, BOX_SPC, x, dy - BOX_SPC);
1865       myfprintf((stderr,"Line %d/%d - %d/%d\n",
1866 		 x, BOX_SPC, x, dy - BOX_SPC));
1867     }
1868     myfprintf((stderr,"Just drew input field. click %d\n",(int)click));
1869     break;
1870   case I_CHOICE:
1871     dx = dy = item->header.size_y - 1;
1872     if (item->choice.on) {
1873 	XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1874 		       item->header.dt_ptr->dt_colors[c_item_fg]);
1875       if (item->choice.sel->selection.key == IS_MULTIPLE) {
1876 	xsegs[0].x1 = 5, xsegs[0].y1 = 5;
1877 	xsegs[0].x2 = dx - 5, xsegs[0].y2 = dy - 5;
1878 	xsegs[1].x1 = 5, xsegs[1].y1 = dy - 5;
1879 	xsegs[1].x2 = dx - 5, xsegs[1].y2 = 5;
1880 	XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1881 		      xsegs, 2);
1882       } else {
1883 	XDrawArc(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1884 		 5, 5, dx - 10, dy - 10, 0, 360 * 64);
1885       }
1886     } else
1887       XClearWindow(dpy, item->header.win);
1888     if (item->choice.on)
1889       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1890 		     item->header.dt_ptr->dt_colors[c_itemlo]);
1891     else
1892       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1893 		     item->header.dt_ptr->dt_colors[c_itemhi]);
1894     xsegs[0].x1 = 0, xsegs[0].y1 = 0;
1895     xsegs[0].x2 = 0, xsegs[0].y2 = dy;
1896     xsegs[1].x1 = 0, xsegs[1].y1 = 0;
1897     xsegs[1].x2 = dx, xsegs[1].y2 = 0;
1898     xsegs[2].x1 = 1, xsegs[2].y1 = 1;
1899     xsegs[2].x2 = 1, xsegs[2].y2 = dy - 1;
1900     xsegs[3].x1 = 1, xsegs[3].y1 = 1;
1901     xsegs[3].x2 = dx - 1, xsegs[3].y2 = 1;
1902     XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1903 		  xsegs, 4);
1904     if (item->choice.on)
1905       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1906 		     item->header.dt_ptr->dt_colors[c_itemhi]);
1907     else
1908       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1909 		     item->header.dt_ptr->dt_colors[c_itemlo]);
1910     xsegs[0].x1 = 1, xsegs[0].y1 = dy;
1911     xsegs[0].x2 = dx, xsegs[0].y2 = dy;
1912     xsegs[1].x1 = 2, xsegs[1].y1 = dy - 1;
1913     xsegs[1].x2 = dx, xsegs[1].y2 = dy - 1;
1914     xsegs[2].x1 = dx, xsegs[2].y1 = 1;
1915     xsegs[2].x2 = dx, xsegs[2].y2 = dy;
1916     xsegs[3].x1 = dx - 1, xsegs[3].y1 = 2;
1917     xsegs[3].x2 = dx - 1, xsegs[3].y2 = dy;
1918     XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1919 		  xsegs, 4);
1920     break;
1921   case I_BUTTON:
1922     if (!text_inter)
1923     {
1924 	    return;
1925     }
1926     dx = item->header.size_x - 1;
1927     dy = item->header.size_y - 1;
1928     if (click)
1929       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1930 		     item->header.dt_ptr->dt_colors[c_itemlo]);
1931     else
1932       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1933 		     item->header.dt_ptr->dt_colors[c_itemhi]);
1934     xsegs[0].x1 = 0, xsegs[0].y1 = 0;
1935     xsegs[0].x2 = 0, xsegs[0].y2 = dy;
1936     xsegs[1].x1 = 0, xsegs[1].y1 = 0;
1937     xsegs[1].x2 = dx, xsegs[1].y2 = 0;
1938     xsegs[2].x1 = 1, xsegs[2].y1 = 1;
1939     xsegs[2].x2 = 1, xsegs[2].y2 = dy - 1;
1940     xsegs[3].x1 = 1, xsegs[3].y1 = 1;
1941     xsegs[3].x2 = dx - 1, xsegs[3].y2 = 1;
1942     XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1943 		  xsegs, 4);
1944     if (click)
1945       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1946 		     item->header.dt_ptr->dt_colors[c_itemhi]);
1947     else
1948       XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1949 		     item->header.dt_ptr->dt_colors[c_itemlo]);
1950     xsegs[0].x1 = 1, xsegs[0].y1 = dy;
1951     xsegs[0].x2 = dx, xsegs[0].y2 = dy;
1952     xsegs[1].x1 = 2, xsegs[1].y1 = dy - 1;
1953     xsegs[1].x2 = dx, xsegs[1].y2 = dy - 1;
1954     xsegs[2].x1 = dx, xsegs[2].y1 = 1;
1955     xsegs[2].x2 = dx, xsegs[2].y2 = dy;
1956     xsegs[3].x1 = dx - 1, xsegs[3].y1 = 2;
1957     xsegs[3].x2 = dx - 1, xsegs[3].y2 = dy;
1958     XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
1959 		  xsegs, 4);
1960     XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
1961 		   item->header.dt_ptr->dt_colors[c_item_fg]);
1962     item->header.dt_ptr->dt_Fstr->win = item->header.win;
1963     item->header.dt_ptr->dt_Fstr->gc  = item->header.dt_ptr->dt_item_GC;
1964     item->header.dt_ptr->dt_Fstr->flags.has_colorset = False;
1965     if (itemcolorset >= 0)
1966     {
1967       item->header.dt_ptr->dt_Fstr->colorset = &Colorset[itemcolorset];
1968       item->header.dt_ptr->dt_Fstr->flags.has_colorset = True;
1969     }
1970     item->header.dt_ptr->dt_Fstr->str = item->button.text;
1971     item->header.dt_ptr->dt_Fstr->x   = BOX_SPC + TEXT_SPC;
1972     item->header.dt_ptr->dt_Fstr->y   = BOX_SPC + TEXT_SPC
1973       + item->header.dt_ptr->dt_Ffont->ascent;
1974     item->header.dt_ptr->dt_Fstr->len = item->button.len;
1975     if (FftSupport)
1976     {
1977       if (item->header.dt_ptr->dt_Ffont->fftf.fftfont != NULL)
1978 	XClearArea(dpy, item->header.win,
1979 		   inter.x, inter.y, inter.width, inter.height, False);
1980     }
1981     FlocaleDrawString(dpy,
1982 		      item->header.dt_ptr->dt_Ffont,
1983 		      item->header.dt_ptr->dt_Fstr, FWS_HAVE_LENGTH);
1984     myfprintf((stderr,"Just put %s into a button\n",
1985 	       item->button.text));
1986     break;
1987   case I_SEPARATOR:
1988     RedrawSeparator(item);
1989     break;
1990   }
1991   if (dt_ptr && dt_ptr->dt_Fstr)
1992   {
1993 	  dt_ptr->dt_Fstr->flags.has_clip_region = False;
1994   }
1995   if (region)
1996   {
1997 	  XDestroyRegion(region);
1998   }
1999   XFlush(dpy);
2000 }
2001 
2002 /* update transparency if background colorset is transparent */
2003 /* window has moved redraw the background if it is transparent */
UpdateRootTransapency(Bool pr_only,Bool do_draw)2004 void UpdateRootTransapency(Bool pr_only, Bool do_draw)
2005 {
2006 	Item *item;
2007 
2008 	if (CSET_IS_TRANSPARENT(colorset))
2009 	{
2010 		if (CSET_IS_TRANSPARENT_PR_PURE(colorset))
2011 		{
2012 			XClearArea(dpy, CF.frame, 0,0,0,0, False);
2013 			if (do_draw)
2014 			{
2015 				RedrawFrame(NULL);
2016 			}
2017 		}
2018 		else if (!pr_only || CSET_IS_TRANSPARENT_PR(colorset))
2019 		{
2020 			SetWindowBackground(
2021 				dpy, CF.frame, CF.max_width, CF.total_height,
2022 				&Colorset[(colorset)], Pdepth,
2023 				root_item_ptr->header.dt_ptr->dt_GC, False);
2024 			if (do_draw)
2025 			{
2026 				XClearArea(dpy, CF.frame, 0,0,0,0, False);
2027 				RedrawFrame(NULL);
2028 			}
2029 		}
2030 	}
2031 	if (!root_item_ptr->header.next || !CSET_IS_TRANSPARENT(itemcolorset))
2032 	{
2033 		return;
2034 	}
2035 	for (item = root_item_ptr->header.next; item != NULL;
2036 	     item = item->header.next)
2037 	{
2038 		if (item->header.win != None)
2039 		{
2040 			if (CSET_IS_TRANSPARENT_PR(itemcolorset) &&
2041 			    !CSET_IS_TRANSPARENT(colorset))
2042 			{
2043 				continue;
2044 			}
2045 			if (CSET_IS_TRANSPARENT_PR_PURE(itemcolorset))
2046 			{
2047 				XClearArea(
2048 					dpy, item->header.win, 0,0,0,0, False);
2049 				if (do_draw)
2050 				{
2051 					RedrawItem(item, 0, NULL);
2052 				}
2053 			}
2054 			else if (!pr_only ||
2055 				 CSET_IS_TRANSPARENT_PR(itemcolorset))
2056 			{
2057 				SetWindowBackground(
2058 					dpy, item->header.win,
2059 					item->header.size_x, item->header.size_y,
2060 					&Colorset[(itemcolorset)], Pdepth,
2061 					item->header.dt_ptr->dt_GC, False);
2062 				XClearArea(
2063 					dpy, item->header.win, 0,0,0,0, False);
2064 				if (do_draw)
2065 				{
2066 					RedrawItem(item, 0, NULL);
2067 				}
2068 			}
2069 		}
2070 	}
2071 }
2072 
2073 /* execute a command */
DoCommand(Item * cmd)2074 void DoCommand (Item *cmd)
2075 {
2076   int k, dn;
2077   char *sp;
2078   Item *item;
2079 
2080   /* pre-command */
2081   if (cmd->button.key == IB_QUIT)
2082   {
2083     if (!XWithdrawWindow(dpy, CF.frame, screen))
2084     {
2085       /* hm, what can we do now? just ignore this situation. */
2086     }
2087   }
2088 
2089   for (k = 0; k < cmd->button.n; k++) {
2090     char *parsed_command;
2091     /* construct command */
2092     parsed_command = ParseCommand(0, cmd->button.commands[k], '\0', &dn, &sp);
2093     myfprintf((stderr, "Final command[%d]: [%s]\n", k, parsed_command));
2094 
2095     /* send command */
2096     if ( parsed_command[0] == '!') {	/* If command starts with ! */
2097       int n;
2098 
2099       n = system(parsed_command+1);		/* Need synchronous execution */
2100       (void)n;
2101     } else {
2102       SendText(Channel,parsed_command, ref);
2103     }
2104   }
2105 
2106   /* post-command */
2107   if (CF.last_error) {			/* if form has last_error field */
2108     memset(CF.last_error->text.value, ' ', CF.last_error->text.n); /* clear */
2109     /* To do this more elegantly, the window resize logic should recalculate
2110        size_x for the Message as the window resizes.  Right now, just clear
2111        a nice wide area. dje */
2112     XClearArea(dpy,CF.frame,
2113 	       CF.last_error->header.pos_x,
2114 	       CF.last_error->header.pos_y,
2115 	       /* CF.last_error->header.size_x, */
2116 	       2000,
2117 	       CF.last_error->header.size_y, False);
2118   } /* end form has last_error field */
2119   if (cmd->button.key == IB_QUIT) {
2120     if (CF.grab_server)
2121       XUngrabServer(dpy);
2122     /* This is a temporary bug workaround for the pipe drainage problem */
2123     SendQuitNotification(Channel);    /* let commands complete */
2124     /* Note how the window is withdrawn, but execution continues until
2125        the quit notifcation catches up with this module...
2126        Should not be a problem, there shouldn't be any more commands
2127        coming into FvwmForm.  dje */
2128   }
2129   else if (cmd->button.key == IB_RESTART) {
2130     Restart();
2131     for (item = root_item_ptr; item != 0;
2132 	 item = item->header.next) {	/* all items */
2133       if (item->type == I_INPUT) {
2134 	XClearWindow(dpy, item->header.win);
2135 	RedrawItem(item, 0, NULL);
2136       }
2137       if (item->type == I_CHOICE)
2138 	RedrawItem(item, 0, NULL);
2139     }
2140   }
2141 }
2142 
2143 /* open the windows */
OpenWindows(void)2144 static void OpenWindows(void)
2145 {
2146   int x, y;
2147   int gravity = NorthWestGravity;
2148   Item *item;
2149   static XSetWindowAttributes xswa;
2150   static XWMHints wmh = { InputHint, True };
2151   static XSizeHints sh =
2152     { PPosition | PSize | USPosition | USSize | PWinGravity};
2153   XClassHint myclasshints;
2154 
2155   if (!CF.pointer[input_pointer]) {
2156     CF.pointer[input_pointer] = XCreateFontCursor(dpy, XC_xterm);
2157   }
2158   if (!CF.pointer[button_in_pointer]) {
2159     CF.pointer[button_in_pointer] = XCreateFontCursor(dpy, XC_hand2);
2160   }
2161   if (!CF.pointer[button_pointer]) {
2162     CF.pointer[button_pointer] = XCreateFontCursor(dpy,XC_hand2);
2163   }
2164   CF.screen_background = (colorset < 0)
2165     ? GetColor(screen_background_color)
2166     : Colorset[colorset].bg;
2167 
2168   if (!CF.p_c[input_back].used) {  /* if not set, use screen b/g */
2169     CF.p_c[input_back].pointer_color.pixel = CF.screen_background;
2170   }
2171   myfprintf((stderr,
2172 	     "screen bg %X, getcolor bg %X, colorset bg %X colorset %d\n",
2173 	     (int)CF.screen_background,
2174 	     (int)GetColor(screen_background_color),
2175 	     (int)Colorset[colorset].bg,
2176 	     (int)colorset));
2177   XQueryColor(dpy, Pcmap, &CF.p_c[input_fore].pointer_color);
2178   XQueryColor(dpy, Pcmap, &CF.p_c[input_back].pointer_color);
2179   XRecolorCursor(dpy, CF.pointer[input_pointer],
2180 		 &CF.p_c[input_fore].pointer_color,
2181 		 &CF.p_c[input_back].pointer_color);
2182   myfprintf((stderr,"input fore %X, back %X\n",
2183 	  (int)CF.p_c[input_fore].pointer_color.pixel,
2184 	  (int)CF.p_c[input_back].pointer_color.pixel));
2185   /* The input cursor is handled differently than the 2 button cursors. */
2186   XQueryColor(dpy, Pcmap, &CF.p_c[button_fore].pointer_color);
2187   XQueryColor(dpy, Pcmap, &CF.p_c[button_back].pointer_color);
2188   XRecolorCursor(dpy, CF.pointer[button_pointer],
2189 		 &CF.p_c[button_fore].pointer_color,
2190 		 &CF.p_c[button_back].pointer_color);
2191   myfprintf((stderr,"button fore %X, back %X\n",
2192 	  (int)CF.p_c[button_fore].pointer_color.pixel,
2193 	  (int)CF.p_c[button_back].pointer_color.pixel));
2194   XQueryColor(dpy, Pcmap, &CF.p_c[button_in_fore].pointer_color);
2195   XQueryColor(dpy, Pcmap, &CF.p_c[button_in_back].pointer_color);
2196   XRecolorCursor(dpy, CF.pointer[button_in_pointer],
2197 		 &CF.p_c[button_in_fore].pointer_color,
2198 		 &CF.p_c[button_in_back].pointer_color);
2199   myfprintf((stderr,"button in fore %X, back %X\n",
2200 	  (int)CF.p_c[button_in_fore].pointer_color.pixel,
2201 	  (int)CF.p_c[button_in_back].pointer_color.pixel));
2202   /* the frame window first */
2203   if (CF.have_geom)
2204   {
2205     if (CF.xneg)
2206     {
2207       x = DisplayWidth(dpy, screen) - CF.max_width + CF.gx;
2208       gravity = NorthEastGravity;
2209     }
2210     else
2211     {
2212       x = CF.gx;
2213     }
2214     if (CF.yneg)
2215     {
2216       y = DisplayHeight(dpy, screen) - CF.total_height + CF.gy;
2217       gravity = SouthWestGravity;
2218     }
2219     else
2220     {
2221       y = CF.gy;
2222     }
2223     if (CF.xneg && CF.yneg)
2224     {
2225       gravity = SouthEastGravity;
2226     }
2227   } else {
2228     FScreenCenterOnScreen(
2229       NULL, FSCREEN_CURRENT, &x, &y, CF.max_width, CF.total_height);
2230   }
2231   /* hack to prevent mapping on wrong screen with StartsOnScreen */
2232   FScreenMangleScreenIntoUSPosHints(FSCREEN_CURRENT, &sh);
2233   xswa.background_pixel = CF.screen_background;
2234   xswa.border_pixel = 0;
2235   xswa.colormap = Pcmap;
2236   myfprintf((stderr,
2237 	  "going to create window w. bg %s, b/g pixel %X, black pixel %X\n",
2238 	     screen_background_color,
2239 	     (int)xswa.background_pixel,
2240 	     (int)BlackPixel(dpy, screen)));
2241   CF.frame = XCreateWindow(dpy, root, x, y, CF.max_width, CF.total_height, 0,
2242 			   Pdepth, InputOutput, Pvisual,
2243 			   CWColormap | CWBackPixel | CWBorderPixel, &xswa);
2244   wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
2245   XSetWMProtocols(dpy, CF.frame, &wm_del_win, 1);
2246   XSelectInput(dpy, CF.frame,
2247 	       KeyPressMask | ExposureMask | StructureNotifyMask |
2248 	       VisibilityChangeMask);
2249   if (!CF.title) {
2250     CF.title = module->name;
2251   }
2252   XStoreName(dpy, CF.frame, CF.title);
2253   XSetWMHints(dpy, CF.frame, &wmh);
2254   myclasshints.res_name = module->name;
2255   myclasshints.res_class = "FvwmForm";
2256   XSetClassHint(dpy,CF.frame,&myclasshints);
2257   sh.width = CF.max_width;
2258   sh.height = CF.total_height;
2259   sh.win_gravity = gravity;
2260   XSetWMNormalHints(dpy, CF.frame, &sh);
2261 
2262   for (item = root_item_ptr; item != 0;
2263        item = item->header.next) {	/* all items */
2264     switch (item->type) {
2265     case I_INPUT:
2266       myfprintf((stderr,"Checking alloc during OpenWindow on input\n"));
2267       CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
2268       item->header.win =
2269 	XCreateSimpleWindow(dpy, CF.frame,
2270 			    item->header.pos_x, item->header.pos_y,
2271 			    item->header.size_x, item->header.size_y,
2272 			    0, CF.screen_background,
2273 			    item->header.dt_ptr->dt_colors[c_item_bg]);
2274       XSelectInput(dpy, item->header.win, ButtonPressMask | ExposureMask);
2275       xswa.cursor = CF.pointer[input_pointer];
2276       XChangeWindowAttributes(dpy, item->header.win, CWCursor, &xswa);
2277       if (itemcolorset >= 0)
2278       {
2279 	SetWindowBackground(dpy, item->header.win,
2280 			    item->header.size_x, item->header.size_y,
2281 			    &Colorset[(itemcolorset)], Pdepth,
2282 			    item->header.dt_ptr->dt_GC, True);
2283       }
2284       break;
2285     case I_CHOICE:
2286       myfprintf((stderr,"Checking alloc during Openwindow on choice\n"));
2287       CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
2288       item->header.win =
2289 	XCreateSimpleWindow(dpy, CF.frame,
2290 			    item->header.pos_x, item->header.pos_y,
2291 			    item->header.size_y, item->header.size_y,
2292 			    0, CF.screen_background,
2293 			    item->header.dt_ptr->dt_colors[c_item_bg]);
2294       XSelectInput(dpy, item->header.win, ButtonPressMask | ExposureMask);
2295       xswa.cursor = CF.pointer[button_pointer];
2296       XChangeWindowAttributes(dpy, item->header.win, CWCursor, &xswa);
2297       if (itemcolorset >= 0)
2298       {
2299 	SetWindowBackground(dpy, item->header.win,
2300 			    item->header.size_x, item->header.size_y,
2301 			    &Colorset[(itemcolorset)], Pdepth,
2302 			    item->header.dt_ptr->dt_GC, True);
2303       }
2304       break;
2305     case I_BUTTON:
2306       myfprintf((stderr,"Checking alloc during Openwindow on button\n"));
2307       CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
2308       item->header.win =
2309 	XCreateSimpleWindow(dpy, CF.frame,
2310 			    item->header.pos_x, item->header.pos_y,
2311 			    item->header.size_x, item->header.size_y,
2312 			    0, CF.screen_background,
2313 			    item->header.dt_ptr->dt_colors[c_item_bg]);
2314       XSelectInput(dpy, item->header.win,
2315 		   ButtonPressMask | ExposureMask);
2316       xswa.cursor = CF.pointer[button_pointer];
2317       XChangeWindowAttributes(dpy, item->header.win, CWCursor, &xswa);
2318       if (itemcolorset >= 0)
2319       {
2320 	SetWindowBackground(dpy, item->header.win,
2321 			    item->header.size_x, item->header.size_y,
2322 			    &Colorset[(itemcolorset)], Pdepth,
2323 			    item->header.dt_ptr->dt_GC, True);
2324       }
2325       break;
2326     case I_SEPARATOR:
2327       myfprintf((stderr,"Checking alloc during OpenWindow on separator\n"));
2328       CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
2329       item->header.size_x = CF.max_width - 6;
2330       myfprintf((stderr,"Create win %d/%d %d/%d\n",3,item->header.pos_y,
2331 			    item->header.size_x, 2));
2332       item->header.win =
2333 	XCreateSimpleWindow(dpy, CF.frame,
2334 			    3, item->header.pos_y,
2335 			    item->header.size_x, 2,
2336 			    0, CF.screen_background,
2337 			    item->header.dt_ptr->dt_colors[c_bg]);
2338       XSelectInput(dpy, item->header.win, ExposureMask);
2339       if (itemcolorset >= 0)
2340       {
2341 	SetWindowBackground(dpy, item->header.win,
2342 			    item->header.size_x, 2,
2343 			    &Colorset[(itemcolorset)], Pdepth,
2344 			    item->header.dt_ptr->dt_GC, True);
2345       }
2346       break;
2347     }
2348   }
2349   Restart();
2350   if (colorset >= 0)
2351   {
2352     CheckAlloc(root_item_ptr,root_item_ptr->header.dt_ptr);
2353     SetWindowBackground(dpy, CF.frame, CF.max_width, CF.total_height,
2354 			&Colorset[(colorset)], Pdepth,
2355 			root_item_ptr->header.dt_ptr->dt_GC, True);
2356   }
2357   if (preload_yorn != 'y') {		/* if not a preload */
2358     XMapRaised(dpy, CF.frame);
2359     XMapSubwindows(dpy, CF.frame);
2360     if (CF.warp_pointer) {
2361       FWarpPointer(dpy, None, CF.frame, 0, 0, 0, 0,
2362 		   CF.max_width / 2, CF.total_height - 1);
2363     }
2364   }
2365   DoCommand(&CF.def_button);
2366 }
2367 
2368 static void process_message(unsigned long, unsigned long *); /* proto */
2369 static void ParseActiveMessage(char *); /* proto */
2370 
2371 /* read something from Fvwm */
ReadFvwm(void)2372 static void ReadFvwm(void)
2373 {
2374 
2375     FvwmPacket* packet = ReadFvwmPacket(Channel[1]);
2376     if ( packet == NULL )
2377 	exit(0);
2378     else
2379 	process_message( packet->type, packet->body );
2380 }
process_message(unsigned long type,unsigned long * body)2381 static void process_message(unsigned long type, unsigned long *body)
2382 {
2383   switch (type) {
2384   case M_CONFIG_INFO:			/* any module config command */
2385     myfprintf((stderr,"process_message: Got command: %s\n", (char *)&body[3]));
2386     ParseActiveMessage((char *)&body[3]);
2387     break;
2388   case MX_PROPERTY_CHANGE:
2389     if (body[0] == MX_PROPERTY_CHANGE_BACKGROUND &&
2390 	((!Swallowed && body[2] == 0) || (Swallowed && body[2] == CF.frame)))
2391     {
2392       UpdateRootTransapency(True, True);
2393     }
2394     else if  (body[0] == MX_PROPERTY_CHANGE_SWALLOW && body[2] == CF.frame)
2395     {
2396 	Swallowed = body[1];
2397     }
2398     break;
2399   case M_ERROR:
2400   case M_STRING:
2401     if (CF.last_error) {		/* if form has message area */
2402       /* ignore form size, its OK to write outside the window boundary */
2403       int msg_len;
2404       char *msg_ptr;
2405       /* Clear old message first */
2406       memset(CF.last_error->text.value, ' ', CF.last_error->text.n); /* clear */
2407       XClearArea(dpy,CF.frame,
2408 		 CF.last_error->header.pos_x,
2409 		 CF.last_error->header.pos_y,
2410 		 2000,
2411 		 CF.last_error->header.size_y, False);
2412       msg_ptr = (char *)&body[3];
2413       msg_len = strlen(msg_ptr);
2414       if (msg_ptr[msg_len-1] == '\n') { /* line ends w newline */
2415 	msg_ptr[msg_len-1] = '\0'; /* strip off \n */
2416       }
2417       if (CF.last_error->text.n <= msg_len) { /* if message wont fit */
2418 	CF.last_error->text.value = saferealloc(CF.last_error->text.value,
2419 						msg_len * 2);
2420 	CF.last_error->text.n = msg_len * 2;
2421       }
2422       strncpy(CF.last_error->text.value,msg_ptr,
2423 	      CF.last_error->text.n);
2424       RedrawText(CF.last_error);
2425       break;
2426     } /* module has last_error field */
2427   } /* end switch header */
2428 }
2429 
2430 /* These are the message from fvwm FvwmForm understands after form is
2431    active. */
2432 static void am_Map(char *);
2433 static void am_UnMap(char *);
2434 static void am_Stop(char *);
2435 static struct CommandTable am_table[] =
2436 {
2437   {"Map",am_Map},
2438   {"Stop",am_Stop},
2439   {"UnMap",am_UnMap}
2440 };
2441 
2442 /* This is similar to the other 2 "Parse" functions. */
ParseActiveMessage(char * buf)2443 static void ParseActiveMessage(char *buf)
2444 {
2445 	char *p;
2446 	struct CommandTable *e;
2447 	if (buf[strlen(buf)-1] == '\n')
2448 	{     /* if line ends with newline */
2449 		buf[strlen(buf)-1] = '\0';  /* strip off \n */
2450 	}
2451 
2452 	if (strncasecmp(buf, "Colorset", 8) == 0)
2453 	{
2454 		Item *item;
2455 		int n = LoadColorset(&buf[8]);
2456 		if(n == colorset || n == itemcolorset)
2457 		{
2458 			for (item = root_item_ptr; item != 0;
2459 			     item = item->header.next)
2460 			{
2461 				DrawTable *dt_ptr = item->header.dt_ptr;
2462 				if (dt_ptr)
2463 				{
2464 					dt_ptr->dt_used = 0;
2465 					if(dt_ptr->dt_GC)
2466 					{
2467 						XFreeGC(dpy, dt_ptr->dt_GC);
2468 						dt_ptr->dt_GC = None;
2469 					}
2470 					if(dt_ptr->dt_item_GC)
2471 					{
2472 						XFreeGC(
2473 							dpy,
2474 							dt_ptr->dt_item_GC);
2475 						dt_ptr->dt_item_GC = None;
2476 					}
2477 				}
2478 			}
2479 			for (item = root_item_ptr; item != 0;
2480 			     item = item->header.next)
2481 			{
2482 				DrawTable *dt_ptr = item->header.dt_ptr;
2483 				if (dt_ptr)
2484 				{
2485 					CheckAlloc(item, dt_ptr);
2486 				}
2487 			}
2488 			if (colorset >= 0)
2489 			{
2490 				SetWindowBackground(
2491 					dpy, CF.frame, CF.max_width,
2492 					CF.total_height,
2493 					&Colorset[(colorset)], Pdepth,
2494 					root_item_ptr->header.dt_ptr->dt_GC,
2495 					False);
2496 				RedrawFrame(NULL);
2497 			}
2498 			for (item = root_item_ptr->header.next; item != 0;
2499 			     item = item->header.next)
2500 			{
2501 				DrawTable *dt_ptr = item->header.dt_ptr;
2502 				if (dt_ptr && itemcolorset >= 0 &&
2503 				    item->header.win != 0)
2504 				{
2505 					SetWindowBackground(
2506 						dpy, item->header.win,
2507 						item->header.size_x,
2508 						item->header.size_y,
2509 						&Colorset[(itemcolorset)],
2510 						Pdepth, dt_ptr->dt_GC, True);
2511 					RedrawItem(item, 0, NULL);
2512 				} /* end item has a drawtable */
2513 			} /* end all items */
2514 		}
2515 		return;
2516 	} /* end colorset command */
2517 	if (strncasecmp(
2518 		buf, XINERAMA_CONFIG_STRING, sizeof(XINERAMA_CONFIG_STRING)-1)
2519 	    == 0)
2520 	{
2521 		FScreenConfigureModule(buf + sizeof(XINERAMA_CONFIG_STRING)-1);
2522 		return;
2523 	}
2524 	if (
2525 		strncasecmp(
2526 			buf, CatString3("*",module->name,0),
2527 			module->namelen+1) != 0)
2528 	{
2529 		/* If its not for me */
2530 		return;
2531 	} /* Now I know its for me. */
2532 	p = buf+module->namelen+1;		    /* jump to end of my name */
2533 	/* at this point we have recognized "*FvwmForm" */
2534 	e = FindToken(p,am_table,struct CommandTable);/* find cmd in table */
2535 	if (e == 0)
2536 	{
2537 		/* if no match */
2538 		/* this may be a configuration command of another same form */
2539 		if (FindToken(p, ct_table, struct CommandTable) == 0)
2540 			fprintf(
2541 				stderr,"%s: Active command unknown: %s\n",
2542 				module->name,buf);
2543 		return;				    /* ignore it */
2544 	}
2545 
2546 	p=p+strlen(e->name);			/* skip over name */
2547 	while (isspace((unsigned char)*p)) p++; /* skip whitespace */
2548 
2549 	FormVarsCheck(&p);
2550 	e->function(p);			      /* call cmd processor */
2551 	return;
2552 } /* end function */
2553 
am_Map(char * cp)2554 static void am_Map(char *cp)
2555 {
2556   XMapRaised(dpy, CF.frame);
2557   XMapSubwindows(dpy, CF.frame);
2558   if (CF.warp_pointer) {
2559     FWarpPointer(dpy, None, CF.frame, 0, 0, 0, 0,
2560 		 CF.max_width / 2, CF.total_height - 1);
2561   }
2562   myfprintf((stderr, "Map: got it\n"));
2563 }
am_UnMap(char * cp)2564 static void am_UnMap(char *cp)
2565 {
2566   XUnmapWindow(dpy, CF.frame);
2567   myfprintf((stderr, "UnMap: got it\n"));
2568 }
am_Stop(char * cp)2569 static void am_Stop(char *cp)
2570 {
2571   /* syntax: *FFStop */
2572   myfprintf((stderr,"Got stop command.\n"));
2573   exit (0);				/* why bother, just exit. */
2574 }
2575 
2576 /* main event loop */
MainLoop(void)2577 static void MainLoop(void)
2578 {
2579   fd_set fds;
2580   fd_set_size_t fd_width = GetFdWidth();
2581 
2582   while ( !isTerminated ) {
2583     /* TA:  20091219:  Automatically flush the buffer from the XServer and
2584      * process each request as we receive them.
2585      */
2586     while(FPending(dpy))
2587 	    ReadXServer();
2588 
2589     FD_ZERO(&fds);
2590     FD_SET(Channel[1], &fds);
2591     FD_SET(fd_x, &fds);
2592 
2593     /* TA:  20091219:  Using XFlush() here was always a nasty hack!  See
2594      * comments above.
2595      */
2596     /*XFlush(dpy);*/
2597     if (fvwmSelect(fd_width, &fds, NULL, NULL, NULL) > 0) {
2598       if (FD_ISSET(Channel[1], &fds))
2599 	ReadFvwm();
2600       if (FD_ISSET(fd_x, &fds))
2601 	ReadXServer();
2602     }
2603   }
2604 }
2605 
2606 
2607 /* signal-handler to make the application quit */
2608 static RETSIGTYPE
TerminateHandler(int sig)2609 TerminateHandler(int sig)
2610 {
2611   fvwmSetTerminate(sig);
2612   SIGNAL_RETURN;
2613 }
2614 
2615 /* signal-handler to make the timer work */
2616 static RETSIGTYPE
TimerHandler(int sig)2617 TimerHandler(int sig)
2618 {
2619   int dn;
2620   char *sp;
2621   char *parsed_command;
2622 
2623   timer->timeout.timeleft--;
2624   if (timer->timeout.timeleft <= 0) {
2625     /* pre-command */
2626     if (!XWithdrawWindow(dpy, CF.frame, screen))
2627     {
2628       /* hm, what can we do now? just ignore this situation. */
2629     }
2630 
2631     /* construct command */
2632     parsed_command = ParseCommand(0, timer->timeout.command, '\0', &dn, &sp);
2633     myfprintf((stderr, "Final command: %s\n", parsed_command));
2634 
2635     /* send command */
2636     if ( parsed_command[0] == '!') {	/* If command starts with ! */
2637       int n;
2638 
2639       n = system(parsed_command+1);		/* Need synchronous execution */
2640       (void)n;
2641     } else {
2642       SendText(Channel,parsed_command, ref);
2643     }
2644 
2645     /* post-command */
2646     if (CF.last_error) {		  /* if form has last_error field */
2647       memset(CF.last_error->text.value, ' ', CF.last_error->text.n); /* clear */
2648       /* To do this more elegantly, the window resize logic should recalculate
2649 	 size_x for the Message as the window resizes.	Right now, just clear
2650 	 a nice wide area. dje */
2651       XClearArea(dpy,CF.frame,
2652 		 CF.last_error->header.pos_x,
2653 		 CF.last_error->header.pos_y,
2654 		 /* CF.last_error->header.size_x, */
2655 		 2000,
2656 		 CF.last_error->header.size_y, False);
2657     } /* end form has last_error field */
2658     if (CF.grab_server)
2659       XUngrabServer(dpy);
2660     /* This is a temporary bug workaround for the pipe drainage problem */
2661     SendQuitNotification(Channel);    /* let commands complete */
2662     /* Note how the window is withdrawn, but execution continues until
2663        the quit notifcation catches up with this module...
2664        Should not be a problem, there shouldn't be any more commands
2665        coming into FvwmForm.  dje */
2666   }
2667   else {
2668     RedrawTimeout(timer);
2669     alarm(1);
2670   }
2671 
2672   SIGNAL_RETURN;
2673 }
2674 
2675 
2676 /* main procedure */
main(int argc,char ** argv)2677 int main (int argc, char **argv)
2678 {
2679   int i;
2680   char cmd[200];
2681 
2682 #ifdef DEBUGTOFILE
2683   freopen(".FvwmFormDebug","w",stderr);
2684 #endif
2685 
2686   FlocaleInit(LC_CTYPE, "", "", "FvwmForm");
2687 
2688   module = ParseModuleArgs(argc,argv,1); /* allow an alias */
2689   if (module == NULL)
2690   {
2691     fprintf(
2692 	    stderr,
2693 	    "FvwmForm Version "VERSION" should only be executed by fvwm!\n");
2694     exit(1);
2695   }
2696 
2697 #ifdef HAVE_SIGACTION
2698   {
2699     struct sigaction  sigact;
2700 
2701 #ifdef SA_INTERRUPT
2702     sigact.sa_flags = SA_INTERRUPT;
2703 #else
2704     sigact.sa_flags = 0;
2705 #endif
2706     sigemptyset(&sigact.sa_mask);
2707     sigaddset(&sigact.sa_mask, SIGTERM);
2708     sigaddset(&sigact.sa_mask, SIGPIPE);
2709     sigaddset(&sigact.sa_mask, SIGINT);
2710     sigact.sa_handler = TerminateHandler;
2711 
2712     sigaction(SIGPIPE, &sigact, NULL);
2713     sigaction(SIGTERM, &sigact, NULL);
2714     sigaction(SIGINT, &sigact, NULL);
2715   }
2716 #else
2717 #ifdef USE_BSD_SIGNALS
2718   fvwmSetSignalMask( sigmask(SIGPIPE) | sigmask(SIGTERM) | sigmask(SIGINT) );
2719 #endif
2720   signal(SIGPIPE, TerminateHandler);  /* Dead pipe == Fvwm died */
2721   signal(SIGTERM, TerminateHandler);
2722   signal(SIGINT, TerminateHandler);
2723 #ifdef HAVE_SIGINTERRUPT
2724   siginterrupt(SIGPIPE, 1);
2725   siginterrupt(SIGTERM, 1);
2726   siginterrupt(SIGINT, 1);
2727 #endif
2728 #endif
2729 
2730   Channel[0] = module->to_fvwm;
2731   Channel[1] = module->from_fvwm;
2732 
2733   dpy = XOpenDisplay("");
2734   if (dpy==NULL) {
2735     fprintf(stderr,"%s: could not open display\n",module->name);
2736     exit(1);
2737   }
2738   /* From FvwmAnimate end */
2739 
2740   i = 7;
2741   if (argc >= 8) {			/* if have arg 7 */
2742     if (strcasecmp(argv[7],"preload") == 0) { /* if its preload */
2743       preload_yorn = 'y';		/* remember that. */
2744       i = 8;
2745     }
2746   }
2747   for (;i<argc;i++) {			/* look at remaining args */
2748     if (strchr(argv[i],'=')) {		/* if its a candidate */
2749       putenv(argv[i]);			/* save it away */
2750       CF.have_env_var = 'y';		/* remember we have at least one */
2751     }
2752   }
2753   ref = strtol(argv[4], NULL, 16);	/* capture reference window */
2754   if (ref == 0) ref = None;
2755   myfprintf((stderr, "ref == %d\n", (int)ref));
2756 
2757   flib_init_graphics(dpy);
2758 
2759   fd_x = XConnectionNumber(dpy);
2760 
2761   screen = DefaultScreen(dpy);
2762   root = RootWindow(dpy, screen);
2763 
2764   InitConstants();
2765   ReadDefaults();			/* get config from fvwm */
2766 
2767   if (strcasecmp(module->name,"FvwmForm") != 0) { /* if not already read */
2768     sprintf(cmd,"read %s Quiet",module->name); /* read quiet modules config */
2769     SendText(Channel,cmd,0);
2770   }
2771 
2772   ReadConfig();				/* get config from fvwm */
2773 
2774   MassageConfig();			/* add data, calc window x/y */
2775 
2776   /* tell fvwm about our mask */
2777   SetMessageMask(Channel, M_SENDCONFIG|M_CONFIG_INFO|M_ERROR|M_STRING);
2778   SetMessageMask(Channel, MX_PROPERTY_CHANGE);
2779   XSetErrorHandler(ErrorHandler);
2780   OpenWindows();			/* create initial window */
2781   SendFinishedStartupNotification(Channel);/* tell fvwm we're running */
2782   if (timer != NULL) {
2783      SetupTimer();
2784   }
2785   MainLoop();				/* start */
2786 
2787   return 0;				/* */
2788 }
2789 
2790 
DeadPipe(int nonsense)2791 RETSIGTYPE DeadPipe(int nonsense)
2792 {
2793   exit(0);
2794   SIGNAL_RETURN;
2795 }
2796 
2797 /*
2798   X Error Handler
2799 */
2800 static int
ErrorHandler(Display * dpy,XErrorEvent * event)2801 ErrorHandler(Display *dpy, XErrorEvent *event)
2802 {
2803   /* some errors are OK=ish */
2804   if (event->error_code == BadPixmap)
2805     return 0;
2806   if (event->error_code == BadDrawable)
2807     return 0;
2808   if (FRenderGetErrorCodeBase() + FRenderBadPicture == event->error_code)
2809     return 0;
2810 
2811   PrintXErrorAndCoredump(dpy, event, module->name);
2812   return 0;
2813 }
2814 /*  Local Variables: */
2815 /*  c-basic-offset: 8 */
2816 /*  indent-tabs-mode: t */
2817 /*  End: */
2818