Cull unused functions when compiling against librplay.
[fvwm.git] / modules / FvwmButtons / FvwmButtons.c
blob1938a7c09e08a3f07f3ee61a74131c9852596711
1 /* -*-c-*- */
2 /*
3 * FvwmButtons, copyright 1996, Jarl Totland
5 * This module, and the entire GoodStuff program, and the concept for
6 * interfacing this module to the Window Manager, are all original work
7 * by Robert Nation
9 * Copyright 1993, Robert Nation. No guarantees or warantees or anything
10 * are provided or implied in any way whatsoever. Use this program at your
11 * own risk. Permission to use this program for any purpose is given,
12 * as long as the copyright is kept intact.
16 /* This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 /* ------------------------------- includes -------------------------------- */
32 #include "config.h"
33 #ifdef HAVE_SYS_BSDTYPES_H
34 #include <sys/bsdtypes.h> /* Saul */
35 #endif
37 #include <ctype.h>
38 #include <stdio.h>
39 #include <signal.h>
40 #include <fcntl.h>
41 #include <sys/wait.h>
42 #include "libs/ftime.h"
43 #include <sys/stat.h>
45 #include <X11/keysym.h>
46 #include <X11/Xlib.h>
47 #include <X11/Xutil.h>
48 #include <X11/Xproto.h>
49 #include <X11/Xatom.h>
50 #include <X11/Intrinsic.h>
52 #include "libs/defaults.h"
53 #include "libs/fvwmlib.h"
54 #include "libs/FScreen.h"
55 #include "libs/FShape.h"
56 #include "libs/FRenderInit.h"
57 #include "libs/Grab.h"
58 #include "libs/gravity.h"
59 #include "fvwm/fvwm.h"
60 #include "libs/Module.h"
61 #include "libs/fvwmsignal.h"
62 #include "libs/Colorset.h"
63 #include "libs/vpacket.h"
64 #include "libs/FRender.h"
65 #include "libs/fsm.h"
66 #include "libs/ColorUtils.h"
67 #include "libs/Graphics.h"
68 #include "libs/Parse.h"
69 #include "libs/Strings.h"
70 #include "libs/System.h"
71 #include "libs/wild.h"
72 #include "libs/WinMagic.h"
73 #include "libs/XError.h"
75 #include "FvwmButtons.h"
76 #include "misc.h" /* ConstrainSize() */
77 #include "parse.h" /* ParseConfiguration(), parse_window_geometry() */
78 #include "draw.h"
79 #include "dynamic.h"
82 #define MW_EVENTS ( \
83 ExposureMask | \
84 StructureNotifyMask | \
85 ButtonReleaseMask | \
86 ButtonPressMask | \
87 LeaveWindowMask | \
88 EnterWindowMask | \
89 PointerMotionMask | \
90 KeyReleaseMask | \
91 KeyPressMask | \
92 ButtonMotionMask | \
94 /* SW_EVENTS are for swallowed windows... */
95 #define SW_EVENTS ( \
96 PropertyChangeMask | \
97 StructureNotifyMask | \
98 ResizeRedirectMask | \
99 SubstructureNotifyMask | \
101 /* PA_EVENTS are for swallowed panels... */
102 #define PA_EVENTS ( \
103 StructureNotifyMask | \
106 extern int nColorsets; /* in libs/Colorsets.c */
108 /* --------------------------- external functions -------------------------- */
109 extern void DumpButtons(button_info *);
110 extern void SaveButtons(button_info *);
112 /* ------------------------------ prototypes ------------------------------- */
114 RETSIGTYPE DeadPipe(int nonsense);
115 static void DeadPipeCleanup(void);
116 static RETSIGTYPE TerminateHandler(int sig);
117 void SetButtonSize(button_info *, int, int);
118 /* main */
119 void Loop(void);
120 void RedrawWindow(void);
121 void RecursiveLoadData(button_info *, int *, int *);
122 void CreateUberButtonWindow(button_info *, int, int);
123 int My_FNextEvent(Display *dpy, XEvent *event);
124 void process_message(unsigned long type, unsigned long *body);
125 extern void send_clientmessage(Display *disp, Window w, Atom a, Time timestamp);
126 void parse_message_line(char *line);
127 void CheckForHangon(unsigned long *);
128 static Window GetRealGeometry(
129 Display *, Window, int *, int *, unsigned int *, unsigned int *,
130 unsigned int *, unsigned int *);
131 static void GetPanelGeometry(
132 Bool get_big, button_info *b, int lb, int tb, int rb, int bb,
133 int *x, int *y, int *w, int *h);
134 void swallow(unsigned long *);
135 void AddButtonAction(button_info *, int, char *);
136 char *GetButtonAction(button_info *, int);
137 static void update_root_transparency(XEvent *Event);
138 static void change_colorset(int colorset, XEvent *Event);
140 void DebugEvents(XEvent *);
142 static void HandlePanelPress(button_info *b);
144 /* -------------------------------- globals ---------------------------------*/
146 Display *Dpy;
147 Window Root;
148 Window MyWindow;
149 char *MyName;
150 int screen;
152 static int x_fd;
153 static fd_set_size_t fd_width;
155 char *config_file = NULL;
157 static Atom _XA_WM_DEL_WIN;
159 char *imagePath = NULL;
161 static Pixel hilite_pix, back_pix, shadow_pix, fore_pix;
162 GC NormalGC;
163 /* needed for relief drawing only */
164 GC ShadowGC;
165 /* needed for transparency */
166 GC transGC = NULL;
168 int Width, Height;
169 static int x = -30000, y = -30000;
170 int w = -1;
171 int h = -1;
172 static int gravity = NorthWestGravity;
173 int new_desk = 0;
174 static int ready = 0;
175 static int xneg = 0;
176 static int yneg = 0;
177 int button_width = 0;
178 int button_height = 0;
179 int button_lborder = 0;
180 int button_rborder = 0;
181 int button_tborder = 0;
182 int button_bborder = 0;
183 Bool has_button_geometry = 0;
184 Bool is_transient = 0;
185 Bool is_transient_panel = 0;
187 /* $CurrentButton is set on ButtonPress, $ActiveButton is set whenever the
188 mouse is over a button that is redrawn specially. */
189 button_info *CurrentButton = NULL, *ActiveButton = NULL;
190 Bool is_pointer_in_current_button = False;
191 int fd[2];
193 button_info *UberButton = NULL;
195 int dpw;
196 int dph;
198 Bool do_allow_bad_access = False;
199 Bool was_bad_access = False;
200 Bool swallowed = False;
201 Window swallower_win = 0;
203 /* ------------------------------ Misc functions ----------------------------*/
205 #ifdef DEBUG
206 char *mymalloc(int length)
208 int i = length;
209 char *p = safemalloc(length);
210 while (i)
212 p[--i] = 255;
214 return p;
216 #endif
219 *** Some fancy routines straight out of the manual :-) Used in DeadPipe.
221 Bool DestroyedWindow(Display *d, XEvent *e, char *a)
223 if (e->xany.window == (Window)a &&
224 ((e->type == DestroyNotify
225 && e->xdestroywindow.window == (Window)a) ||
226 (e->type == UnmapNotify && e->xunmap.window == (Window)a)))
228 return True;
230 return False;
233 static Window SwallowedWindow(button_info *b)
235 return (b->flags.b_Panel) ? b->PanelWin : b->IconWin;
238 int IsThereADestroyEvent(button_info *b)
240 XEvent event;
241 return FCheckIfEvent(
242 Dpy, &event, DestroyedWindow, (char *)SwallowedWindow(b));
246 *** DeadPipe()
247 *** Externally callable function to quit! Note that DeadPipeCleanup
248 *** is an exit-procedure and so will be called automatically
250 RETSIGTYPE DeadPipe(int whatever)
252 exit(0);
253 SIGNAL_RETURN;
257 *** TerminateHandler()
258 *** Signal handler that will make the event-loop terminate
260 static RETSIGTYPE
261 TerminateHandler(int sig)
263 fvwmSetTerminate(sig);
264 SIGNAL_RETURN;
268 *** DeadPipeCleanup()
269 *** Remove all the windows from the Button-Bar, and close them as necessary
271 static void DeadPipeCleanup(void)
273 button_info *b, *ub = UberButton;
274 int button = -1;
275 Window swin;
277 signal(SIGPIPE, SIG_IGN);/* Xsync may cause SIGPIPE */
279 MyXGrabServer(Dpy); /* We don't want interference right now */
280 while (NextButton(&ub, &b, &button, 0))
282 swin = SwallowedWindow(b);
283 /* delete swallowed windows */
284 if ((buttonSwallowCount(b) == 3) && swin)
286 #ifdef DEBUG_HANGON
287 fprintf(stderr,
288 "%s: Button 0x%06x window 0x%x (\"%s\") is ",
289 MyName, (ushort)b, (ushort)swin, b->hangon);
290 #endif
291 if (!IsThereADestroyEvent(b))
292 { /* Has someone destroyed it? */
293 if (!(buttonSwallow(b)&b_NoClose))
295 if (buttonSwallow(b)&b_Kill)
297 XKillClient(Dpy, swin);
298 #ifdef DEBUG_HANGON
299 fprintf(stderr, "now killed\n");
300 #endif
302 else
304 send_clientmessage(
305 Dpy, swin,
306 _XA_WM_DEL_WIN,
307 CurrentTime);
308 #ifdef DEBUG_HANGON
309 fprintf(stderr,
310 "now deleted\n");
311 #endif
314 else
316 #ifdef DEBUG_HANGON
317 fprintf(stderr, "now unswallowed\n");
318 #endif
319 if (b->swallow & b_FvwmModule)
321 char cmd[256];
323 sprintf(
324 cmd,
325 "PropertyChange %u %u %lu",
326 MX_PROPERTY_CHANGE_SWALLOW,
327 0, swin);
328 SendText(fd, cmd, 0);
330 fsm_proxy(Dpy, swin, NULL);
331 XReparentWindow(
332 Dpy, swin, Root, b->x, b->y);
333 XMoveWindow(Dpy, swin, b->x, b->y);
334 XResizeWindow(Dpy, swin, b->w, b->h);
335 XSetWindowBorderWidth(Dpy, swin, b->bw);
336 if (b->flags.b_Panel)
338 XMapWindow(Dpy, swin);
342 #ifdef DEBUG_HANGON
343 else
345 fprintf(stderr, "already handled\n");
347 #endif
350 XSync(Dpy, 0);
351 MyXUngrabServer(Dpy); /* We're through */
353 fsm_close();
354 /* Hey, we have to free the pictures too! */
355 button = -1;
356 ub = UberButton;
357 while (NextButton(&ub, &b, &button, 1))
359 /* The picture pointer is NULL if the pixmap came from a
360 * colorset. */
361 if (b->flags.b_Icon && b->icon != NULL)
363 PDestroyFvwmPicture(Dpy, b->icon);
365 if (b->flags.b_IconBack && b->backicon != NULL)
367 PDestroyFvwmPicture(Dpy, b->icon);
369 if (b->flags.b_Container && b->c->flags.b_IconBack &&
370 !(b->c->flags.b_TransBack) && b->c->backicon != NULL)
372 PDestroyFvwmPicture(Dpy, b->c->backicon);
378 *** SetButtonSize()
379 *** Propagates global geometry down through the button hierarchy.
381 void SetButtonSize(button_info *ub, int w, int h)
383 int i = 0;
384 int dx;
385 int dy;
387 if (!ub || !(ub->flags.b_Container))
389 fprintf(stderr,
390 "%s: BUG: Tried to set size of noncontainer\n", MyName);
391 exit(2);
393 if (ub->c->num_rows == 0 || ub->c->num_columns == 0)
395 fprintf(stderr,
396 "%s: BUG: Set size when rows/cols was unset\n", MyName);
397 exit(2);
400 if (ub->parent)
402 i = buttonNum(ub);
403 ub->c->xpos = buttonXPos(ub, i);
404 ub->c->ypos = buttonYPos(ub, i);
406 dx = buttonXPad(ub) + buttonFrame(ub);
407 dy = buttonYPad(ub) + buttonFrame(ub);
408 ub->c->xpos += dx;
409 ub->c->ypos += dy;
410 w -= 2 * dx;
411 h -= 2 * dy;
412 ub->c->width = w;
413 ub->c->height = h;
415 i = 0;
416 while (i < ub->c->num_buttons)
418 if (ub->c->buttons[i] && ub->c->buttons[i]->flags.b_Container)
420 SetButtonSize(
421 ub->c->buttons[i],
422 buttonWidth(ub->c->buttons[i]),
423 buttonHeight(ub->c->buttons[i]));
425 i++;
431 *** AddButtonAction()
433 void AddButtonAction(button_info *b, int n, char *action)
435 int l;
436 char *s;
437 char *t;
439 if (!b || n < 0 || n > NUMBER_OF_EXTENDED_MOUSE_BUTTONS || !action)
441 fprintf(stderr, "%s: BUG: AddButtonAction failed\n", MyName);
442 exit(2);
444 if (b->flags.b_Action)
446 if (b->action[n])
448 free(b->action[n]);
451 else
453 int i;
454 b->action = (char**)mymalloc(
455 (NUMBER_OF_EXTENDED_MOUSE_BUTTONS + 1) * sizeof(char*));
456 for (i = 0; i <= NUMBER_OF_EXTENDED_MOUSE_BUTTONS;
457 b->action[i++] = NULL)
460 b->flags.b_Action = 1;
463 while (*action && isspace((unsigned char)*action))
465 action++;
467 l = strlen(action);
468 if (l > 1)
470 switch (action[0])
472 case '\"':
473 case '\'':
474 case '`':
475 s = SkipQuote(action, NULL, "", "");
476 /* Strip outer quotes */
477 if (*s == 0)
479 action++;
480 l -= 2;
482 break;
483 default:
484 break;
487 t = (char *)mymalloc(l + 1);
488 memmove(t, action, l);
489 t[l] = 0;
490 b->action[n] = t;
495 *** GetButtonAction()
497 char *GetButtonAction(button_info *b, int n)
499 rectangle r;
500 char *act;
502 if (!b || !(b->flags.b_Action) || !(b->action) || n < 0 ||
503 n > NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
505 return NULL;
507 if (!b->action[n])
509 return NULL;
511 get_button_root_geometry(&r, b);
512 act = module_expand_action(
513 Dpy, screen, b->action[n], &r, UberButton->c->fore,
514 UberButton->c->back);
516 return act;
519 Pixmap shapeMask = None;
521 *** SetTransparentBackground()
522 *** use the Shape extension to create a transparent background.
523 *** Patrice Fortier & others.
525 void SetTransparentBackground(button_info *ub, int w, int h)
527 if (FShapesSupported)
529 button_info *b;
530 int i = -1;
531 XGCValues gcv;
533 if (shapeMask != None)
534 XFreePixmap(Dpy, shapeMask);
535 shapeMask = XCreatePixmap(Dpy, MyWindow, w, h, 1);
536 if (transGC == NULL)
538 transGC = fvwmlib_XCreateGC(Dpy, shapeMask, 0, &gcv);
540 XSetClipMask(Dpy, transGC, None);
541 XSetForeground(Dpy, transGC, 1);
542 XFillRectangle(Dpy, shapeMask, transGC, x, y, w, h);
544 while (NextButton(&ub, &b, &i, 0))
546 RedrawButton(b, DRAW_FORCE, NULL);
548 XFlush(Dpy);
553 *** myErrorHandler()
554 *** Shows X errors made by FvwmButtons.
556 XErrorHandler oldErrorHandler = NULL;
557 int myErrorHandler(Display *dpy, XErrorEvent *event)
559 /* some errors are acceptable, mostly they're caused by
560 * trying to update a lost window */
561 if ((event->error_code == BadAccess) && do_allow_bad_access)
563 was_bad_access = 1;
564 return 0;
566 if ((event->error_code == BadWindow)
567 || (event->error_code == BadDrawable)
568 || (event->error_code == BadMatch)
569 || (event->request_code == X_GrabButton)
570 || (event->request_code == X_GetGeometry)
571 || (event->error_code == BadPixmap)
572 || (event->error_code ==
573 FRenderGetErrorCodeBase() + FRenderBadPicture))
574 return 0;
576 PrintXErrorAndCoredump(dpy, event, MyName);
578 /* return (*oldErrorHandler)(dpy, event); */
579 return 0;
582 /* ---------------------------------- main ----------------------------------*/
585 *** main()
587 int main(int argc, char **argv)
589 int i;
590 Window root;
591 int x, y, maxx, maxy, border_width;
592 unsigned int depth;
593 button_info *b, *ub;
594 int geom_option_argc = 0;
595 XSetWindowAttributes xswa;
597 FlocaleInit(LC_CTYPE, "", "", "FvwmButtons");
599 MyName = GetFileNameFromPath(argv[0]);
601 #ifdef HAVE_SIGACTION
603 struct sigaction sigact;
605 sigemptyset(&sigact.sa_mask);
606 sigaddset(&sigact.sa_mask, SIGPIPE);
607 sigaddset(&sigact.sa_mask, SIGINT);
608 sigaddset(&sigact.sa_mask, SIGHUP);
609 sigaddset(&sigact.sa_mask, SIGQUIT);
610 sigaddset(&sigact.sa_mask, SIGTERM);
611 #ifdef SA_INTERRUPT
612 sigact.sa_flags = SA_INTERRUPT;
613 # else
614 sigact.sa_flags = 0;
615 #endif
616 sigact.sa_handler = TerminateHandler;
618 sigaction(SIGPIPE, &sigact, NULL);
619 sigaction(SIGINT, &sigact, NULL);
620 sigaction(SIGHUP, &sigact, NULL);
621 sigaction(SIGQUIT, &sigact, NULL);
622 sigaction(SIGTERM, &sigact, NULL);
624 #else
625 /* We don't have sigaction(), so fall back to less robust methods. */
626 #ifdef USE_BSD_SIGNALS
627 fvwmSetSignalMask(
628 sigmask(SIGPIPE) |
629 sigmask(SIGINT) |
630 sigmask(SIGHUP) |
631 sigmask(SIGQUIT) |
632 sigmask(SIGTERM));
633 #endif
635 signal(SIGPIPE, TerminateHandler);
636 signal(SIGINT, TerminateHandler);
637 signal(SIGHUP, TerminateHandler);
638 signal(SIGQUIT, TerminateHandler);
639 signal(SIGTERM, TerminateHandler);
640 #ifdef HAVE_SIGINTERRUPT
641 siginterrupt(SIGPIPE, 1);
642 siginterrupt(SIGINT, 1);
643 siginterrupt(SIGHUP, 1);
644 siginterrupt(SIGQUIT, 1);
645 siginterrupt(SIGTERM, 1);
646 #endif
647 #endif
649 if (argc<6 || argc>10)
651 fprintf(stderr,
652 "%s version %s should only be executed by fvwm!\n",
653 MyName, VERSION);
654 exit(1);
657 for (i = 6; i < argc && i < 11; i++)
659 static Bool has_name = 0;
660 static Bool has_file = 0;
661 static Bool has_geometry = 0;
663 if (!has_geometry && strcmp(argv[i], "-g") == 0)
665 has_geometry = 1;
666 if (++i < argc)
668 /* can't do that now because the UberButton */
669 /* has not been set up */
670 geom_option_argc = i;
673 else if (!is_transient && !is_transient_panel &&
674 (strcmp(argv[i], "-transient") == 0 ||
675 strcmp(argv[i], "transient") == 0))
677 is_transient = 1;
679 else if (!is_transient && !is_transient_panel &&
680 (strcmp(argv[i], "-transientpanel") == 0 ||
681 strcmp(argv[i], "transientpanel") == 0))
683 is_transient_panel = 1;
685 else if (!has_name)
686 /* There is a naming argument here! */
688 free(MyName);
689 MyName = safestrdup(argv[i]);
690 has_name = 1;
692 else if (!has_file)
693 /* There is a config file here! */
695 config_file = safestrdup(argv[i]);
696 has_file = 1;
700 fd[0] = atoi(argv[1]);
701 fd[1] = atoi(argv[2]);
702 if (!(Dpy = XOpenDisplay(NULL)))
704 fprintf(stderr,
705 "%s: Can't open display %s", MyName,
706 XDisplayName(NULL));
707 exit (1);
709 flib_init_graphics(Dpy);
711 x_fd = XConnectionNumber(Dpy);
712 fd_width = GetFdWidth();
714 screen = DefaultScreen(Dpy);
715 Root = RootWindow(Dpy, screen);
716 if (Root == None)
718 fprintf(stderr, "%s: Screen %d is not valid\n", MyName, screen);
719 exit(1);
722 oldErrorHandler = XSetErrorHandler(myErrorHandler);
724 UberButton = (button_info*)mymalloc(sizeof(button_info));
725 memset(UberButton, 0, sizeof(button_info));
726 UberButton->BWidth = 1;
727 UberButton->BHeight = 1;
728 MakeContainer(UberButton);
730 dpw = DisplayWidth(Dpy, screen);
731 dph = DisplayHeight(Dpy, screen);
733 #ifdef DEBUG_INIT
734 fprintf(stderr, "%s: Parsing...", MyName);
735 #endif
737 UberButton->title = MyName;
738 UberButton->swallow = 1; /* the panel is shown */
740 /* parse module options */
741 ParseConfiguration(UberButton);
743 /* To avoid an infinite loop of Enter/Leave-Notify events, if the user
744 uses ActiveIcon with "Pixmap none" they MUST specify ActiveColorset.
746 if (FShapesSupported && (UberButton->c->flags.b_TransBack) &&
747 !(UberButton->c->flags.b_ActiveColorset))
749 i = -1;
750 ub = UberButton;
751 while (NextButton(&ub, &b, &i, 0))
753 if (b->flags.b_ActiveIcon || b->flags.b_ActiveTitle)
755 fprintf(stderr,
756 "%s: Must specify ActiveColorset when "
757 "using ActiveIcon or ActiveTitle with "
758 "\"Pixmap none\".\n", MyName);
759 exit(0);
764 /* we can't set the size if it was specified in pixels per button here;
765 * delay until after call to RecursiveLoadData. */
766 /* parse the geometry string */
767 if (geom_option_argc != 0)
769 parse_window_geometry(argv[geom_option_argc], 0);
772 /* Don't quit if only a subpanel is empty */
773 if (UberButton->c->num_buttons == 0)
775 fprintf(stderr, "%s: No buttons defined. Quitting\n", MyName);
776 exit(0);
779 #ifdef DEBUG_INIT
780 fprintf(stderr, "OK\n%s: Shuffling...", MyName);
781 #endif
783 ShuffleButtons(UberButton);
784 NumberButtons(UberButton);
786 #ifdef DEBUG_INIT
787 fprintf(stderr, "OK\n%s: Creating Main Window ...", MyName);
788 #endif
790 xswa.colormap = Pcmap;
791 xswa.border_pixel = 0;
792 xswa.background_pixmap = None;
793 MyWindow = XCreateWindow(
794 Dpy, Root, 0, 0, 1, 1, 0, Pdepth, InputOutput, Pvisual,
795 CWColormap | CWBackPixmap | CWBorderPixel, &xswa);
797 #ifdef DEBUG_INIT
798 fprintf(stderr, "OK\n%s: Loading data...\n", MyName);
799 #endif
801 /* Load fonts and icons, calculate max buttonsize */
802 maxx = 0;
803 maxy = 0;
804 RecursiveLoadData(UberButton, &maxx, &maxy);
806 /* now we can size the main window if pixels per button were specified
808 if (has_button_geometry && button_width > 0 && button_height > 0)
810 w = button_width * UberButton->c->num_columns;
811 h = button_height * UberButton->c->num_rows;
814 #ifdef DEBUG_INIT
815 fprintf(stderr, "%s: Configuring main window...", MyName);
816 #endif
818 CreateUberButtonWindow(UberButton, maxx, maxy);
820 if (!XGetGeometry(
821 Dpy, MyWindow, &root, &x, &y, (unsigned int *)&Width,
822 (unsigned int *)&Height, (unsigned int *)&border_width, &depth))
824 fprintf(stderr, "%s: Failed to get window geometry\n", MyName);
825 exit(0);
827 SetButtonSize(UberButton, Width, Height);
828 i = -1;
829 ub = UberButton;
831 if (FShapesSupported)
833 if (UberButton->c->flags.b_TransBack)
835 SetTransparentBackground(UberButton, Width, Height);
839 i = -1;
840 ub = UberButton;
841 while (NextButton(&ub, &b, &i, 0))
843 MakeButton(b);
846 #ifdef DEBUG_INIT
847 fprintf(stderr, "OK\n%s: Mapping windows...", MyName);
848 #endif
850 XMapSubwindows(Dpy, MyWindow);
851 XMapWindow(Dpy, MyWindow);
853 SetMessageMask(
854 fd, M_NEW_DESK | M_END_WINDOWLIST | M_MAP | M_WINDOW_NAME
855 | M_RES_CLASS | M_CONFIG_INFO | M_END_CONFIG_INFO | M_RES_NAME
856 | M_SENDCONFIG | M_ADD_WINDOW | M_CONFIGURE_WINDOW | M_STRING);
857 SetMessageMask(fd, MX_PROPERTY_CHANGE);
859 /* request a window list, since this triggers a response which
860 * will tell us the current desktop and paging status, needed to
861 * indent buttons correctly */
862 SendText(fd, "Send_WindowList", 0);
864 #ifdef DEBUG_INIT
865 fprintf(stderr, "OK\n%s: Startup complete\n", MyName);
866 #endif
869 ** Now that we have finished initialising everything,
870 ** it is safe(r) to install the clean-up handlers ...
872 atexit(DeadPipeCleanup);
874 /* tell fvwm we're running */
875 SendFinishedStartupNotification(fd);
877 Loop();
879 return 0;
882 static button_info *handle_new_position(
883 button_info *b, int pos_x, int pos_y)
885 Bool f = is_pointer_in_current_button, redraw = False;
887 if (pos_x < 0 || pos_x >= Width ||
888 pos_y < 0 || pos_y >= Height)
890 /* cursor is outside of FvwmButtons window. */
891 return b;
894 /* find out which button the cursor is in now. */
895 b = select_button(UberButton, pos_x, pos_y);
897 is_pointer_in_current_button =
898 (CurrentButton && CurrentButton == b);
899 if (CurrentButton && is_pointer_in_current_button != f)
901 redraw = True;
904 if (b != ActiveButton && CurrentButton == NULL)
906 if (ActiveButton)
908 button_info *tmp = ActiveButton;
909 ActiveButton = b;
910 RedrawButton(tmp, DRAW_FORCE, NULL);
912 if (
913 b->flags.b_ActiveIcon ||
914 b->flags.b_ActiveTitle ||
915 b->flags.b_ActiveColorset ||
916 UberButton->c->flags.b_ActiveColorset)
918 ActiveButton = b;
919 redraw = True;
922 if (redraw)
924 RedrawButton(b, DRAW_FORCE, NULL);
927 return b;
930 void ButtonPressProcess (button_info *b, char **act)
932 FlocaleNameString tmp;
933 int i,i2;
935 memset(&tmp, 0, sizeof(tmp));
936 if (b && !*act && b->flags.b_Panel)
938 ActiveButton = b;
939 HandlePanelPress(b);
940 if (b->newflags.panel_mapped == 0)
942 if (is_transient)
944 /* terminate if transient and panel has been
945 * unmapped */
946 exit(0);
948 else if (is_transient_panel)
950 XWithdrawWindow(Dpy, MyWindow, screen);
953 } /* panel */
954 else
956 if (!*act)
957 *act=GetButtonAction(b,0);
958 if (b && b == CurrentButton && *act)
960 if (strncasecmp(*act,"Exec",4) == 0 &&
961 isspace((*act)[4]))
964 /* Look for Exec "identifier", in which case
965 * the button stays down until window
966 * "identifier" materializes */
967 i = 4;
968 while(isspace((unsigned char)(*act)[i]))
969 i++;
970 if((*act)[i] == '"')
972 i2=i+1;
973 while((*act)[i2]!=0 && (*act)[i2]!='"')
974 i2++;
976 if(i2-i>1)
978 b->flags.b_Hangon = 1;
979 b->hangon = mymalloc(i2-i);
980 strncpy(
981 b->hangon, &(*act)[i+1],
982 i2-i-1);
983 b->hangon[i2-i-1] = 0;
985 i2++;
987 else
988 i2=i;
990 tmp.name = mymalloc(strlen(*act)+1);
991 strcpy(tmp.name, "Exec ");
992 while (
993 (*act)[i2]!=0 &&
994 isspace((unsigned char)(*act)[i2]))
996 i2++;
998 strcat(tmp.name ,&(*act)[i2]);
999 if (is_transient)
1001 /* delete the window before continuing
1003 XDestroyWindow(Dpy, MyWindow);
1004 XSync(Dpy, 0);
1006 SendText(fd,tmp.name,0);
1007 if (is_transient)
1009 /* and exit */
1010 exit(0);
1012 if (is_transient_panel)
1014 XWithdrawWindow(Dpy, MyWindow, screen);
1016 free(tmp.name);
1017 } /* exec */
1018 else if(strncasecmp(*act,"DumpButtons",11)==0)
1019 DumpButtons(UberButton);
1020 else if(strncasecmp(*act,"SaveButtons",11)==0)
1021 SaveButtons(UberButton);
1022 else
1024 if (is_transient)
1026 /* delete the window before continuing
1028 XDestroyWindow(Dpy, MyWindow);
1029 XSync(Dpy, 0);
1031 SendText(fd,*act,0);
1032 if (is_transient)
1034 /* and exit */
1035 exit(0);
1037 if (is_transient_panel)
1039 XWithdrawWindow(Dpy, MyWindow, screen);
1042 } /* *act */
1043 } /* !panel */
1046 /* -------------------------------- Main Loop -------------------------------*/
1049 *** Loop
1051 void Loop(void)
1053 XEvent Event;
1054 KeySym keysym;
1055 char buffer[10], *act;
1056 int i, button;
1057 int x;
1058 int y;
1059 button_info *ub, *b;
1060 button_info *tmpb;
1061 FlocaleNameString tmp;
1063 tmp.name = NULL;
1064 tmp.name_list = NULL;
1065 while ( !isTerminated )
1067 if (My_FNextEvent(Dpy, &Event))
1069 if (FShapesSupported && Event.type == FShapeEventBase + FShapeNotify)
1071 FShapeEvent *sev = (FShapeEvent *) &Event;
1073 if (sev->kind != FShapeBounding)
1075 return;
1077 if (UberButton->c->flags.b_TransBack)
1079 SetTransparentBackground(
1080 UberButton, Width, Height);
1082 continue;
1084 switch (Event.type)
1086 case Expose:
1088 int ex, ey, ex2, ey2;
1090 if (Event.xexpose.window != MyWindow)
1092 break;
1094 ex = Event.xexpose.x;
1095 ey = Event.xexpose.y;
1096 ex2= Event.xexpose.x +
1097 Event.xexpose.width;
1098 ey2= Event.xexpose.y +
1099 Event.xexpose.height;
1100 while (
1101 FCheckTypedWindowEvent(
1102 Dpy, MyWindow, Expose,
1103 &Event))
1105 /* maybe we should not purge here,
1106 * this may interfere
1107 * with configure notify */
1108 ex = min(ex, Event.xexpose.x);
1109 ey = min(ey, Event.xexpose.y);
1110 ex2 = max(ex2, Event.xexpose.x
1111 + Event.xexpose.width);
1112 ey2 = max(ey2, Event.xexpose.y
1113 + Event.xexpose.height);
1115 Event.xexpose.x = ex;
1116 Event.xexpose.y = ey;
1117 Event.xexpose.width = ex2-ex;
1118 Event.xexpose.height = ey2-ey;
1119 button = -1;
1120 ub = UberButton;
1121 while (NextButton(&ub, &b, &button, 1))
1123 Bool r = False;
1125 tmpb = b;
1126 while (tmpb->parent != NULL)
1128 if (tmpb->flags.b_Container)
1130 int b =
1131 buttonNum(tmpb);
1132 x = buttonXPos(
1133 tmpb,
1135 y = buttonYPos(
1136 tmpb,
1139 else
1141 x = buttonXPos(
1142 tmpb,
1143 button);
1144 y = buttonYPos(
1145 tmpb,
1146 button);
1148 if (!(ex > x +
1149 buttonWidth(tmpb) ||
1150 ex2 < x ||
1151 ey > y +
1152 buttonHeight(tmpb) ||
1153 ey2 < y))
1155 r = True;
1157 tmpb = tmpb->parent;
1159 if (r)
1161 if (ready < 1 &&
1162 !(b->flags.b_Container))
1164 MakeButton(b);
1166 RedrawButton(
1167 b, DRAW_ALL, &Event);
1170 if (ready < 1)
1172 ready++;
1175 break;
1177 /* Start of TO-BE-REFORMATTED-BLOCK */
1179 case ConfigureNotify:
1181 XEvent event;
1182 unsigned int depth, tw, th, border_width;
1183 Window root;
1185 while (FCheckTypedWindowEvent(Dpy, MyWindow, ConfigureNotify, &event))
1187 if (!event.xconfigure.send_event &&
1188 Event.xconfigure.window != MyWindow)
1189 continue;
1190 Event.xconfigure.x = event.xconfigure.x;
1191 Event.xconfigure.y = event.xconfigure.y;
1192 Event.xconfigure.send_event = True;
1194 if (!XGetGeometry(Dpy, MyWindow, &root, &x, &y, &tw, &th,
1195 &border_width, &depth))
1197 break;
1199 if (tw != Width || th != Height)
1201 Width = tw;
1202 Height = th;
1203 SetButtonSize(UberButton, Width, Height);
1204 button = -1;
1205 ub = UberButton;
1206 /* what follow can be optimized */
1207 while (NextButton(&ub, &b, &button, 0))
1208 MakeButton(b);
1209 if (FShapesSupported && UberButton->c->flags.b_TransBack)
1211 SetTransparentBackground(UberButton, Width, Height);
1213 for (i = 0; i < nColorsets; i++)
1214 change_colorset(i, &Event);
1215 if (!UberButton->c->flags.b_Colorset)
1217 XClearWindow(Dpy, MyWindow);
1218 RedrawWindow();
1221 else if (Event.xconfigure.window == MyWindow &&
1222 Event.xconfigure.send_event)
1224 update_root_transparency(&Event);
1227 break;
1229 case EnterNotify:
1230 b = handle_new_position(
1231 b, Event.xcrossing.x, Event.xcrossing.y);
1232 break;
1234 case MotionNotify:
1235 b = handle_new_position(b, Event.xmotion.x, Event.xmotion.y);
1236 break;
1238 case LeaveNotify:
1239 if (
1240 Event.xcrossing.x < 0 || Event.xcrossing.x >= Width ||
1241 Event.xcrossing.y < 0 || Event.xcrossing.y >= Height ||
1243 /* We get LeaveNotify events when the mouse enters
1244 * a swallowed window of FvwmButtons, but we're not
1245 * interested in these situations. */
1246 !select_button(UberButton, x, y)->flags.b_Swallow ||
1248 /* But we're interested in those situations when
1249 * the mouse enters a window which overlaps with
1250 * the swallowed window. */
1251 Event.xcrossing.subwindow != None)
1253 if (ActiveButton)
1255 b = ActiveButton;
1256 ActiveButton = NULL;
1257 RedrawButton(b, DRAW_FORCE, NULL);
1259 if (CurrentButton)
1261 RedrawButton(b, DRAW_FORCE, NULL);
1264 break;
1266 case KeyPress:
1267 XLookupString(&Event.xkey, buffer, 10, &keysym, 0);
1268 if (
1269 keysym != XK_Return && keysym != XK_KP_Enter &&
1270 keysym != XK_Linefeed)
1271 break; /* fall through to ButtonPress */
1273 case ButtonPress:
1274 if (Event.xbutton.window == MyWindow)
1276 x = Event.xbutton.x;
1277 y = Event.xbutton.y;
1279 else
1281 Window dummy;
1283 XTranslateCoordinates(
1284 Dpy, Event.xbutton.window, MyWindow, Event.xbutton.x,
1285 Event.xbutton.y, &x, &y, &dummy);
1287 if (CurrentButton)
1289 b = CurrentButton;
1290 CurrentButton = NULL;
1291 ActiveButton = select_button(UberButton, x, y);
1292 RedrawButton(b, DRAW_FORCE, NULL);
1293 if (ActiveButton != b)
1295 RedrawButton(ActiveButton, DRAW_FORCE, NULL);
1296 b = ActiveButton;
1298 break;
1300 if (Event.xbutton.state & DEFAULT_ALL_BUTTONS_MASK)
1302 break;
1305 CurrentButton = b = select_button(UberButton, x, y);
1306 is_pointer_in_current_button = True;
1308 act = NULL;
1309 if (!b->flags.b_Panel &&
1310 (!b || !b->flags.b_Action ||
1311 ((act = GetButtonAction(b, Event.xbutton.button)) == NULL &&
1312 (act = GetButtonAction(b, 0)) == NULL)))
1314 CurrentButton = NULL;
1315 break;
1318 /* Undraw ActiveButton (if there is one). */
1319 if (ActiveButton)
1321 /* $b & $ActiveButton are always the same button. */
1322 button_info *tmp = ActiveButton;
1323 ActiveButton = NULL;
1324 RedrawButton(tmp, DRAW_FORCE, NULL);
1326 else
1327 RedrawButton(b, DRAW_FORCE, NULL);
1328 if (!act)
1330 break;
1332 if (act && !b->flags.b_ActionOnPress &&
1333 strncasecmp(act, "popup", 5) != 0)
1335 free(act);
1336 act = NULL;
1337 break;
1339 else /* i.e. action is Popup */
1341 XUngrabPointer(Dpy, CurrentTime); /* And fall through */
1343 if (act)
1345 free(act);
1346 act = NULL;
1348 /* fall through */
1350 case KeyRelease:
1351 case ButtonRelease:
1352 if (CurrentButton == NULL || !is_pointer_in_current_button)
1354 if (CurrentButton)
1356 button_info *tmp = CurrentButton;
1357 CurrentButton = NULL;
1358 RedrawButton(tmp, DRAW_FORCE, NULL);
1361 CurrentButton = NULL;
1362 ActiveButton = b;
1363 if (ActiveButton)
1364 RedrawButton(ActiveButton, DRAW_FORCE, NULL);
1365 break;
1367 if (Event.xbutton.window == MyWindow)
1369 x = Event.xbutton.x;
1370 y = Event.xbutton.y;
1372 else
1374 Window dummy;
1376 XTranslateCoordinates(
1377 Dpy, Event.xbutton.window, MyWindow, Event.xbutton.x,
1378 Event.xbutton.y, &x, &y, &dummy);
1380 b = select_button(UberButton, x, y);
1381 act = GetButtonAction(b,Event.xbutton.button);
1383 ButtonPressProcess(b, &act);
1385 if (act != NULL)
1387 free(act);
1388 act = NULL;
1390 b = CurrentButton;
1391 CurrentButton = NULL;
1392 ActiveButton = b;
1393 if (b && !b->flags.b_Hangon)
1394 RedrawButton(b, DRAW_FORCE, NULL);
1395 break;
1397 case ClientMessage:
1398 if (Event.xclient.format == 32 &&
1399 Event.xclient.data.l[0]==_XA_WM_DEL_WIN)
1401 DeadPipe(1);
1403 break;
1405 case PropertyNotify:
1406 if (Event.xany.window == None)
1407 break;
1408 ub = UberButton;button = -1;
1409 while (NextButton(&ub, &b, &button, 0))
1411 Window swin = SwallowedWindow(b);
1413 if ((buttonSwallowCount(b) == 3) && Event.xany.window == swin)
1415 if (Event.xproperty.atom == XA_WM_NAME &&
1416 buttonSwallow(b)&b_UseTitle)
1418 if (b->flags.b_Title)
1419 free(b->title);
1420 b->flags.b_Title = 1;
1421 FlocaleGetNameProperty(XGetWMName, Dpy, swin, &tmp);
1422 if (tmp.name != NULL)
1424 CopyString(&b->title, tmp.name);
1425 FlocaleFreeNameProperty(&tmp);
1427 else
1429 CopyString(&b->title, "");
1431 MakeButton(b);
1433 else if ((Event.xproperty.atom == XA_WM_NORMAL_HINTS) &&
1434 (!(buttonSwallow(b)&b_NoHints)))
1436 long supp;
1437 if (!XGetWMNormalHints(Dpy, swin, b->hints, &supp))
1438 b->hints->flags = 0;
1439 MakeButton(b);
1440 if (FShapesSupported)
1442 if (UberButton->c->flags.b_TransBack)
1444 SetTransparentBackground(UberButton, Width, Height);
1448 RedrawButton(b, DRAW_FORCE, NULL);
1451 break;
1453 case MapNotify:
1454 case UnmapNotify:
1456 ub = UberButton;
1457 button = -1;
1458 while (NextButton(&ub, &b, &button, 0))
1460 if (b->flags.b_Panel && Event.xany.window == b->PanelWin)
1462 /* A panel has been unmapped, update the button */
1463 b->newflags.panel_mapped = (Event.type == MapNotify);
1464 RedrawButton(b, DRAW_FORCE, NULL);
1465 break;
1468 break;
1470 case DestroyNotify:
1471 ub = UberButton;button = -1;
1472 while (NextButton(&ub, &b, &button, 0))
1474 Window swin = SwallowedWindow(b);
1476 if ((buttonSwallowCount(b) == 3) && Event.xany.window == swin)
1478 #ifdef DEBUG_HANGON
1479 fprintf(stderr,
1480 "%s: Button 0x%06x lost its window 0x%x (\"%s\")",
1481 MyName, (ushort)b, (ushort)swin, b->hangon);
1482 #endif
1483 b->swallow &= ~b_Count;
1484 if (b->flags.b_Panel)
1485 b->PanelWin = None;
1486 else
1487 b->IconWin = None;
1488 if (buttonSwallow(b)&b_Respawn && b->hangon && b->spawn)
1490 char *p;
1492 #ifdef DEBUG_HANGON
1493 fprintf(stderr, ", respawning\n");
1494 #endif
1495 if (b->newflags.is_panel && is_transient)
1497 /* terminate if transient and a panel has been killed */
1498 exit(0);
1500 b->swallow |= 1;
1501 if (!b->newflags.is_panel)
1503 b->flags.b_Swallow = 1;
1504 b->flags.b_Hangon = 1;
1506 else
1508 b->flags.b_Panel = 1;
1509 b->flags.b_Hangon = 1;
1510 b->newflags.panel_mapped = 0;
1512 p = module_expand_action(
1513 Dpy, screen, b->spawn, NULL, UberButton->c->fore,
1514 UberButton->c->back);
1515 if (p)
1517 exec_swallow(p, b);
1518 free(p);
1520 RedrawButton(b, DRAW_CLEAN, NULL); /* ? */
1521 if (is_transient_panel)
1523 XWithdrawWindow(Dpy, MyWindow, screen);
1526 else if (b->newflags.do_swallow_new && b->hangon && b->spawn &&
1527 !b->newflags.is_panel)
1529 #ifdef DEBUG_HANGON
1530 fprintf(stderr, ", waiting for respawned window\n");
1531 #endif
1532 b->swallow |= 1;
1533 b->flags.b_Swallow = 1;
1534 b->flags.b_Hangon = 1;
1535 RedrawButton(b, DRAW_CLEAN, NULL);
1536 if (is_transient_panel)
1538 XWithdrawWindow(Dpy, MyWindow, screen);
1541 else
1543 b->flags.b_Swallow = 0;
1544 b->flags.b_Panel = 0;
1545 RedrawButton(b, DRAW_FORCE, NULL);
1546 #ifdef DEBUG_HANGON
1547 fprintf(stderr, "\n");
1548 #endif
1550 break;
1553 break;
1555 /* End of TO-BE-REFORMATTED-BLOCK */
1557 default:
1558 #ifdef DEBUG_EVENTS
1559 fprintf(stderr,
1560 "%s: Event fell through unhandled\n",
1561 MyName);
1562 #endif
1563 break;
1564 } /* switch */
1565 } /* event handling */
1566 } /* while */
1571 *** RedrawWindow()
1572 *** Draws the window by traversing the button tree
1574 void RedrawWindow(void)
1576 int button;
1577 XEvent dummy;
1578 button_info *ub, *b;
1579 static Bool initial_redraw = True;
1580 Bool clear_buttons;
1582 if (ready < 1)
1584 return;
1587 /* Flush expose events */
1588 while (FCheckTypedWindowEvent(Dpy, MyWindow, Expose, &dummy))
1592 /* Clean out the entire window first */
1593 if (initial_redraw == False)
1595 XClearWindow(Dpy, MyWindow);
1596 clear_buttons = False;
1598 else
1600 initial_redraw = False;
1601 clear_buttons = True;
1604 button = -1;
1605 ub = UberButton;
1606 while (NextButton(&ub, &b, &button, 1))
1608 RedrawButton(b, DRAW_ALL, NULL);
1613 *** LoadIconFile()
1615 int LoadIconFile(const char *s, FvwmPicture **p, int cset)
1617 FvwmPictureAttributes fpa;
1619 fpa.mask = 0;
1620 if (cset >= 0 && Colorset[cset].do_dither_icon)
1622 fpa.mask |= FPAM_DITHER;
1624 if (UberButton->c->flags.b_TransBack)
1626 fpa.mask |= FPAM_NO_ALPHA;
1628 *p = PCacheFvwmPicture(Dpy, MyWindow, imagePath, s, fpa);
1629 if (*p)
1631 return 1;
1633 return 0;
1637 *** RecursiveLoadData()
1638 *** Loads colors, fonts and icons, and calculates buttonsizes
1640 void RecursiveLoadData(button_info *b, int *maxx, int *maxy)
1642 int i, x = 0, y = 0, ix, iy, tx, ty, hix, hiy, htx, hty, pix, piy, ptx, pty;
1643 FlocaleFont *Ffont;
1645 if (!b)
1646 return;
1648 #ifdef DEBUG_LOADDATA
1649 fprintf(stderr, "%s: Loading: Button 0x%06x: colors", MyName, (ushort)b);
1650 #endif
1653 /* initialise button colours and background */
1654 if (b->flags.b_Colorset)
1656 int cset = b->colorset;
1658 /* override normal icon options */
1659 if (b->flags.b_IconBack && !b->flags.b_TransBack)
1661 free(b->back);
1663 b->flags.b_IconBack = 0;
1664 b->flags.b_TransBack = 0;
1666 /* fetch the colours from the colorset */
1667 b->fc = Colorset[cset].fg;
1668 b->bc = Colorset[cset].bg;
1669 b->hc = Colorset[cset].hilite;
1670 b->sc = Colorset[cset].shadow;
1671 if (Colorset[cset].pixmap != None)
1673 /* we have a pixmap */
1674 b->backicon = NULL;
1675 b->flags.b_IconBack = 1;
1678 else /* no colorset */
1680 b->colorset = -1;
1681 if (b->flags.b_Fore)
1682 b->fc = GetColor(b->fore);
1683 if (b->flags.b_Back)
1685 b->bc = GetColor(b->back);
1686 b->hc = GetHilite(b->bc);
1687 b->sc = GetShadow(b->bc);
1688 if (b->flags.b_IconBack)
1690 if (!LoadIconFile(b->back, &b->backicon, b->colorset))
1691 b->flags.b_Back = 0;
1693 } /* b_Back */
1694 } /* !b_Colorset */
1696 /* initialise container colours and background */
1697 if (b->flags.b_Container)
1699 if (b->c->flags.b_Colorset)
1701 int cset = b->c->colorset;
1703 /* override normal icon options */
1704 if (b->c->flags.b_IconBack && !b->c->flags.b_TransBack)
1706 free(b->c->back_file);
1708 b->c->flags.b_IconBack = 0;
1709 b->c->flags.b_TransBack = 0;
1711 /* fetch the colours from the colorset */
1712 b->c->fc = Colorset[cset].fg;
1713 b->c->bc = Colorset[cset].bg;
1714 b->c->hc = Colorset[cset].hilite;
1715 b->c->sc = Colorset[cset].shadow;
1716 if (Colorset[cset].pixmap != None)
1718 /* we have a pixmap */
1719 b->c->backicon = NULL;
1720 b->c->flags.b_IconBack = 1;
1723 else /* no colorset */
1725 b->c->colorset = -1;
1726 #ifdef DEBUG_LOADDATA
1727 fprintf(stderr, ", colors2");
1728 #endif
1729 if (b->c->flags.b_Fore)
1730 b->c->fc = GetColor(b->c->fore);
1731 if (b->c->flags.b_Back)
1733 b->c->bc = GetColor(b->c->back);
1734 b->c->hc = GetHilite(b->c->bc);
1735 b->c->sc = GetShadow(b->c->bc);
1736 if (b->c->flags.b_IconBack && !b->c->flags.b_TransBack)
1738 if (!LoadIconFile(b->c->back_file, &b->c->backicon, -1))
1739 b->c->flags.b_IconBack = 0;
1742 } /* !b_Colorset */
1743 } /* b_Container */
1747 /* Load the font */
1748 if (b->flags.b_Font)
1750 #ifdef DEBUG_LOADDATA
1751 fprintf(stderr, ", font \"%s\"", b->font_string);
1752 #endif
1754 if (strncasecmp(b->font_string, "none", 4) == 0)
1756 b->Ffont = NULL;
1758 else if (!(b->Ffont = FlocaleLoadFont(Dpy, b->font_string, MyName)))
1760 b->flags.b_Font = 0;
1764 if (b->flags.b_Container && b->c->flags.b_Font)
1766 #ifdef DEBUG_LOADDATA
1767 fprintf(stderr, ", font2 \"%s\"", b->c->font_string);
1768 #endif
1769 if (strncasecmp(b->c->font_string, "none", 4) == 0)
1771 b->c->Ffont = NULL;
1773 else if (!(b->c->Ffont = FlocaleLoadFont(Dpy, b->c->font_string, MyName)))
1775 b->c->flags.b_Font = 0;
1779 /* Calculate subbutton sizes */
1780 if (b->flags.b_Container && b->c->num_buttons)
1782 #ifdef DEBUG_LOADDATA
1783 fprintf(stderr, ", entering container\n");
1784 #endif
1785 for (i = 0; i < b->c->num_buttons; i++)
1786 if (b->c->buttons[i])
1787 RecursiveLoadData(b->c->buttons[i], &x, &y);
1789 if (b->c->flags.b_Size)
1791 x = b->c->minx;
1792 y = b->c->miny;
1794 #ifdef DEBUG_LOADDATA
1795 fprintf(stderr, "%s: Loading: Back to container 0x%06x", MyName, (ushort)b);
1796 #endif
1798 x *= b->c->num_columns;
1799 y *= b->c->num_rows;
1800 b->c->width = x;
1801 b->c->height = y;
1804 /* $ix & $iy are dimensions of Icon
1805 $tx & $ty are dimensions of Title
1806 $hix & $hiy are dimensions of ActiveIcon
1807 $htx & $hty are dimensions of ActiveTitle
1808 $pix & $piy are dimensions of PressIcon
1809 $ptx & $pty are dimensions of PressTitle
1811 Note that if No ActiveIcon is specified, Icon is displayed during hover.
1812 Similarly for ActiveTitle, PressIcon & PressTitle. */
1813 ix = iy = tx = ty = hix = hiy = htx = hty = pix = piy = ptx = pty = 0;
1815 /* Load the icon */
1816 if (
1817 b->flags.b_Icon &&
1818 LoadIconFile(b->icon_file, &b->icon, buttonColorset(b)))
1820 #ifdef DEBUG_LOADDATA
1821 fprintf(stderr, ", icon \"%s\"", b->icon_file);
1822 #endif
1823 ix = b->icon->width;
1824 iy = b->icon->height;
1826 else
1827 b->flags.b_Icon = 0;
1829 /* load the active icon. */
1830 if (b->flags.b_ActiveIcon &&
1831 LoadIconFile(b->active_icon_file, &b->activeicon, buttonColorset(b)))
1833 #ifdef DEBUG_LOADDATA
1834 fprintf(stderr, ", active icon \"%s\"", b->active_icon_file);
1835 #endif
1837 hix = b->activeicon->width;
1838 hiy = b->activeicon->height;
1840 else
1842 hix = ix;
1843 hiy = iy;
1844 b->flags.b_ActiveIcon = 0;
1847 /* load the press icon. */
1848 if (b->flags.b_PressIcon &&
1849 LoadIconFile(b->press_icon_file, &b->pressicon, buttonColorset(b)))
1851 #ifdef DEBUG_LOADDATA
1852 fprintf(stderr, ", press icon \"%s\"", b->press_icon_file);
1853 #endif
1855 pix = b->pressicon->width;
1856 piy = b->pressicon->height;
1858 else
1860 pix = ix;
1861 piy = iy;
1862 b->flags.b_PressIcon = 0;
1865 /* calculate Title dimensions. */
1866 if (b->flags.b_Title && (Ffont = buttonFont(b)))
1868 #ifdef DEBUG_LOADDATA
1869 fprintf(stderr, ", title \"%s\"", b->title);
1870 #endif
1871 if (buttonJustify(b)&b_Horizontal)
1873 tx = buttonXPad(b) + FlocaleTextWidth(Ffont, b->title, strlen(b->title));
1874 ty = Ffont->height;
1876 else
1878 tx = FlocaleTextWidth(Ffont, b->title, strlen(b->title));
1879 ty = Ffont->height;
1883 /* calculate ActiveTitle dimensions. */
1884 if (b->flags.b_ActiveTitle && (Ffont = buttonFont(b)))
1886 #ifdef DEBUG_LOADDATA
1887 fprintf(stderr, ", title \"%s\"", b->title);
1888 #endif
1889 if (buttonJustify(b) & b_Horizontal)
1891 htx = buttonXPad(b) + FlocaleTextWidth(Ffont, b->activeTitle,
1892 strlen(b->activeTitle));
1893 hty = Ffont->height;
1895 else
1897 htx = FlocaleTextWidth(Ffont, b->activeTitle, strlen(b->activeTitle));
1898 hty = Ffont->height;
1901 else
1903 htx = tx;
1904 hty = ty;
1907 /* calculate PressTitle dimensions. */
1908 if (b->flags.b_PressTitle && (Ffont = buttonFont(b)))
1910 #ifdef DEBUG_LOADDATA
1911 fprintf(stderr, ", title \"%s\"", b->title);
1912 #endif
1913 if (buttonJustify(b) & b_Horizontal)
1915 ptx = buttonXPad(b) + FlocaleTextWidth(Ffont, b->pressTitle,
1916 strlen(b->pressTitle));
1917 pty = Ffont->height;
1919 else
1921 ptx = FlocaleTextWidth(Ffont, b->pressTitle, strlen(b->pressTitle));
1922 pty = Ffont->height;
1925 else
1927 ptx = tx;
1928 pty = ty;
1931 x += max(max(ix, tx), max(hix, htx));
1932 y += max(iy + ty, hiy + hty);
1934 if (b->flags.b_Size)
1936 x = b->minx;
1937 y = b->miny;
1940 x += 2 * (buttonFrame(b) + buttonXPad(b));
1941 y += 2 * (buttonFrame(b) + buttonYPad(b));
1943 x /= b->BWidth;
1944 y /= b->BHeight;
1946 *maxx = max(x, *maxx);
1947 *maxy = max(y, *maxy);
1948 #ifdef DEBUG_LOADDATA
1949 fprintf(stderr, ", size %ux%u, done\n", x, y);
1950 #endif
1954 static void HandlePanelPress(button_info *b)
1956 int x1, y1, w1, h1;
1957 int x2, y2, w2, h2;
1958 int px, py, pw, ph, pbw;
1959 int ax, ay, aw, ah, abw;
1960 int tb, bb, lb, rb;
1961 int steps = b->slide_steps;
1962 int i;
1963 unsigned int d;
1964 Window JunkW;
1965 Window ancestor;
1966 XSizeHints mysizehints;
1967 long supplied;
1968 Bool is_mapped;
1969 XWindowAttributes xwa;
1970 char cmd[64];
1971 button_info *tmp;
1973 if (XGetWindowAttributes(Dpy, b->PanelWin, &xwa))
1974 is_mapped = (xwa.map_state == IsViewable);
1975 else
1976 is_mapped = False;
1978 /* force StaticGravity on window */
1979 mysizehints.flags = 0;
1980 XGetWMNormalHints(Dpy, b->PanelWin, &mysizehints, &supplied);
1981 mysizehints.flags |= PWinGravity;
1982 mysizehints.win_gravity = StaticGravity;
1983 XSetWMNormalHints(Dpy, b->PanelWin, &mysizehints);
1984 if (is_mapped &&
1985 XGetGeometry(Dpy, b->PanelWin, &JunkW, &b->x, &b->y, &b->w, &b->h,
1986 &b->bw, &d))
1988 /* get the current geometry */
1989 XTranslateCoordinates(
1990 Dpy, b->PanelWin, Root, b->x, b->y, &b->x, &b->y, &JunkW);
1992 else
1994 XWMHints wmhints;
1996 /* Make sure the icon is unmapped first. Needed to work properly with
1997 * shaded and iconified windows. */
1998 XWithdrawWindow(Dpy, b->PanelWin, screen);
2000 /* now request mapping in the void and wait until it gets mapped */
2001 mysizehints.flags = 0;
2002 XGetWMNormalHints(Dpy, b->PanelWin, &mysizehints, &supplied);
2003 mysizehints.flags |= USPosition;
2004 /* hack to prevent mapping panels on wrong screen with StartsOnScreen */
2005 FScreenMangleScreenIntoUSPosHints(FSCREEN_XYPOS, &mysizehints);
2006 XSetWMNormalHints(Dpy, b->PanelWin, &mysizehints);
2007 /* make sure its not mapped as an icon */
2008 wmhints.flags = StateHint;
2009 wmhints.initial_state = NormalState;
2010 XSetWMHints(Dpy, b->PanelWin, &wmhints);
2012 /* map the window in the void */
2013 XMoveWindow(Dpy, b->PanelWin, 32767, 32767);
2014 XMapWindow(Dpy, b->PanelWin);
2015 XSync(Dpy, 0);
2017 /* give the X server the CPU to do something */
2018 usleep(1000);
2020 /* wait until it appears or one second has passed */
2021 for (i = 0; i < 10; i++)
2023 if (!XGetWindowAttributes(Dpy, b->PanelWin, &xwa))
2025 /* the window has been destroyed */
2026 XUnmapWindow(Dpy, b->PanelWin);
2027 XSync(Dpy, 0);
2028 RedrawButton(b, DRAW_CLEAN, NULL);
2029 return;
2031 if (xwa.map_state == IsViewable)
2033 /* okay, it's mapped */
2034 break;
2036 /* still unmapped wait 0.1 seconds and try again */
2037 XSync(Dpy, 0);
2038 usleep(100000);
2040 if (xwa.map_state != IsViewable)
2042 /* give up after one second */
2043 XUnmapWindow(Dpy, b->PanelWin);
2044 XSync(Dpy, 0);
2045 RedrawButton(b, DRAW_FORCE, NULL);
2046 return;
2050 /* We're sure that the window is mapped and decorated now. Find the top level
2051 * ancestor now. */
2052 MyXGrabServer(Dpy);
2053 lb = 0;
2054 tb = 0;
2055 rb = 0;
2056 bb = 0;
2057 if (!b->panel_flags.ignore_lrborder || !b->panel_flags.ignore_tbborder)
2059 ancestor = GetTopAncestorWindow(Dpy, b->PanelWin);
2060 if (ancestor)
2062 if (
2063 XGetGeometry(Dpy, ancestor, &JunkW, &ax, &ay, (unsigned int *)&aw,
2064 (unsigned int *)&ah, (unsigned int *)&abw, &d) &&
2065 XGetGeometry(Dpy, b->PanelWin, &JunkW, &px, &py, (unsigned int *)&pw,
2066 (unsigned int *)&ph, (unsigned int *)&pbw, &d) &&
2067 XTranslateCoordinates(
2068 Dpy, b->PanelWin, ancestor, 0, 0, &px, &py, &JunkW))
2070 /* calculate the 'border' width in all four directions */
2071 if (!b->panel_flags.ignore_lrborder)
2073 lb = max(px, 0) + abw;
2074 rb = max((aw + abw) - (px + pw), 0) + abw;
2076 if (!b->panel_flags.ignore_tbborder)
2078 tb = max(py, 0) + abw;
2079 bb = max((ah + abw) - (py + ph), 0) + abw;
2084 /* now find the source and destination positions for the slide operation */
2085 GetPanelGeometry(is_mapped, b, lb, tb, rb, bb, &x1, &y1, &w1, &h1);
2086 GetPanelGeometry(!is_mapped, b, lb, tb, rb, bb, &x2, &y2, &w2, &h2);
2087 MyXUngrabServer(Dpy);
2089 /* to force fvwm to map the window where we want */
2090 if (is_mapped)
2092 /* don't slide the window if it has been moved or resized */
2093 if (b->x != x1 || b->y != y1 || b->w != w1 || b->h != h1)
2095 steps = 0;
2099 /* redraw our button */
2100 b->newflags.panel_mapped = !is_mapped;
2102 /* make sure the window maps on the current desk */
2103 sprintf(cmd, "Silent WindowId 0x%08x (!Sticky) MoveToDesk 0",
2104 (int)b->PanelWin);
2105 SendInfo(fd, cmd, b->PanelWin);
2106 SlideWindow(Dpy, b->PanelWin,
2107 x1, y1, w1, h1,
2108 x2, y2, w2, h2,
2109 steps, b->slide_delay_ms, NULL, b->panel_flags.smooth,
2110 (b->swallow&b_NoHints) ? 0 : 1);
2111 if (is_mapped)
2113 XUnmapWindow(Dpy, b->PanelWin);
2115 tmp = CurrentButton;
2116 CurrentButton = NULL;
2117 RedrawButton(b, DRAW_FORCE, NULL);
2118 CurrentButton = tmp;
2119 XSync(Dpy, 0);
2120 /* Give fvwm a chance to update the window. Otherwise the window may end up
2121 * too small. This doesn't prevent this completely, but makes it much less
2122 * likely. */
2123 usleep(250000);
2125 return;
2130 *** CreateUberButtonWindow()
2131 *** Sizes and creates the window
2133 void CreateUberButtonWindow(button_info *ub, int maxx, int maxy)
2135 XSizeHints mysizehints;
2136 XGCValues gcv;
2137 unsigned long gcm;
2138 XClassHint myclasshints;
2139 int wx;
2140 int wy;
2142 x = UberButton->x; /* Geometry x where to put the panel */
2143 y = UberButton->y; /* Geometry y where to put the panel */
2144 xneg = UberButton->w;
2145 yneg = UberButton->h;
2147 if (maxx < 16)
2149 maxx = 16;
2151 if (maxy < 16)
2153 maxy = 16;
2156 #ifdef DEBUG_INIT
2157 fprintf(stderr, "making atoms...");
2158 #endif
2160 _XA_WM_DEL_WIN = XInternAtom(Dpy, "WM_DELETE_WINDOW", 0);
2162 #ifdef DEBUG_INIT
2163 fprintf(stderr, "sizing...");
2164 #endif
2166 mysizehints.flags = PWinGravity | PBaseSize;
2167 mysizehints.base_width = 0;
2168 mysizehints.base_height = 0;
2169 mysizehints.width = mysizehints.base_width + maxx;
2170 mysizehints.height = mysizehints.base_height + maxy;
2172 if (w>-1) /* from geometry */
2174 #ifdef DEBUG_INIT
2175 fprintf(stderr, "constraining (w=%i)...", w);
2176 #endif
2177 ConstrainSize(&mysizehints, &w, &h);
2178 mysizehints.width = w;
2179 mysizehints.height = h;
2180 mysizehints.flags |= USSize;
2183 #ifdef DEBUG_INIT
2184 fprintf(stderr, "gravity...");
2185 #endif
2186 wx = 0;
2187 wy = 0;
2188 if (x > -100000)
2190 if (xneg)
2192 wx = DisplayWidth(Dpy, screen) + x - mysizehints.width;
2193 gravity = NorthEastGravity;
2195 else
2197 wx = x;
2199 if (yneg)
2201 wy = DisplayHeight(Dpy, screen) + y - mysizehints.height;
2202 gravity = SouthWestGravity;
2204 else
2206 wy = y;
2208 if (xneg && yneg)
2210 gravity = SouthEastGravity;
2212 mysizehints.flags |= USPosition;
2214 /* hack to prevent mapping panels on wrong screen with
2215 * StartsOnScreen */
2216 FScreenMangleScreenIntoUSPosHints(FSCREEN_XYPOS, &mysizehints);
2218 mysizehints.win_gravity = gravity;
2220 #ifdef DEBUG_INIT
2221 if (mysizehints.flags&USPosition)
2223 fprintf(stderr,
2224 "create(%i,%i,%u,%u,1,%u,%u)...",
2225 wx, wy, mysizehints.width, mysizehints.height,
2226 (ushort)fore_pix, (ushort)back_pix);
2228 else
2230 fprintf(stderr,
2231 "create(-,-,%u,%u,1,%u,%u)...",
2232 mysizehints.width, mysizehints.height,
2233 (ushort)fore_pix, (ushort)back_pix);
2235 #endif
2237 XMoveResizeWindow(
2238 Dpy, MyWindow, wx, wy, mysizehints.width, mysizehints.height);
2240 XSetWMNormalHints(Dpy, MyWindow, &mysizehints);
2242 if (is_transient)
2244 XSetTransientForHint(Dpy, MyWindow, Root);
2247 #ifdef DEBUG_INIT
2248 fprintf(stderr, "colors...");
2249 #endif
2250 if (Pdepth < 2)
2252 back_pix = GetColor("white");
2253 fore_pix = GetColor("black");
2254 hilite_pix = back_pix;
2255 shadow_pix = fore_pix;
2257 else
2259 fore_pix = ub->c->fc;
2260 back_pix = ub->c->bc;
2261 hilite_pix = ub->c->hc;
2262 shadow_pix = ub->c->sc;
2266 #ifdef DEBUG_INIT
2267 fprintf(stderr, "properties...");
2268 #endif
2269 XSetWMProtocols(Dpy, MyWindow, &_XA_WM_DEL_WIN, 1);
2271 myclasshints.res_name = safestrdup(MyName);
2272 myclasshints.res_class = safestrdup("FvwmButtons");
2275 XTextProperty mynametext;
2276 char *list[] = { NULL, NULL };
2277 list[0] = MyName;
2278 if (!XStringListToTextProperty(list, 1, &mynametext))
2280 fprintf(stderr,
2281 "%s: Failed to convert name to XText\n",
2282 MyName);
2283 exit(1);
2285 XSetWMProperties(
2286 Dpy, MyWindow, &mynametext, &mynametext, NULL, 0,
2287 &mysizehints, NULL, &myclasshints);
2288 XFree(mynametext.value);
2291 XSelectInput(Dpy, MyWindow, MW_EVENTS);
2293 #ifdef DEBUG_INIT
2294 fprintf(stderr, "GC...");
2295 #endif
2296 gcm = GCForeground|GCBackground;
2297 gcv.foreground = fore_pix;
2298 gcv.background = back_pix;
2299 if (ub && ub->c && ub->c->Ffont && ub->c->Ffont->font && ub->Ffont)
2301 gcv.font = ub->c->Ffont->font->fid;
2302 gcm |= GCFont;
2304 NormalGC = fvwmlib_XCreateGC(Dpy, MyWindow, gcm, &gcv);
2305 gcv.foreground = shadow_pix;
2306 gcv.background = fore_pix;
2307 ShadowGC = fvwmlib_XCreateGC(Dpy, MyWindow, gcm, &gcv);
2309 if (ub->c->flags.b_Colorset)
2311 SetWindowBackground(
2312 Dpy, MyWindow, mysizehints.width, mysizehints.height,
2313 &Colorset[ub->c->colorset], Pdepth, NormalGC, True);
2315 else if (ub->c->flags.b_IconBack && !ub->c->flags.b_TransBack)
2317 XSetWindowBackgroundPixmap(
2318 Dpy, MyWindow, ub->c->backicon->picture);
2320 else
2322 XSetWindowBackground(Dpy, MyWindow, back_pix);
2325 free(myclasshints.res_class);
2326 free(myclasshints.res_name);
2329 /* --------------------------------------------------------------------------*/
2331 #ifdef DEBUG_EVENTS
2332 void DebugEvents(XEvent *event)
2334 char *event_names[] =
2336 NULL,
2337 NULL,
2338 "KeyPress",
2339 "KeyRelease",
2340 "ButtonPress",
2341 "ButtonRelease",
2342 "MotionNotify",
2343 "EnterNotify",
2344 "LeaveNotify",
2345 "FocusIn",
2346 "FocusOut",
2347 "KeymapNotify",
2348 "Expose",
2349 "GraphicsExpose",
2350 "NoExpose",
2351 "VisibilityNotify",
2352 "CreateNotify",
2353 "DestroyNotify",
2354 "UnmapNotify",
2355 "MapNotify",
2356 "MapRequest",
2357 "ReparentNotify",
2358 "ConfigureNotify",
2359 "ConfigureRequest",
2360 "GravityNotify",
2361 "ResizeRequest",
2362 "CirculateNotify",
2363 "CirculateRequest",
2364 "PropertyNotify",
2365 "SelectionClear",
2366 "SelectionRequest",
2367 "SelectionNotify",
2368 "ColormapNotify",
2369 "ClientMessage",
2370 "MappingNotify"
2372 fprintf(stderr, "%s: Received %s event from window 0x%x\n",
2373 MyName, event_names[event->type], (ushort)event->xany.window);
2375 #endif
2378 *** My_FNextEvent()
2379 *** Waits for next X event, or for an auto-raise timeout.
2381 int My_FNextEvent(Display *Dpy, XEvent *event)
2383 fd_set in_fdset;
2384 static int miss_counter = 0;
2385 static Bool fsm_pending = False;
2386 struct timeval tv;
2388 if (FPending(Dpy))
2390 FNextEvent(Dpy, event);
2391 #ifdef DEBUG_EVENTS
2392 DebugEvents(event);
2393 #endif
2394 return 1;
2397 FD_ZERO(&in_fdset);
2398 FD_SET(x_fd, &in_fdset);
2399 FD_SET(fd[1], &in_fdset);
2400 fsm_fdset(&in_fdset);
2402 if (fsm_pending)
2404 tv.tv_sec = 0;
2405 tv.tv_usec = 10000; /* 10 ms */
2408 if (fvwmSelect(
2409 fd_width, SELECT_FD_SET_CAST &in_fdset, 0, 0,
2410 (fsm_pending)? &tv:NULL) > 0)
2412 if (FD_ISSET(x_fd, &in_fdset))
2414 if (FPending(Dpy))
2416 FNextEvent(Dpy, event);
2417 miss_counter = 0;
2418 #ifdef DEBUG_EVENTS
2419 DebugEvents(event);
2420 #endif
2421 return 1;
2423 else
2425 miss_counter++;
2427 if (miss_counter > 100)
2429 DeadPipe(0);
2433 if (FD_ISSET(fd[1], &in_fdset))
2435 FvwmPacket* packet = ReadFvwmPacket(fd[1]);
2436 if ( packet == NULL )
2438 DeadPipe(0);
2440 else
2442 process_message( packet->type, packet->body );
2446 fsm_pending = fsm_process(&in_fdset);
2449 else
2451 if (fsm_pending)
2453 fsm_pending = fsm_process(&in_fdset);
2456 return 0;
2460 *** SpawnSome()
2461 *** Is run after the windowlist is checked. If any button hangs on UseOld,
2462 *** it has failed, so we try to spawn a window for them.
2464 void SpawnSome(void)
2466 static char first = 1;
2467 button_info *b, *ub = UberButton;
2468 int button = -1;
2469 char *p;
2471 if (!first)
2473 return;
2475 first = 0;
2476 while (NextButton(&ub, &b, &button, 0))
2478 if ((buttonSwallowCount(b) == 1) && b->flags.b_Hangon &&
2479 (buttonSwallow(b)&b_UseOld))
2481 if (b->spawn)
2483 #ifdef DEBUG_HANGON
2484 fprintf(stderr,
2485 "%s: Button 0x%06x did not find a "
2486 "\"%s\" window, %s",
2487 MyName, (ushort)b, b->hangon,
2488 "spawning own\n");
2489 #endif
2490 p = module_expand_action(
2491 Dpy, screen, b->spawn, NULL,
2492 UberButton->c->fore,
2493 UberButton->c->back);
2494 if (p)
2496 exec_swallow(p, b);
2497 free(p);
2504 /* This function is a real hack. It forces our nice background upon the
2505 * windows of the swallowed application! */
2506 void change_swallowed_window_colorset(button_info *b, Bool do_clear)
2508 Window w = SwallowedWindow(b);
2509 Window *children = None;
2510 int nchildren;
2512 nchildren = GetEqualSizeChildren(
2513 Dpy, w, Pdepth, XVisualIDFromVisual(Pvisual), Pcmap, &children);
2514 SetWindowBackground(
2515 Dpy, w, buttonWidth(b), buttonHeight(b),
2516 &Colorset[b->colorset], Pdepth, NormalGC, do_clear);
2517 while (nchildren-- > 0)
2519 SetWindowBackground(
2520 Dpy, children[nchildren], buttonWidth(b),
2521 buttonHeight(b), &Colorset[b->colorset], Pdepth,
2522 NormalGC, do_clear);
2524 if (children)
2526 XFree(children);
2529 return;
2532 static void send_bg_change_to_module(button_info *b, XEvent *Event)
2534 int bx, by, bpx, bpy, bf;
2536 if (Event != NULL)
2538 buttonInfo(b, &bx, &by, &bpx, &bpy, &bf);
2539 Event->xconfigure.x = UberButton->x + bx;
2540 Event->xconfigure.y = UberButton->y + by;
2541 Event->xconfigure.width = b->icon_w;
2542 Event->xconfigure.height = b->icon_h;
2543 Event->xconfigure.window = SwallowedWindow(b);
2544 FSendEvent(Dpy, SwallowedWindow(b), False, NoEventMask, Event);
2546 else
2548 char cmd[256];
2549 sprintf(cmd, "PropertyChange %u %u %lu",
2550 MX_PROPERTY_CHANGE_BACKGROUND, 0, SwallowedWindow(b));
2551 SendText(fd, cmd, 0);
2555 static void update_root_transparency(XEvent *Event)
2557 button_info *ub, *b;
2558 int button;
2559 Bool bg_is_transparent = CSET_IS_TRANSPARENT(UberButton->c->colorset);
2561 if (bg_is_transparent)
2563 SetWindowBackground(
2564 Dpy, MyWindow, UberButton->c->width,
2565 UberButton->c->height,
2566 &Colorset[UberButton->c->colorset],
2567 Pdepth, NormalGC, True);
2568 /* the window is redraw by exposure. It is very difficult
2569 * the clear only the needed part and then redraw the
2570 * cleared buttons (pbs with containers which contain containers
2571 * ...) */
2574 if (!bg_is_transparent)
2576 /* update root transparent button (and its childs) */
2577 ub = UberButton;
2578 button = -1;
2579 while (NextButton(&ub, &b, &button, 1))
2581 button_info *tmpb;
2582 Bool redraw = False;
2584 tmpb = b;
2585 /* see if _a_ parent (!= UberButton) is root trans */
2586 while (tmpb->parent != NULL && !redraw)
2588 if (CSET_IS_TRANSPARENT_ROOT(
2589 buttonColorset(tmpb)))
2591 redraw = True;
2593 tmpb = tmpb->parent;
2595 if (redraw)
2597 RedrawButton(b, DRAW_ALL, NULL);
2603 /* Handle the swallowed window */
2604 ub = UberButton;
2605 button = -1;
2606 while (NextButton(&ub, &b, &button, 0))
2608 if (buttonSwallowCount(b) == 3 && b->flags.b_Swallow)
2610 /* should handle 2 cases: module or not */
2611 if (b->swallow & b_FvwmModule)
2613 if (bg_is_transparent)
2615 XSync(Dpy, 0);
2616 send_bg_change_to_module(b, Event);
2619 else if (b->flags.b_Colorset &&
2620 CSET_IS_TRANSPARENT(b->colorset))
2622 change_swallowed_window_colorset(b, True);
2629 static void recursive_change_colorset(
2630 container_info *c, int colorset, XEvent *Event)
2632 int button;
2633 button_info *ub, *b;
2634 button_info *tmpb;
2636 button = -1;
2637 ub = UberButton;
2638 while (NextButton(&ub, &b, &button, 1))
2640 Bool redraw = False;
2642 tmpb = b;
2643 /* see if _a_ parent (!= UberButton) has colorset as cset */
2644 while (tmpb->parent != NULL && !redraw)
2646 if (buttonColorset(tmpb) == colorset)
2648 redraw = True;
2650 tmpb = tmpb->parent;
2652 if (!redraw)
2654 continue;
2656 if (b->flags.b_Swallow)
2658 /* swallowed window */
2659 if (buttonSwallowCount(b) == 3 && b->IconWin != None)
2661 RedrawButton(b, DRAW_ALL, NULL);
2662 if (b->flags.b_Colorset &&
2663 !(b->swallow&b_FvwmModule))
2665 change_swallowed_window_colorset(
2666 b, True);
2670 else
2672 RedrawButton(b, DRAW_ALL, NULL);
2676 return;
2679 static void change_colorset(int colorset, XEvent *Event)
2681 if (UberButton->c->flags.b_Colorset &&
2682 colorset == UberButton->c->colorset)
2684 button_info *ub, *b;
2685 int button;
2687 SetWindowBackground(
2688 Dpy, MyWindow, UberButton->c->width,
2689 UberButton->c->height,
2690 &Colorset[colorset], Pdepth, NormalGC, True);
2692 /* some change are done by exposing. Do the other one */
2693 button = -1;
2694 ub = UberButton;
2695 while (NextButton(&ub, &b, &button, 0))
2697 if (b->flags.b_Swallow && buttonSwallowCount(b) == 3)
2699 if (b->swallow&b_FvwmModule)
2701 /* the bg has changed send the info to
2702 * modules */
2703 XSync(Dpy, 0);
2704 send_bg_change_to_module(b, Event);
2706 else if (CSET_IS_TRANSPARENT_PR(b->colorset) &&
2707 !buttonBackgroundButton(b->parent, NULL))
2709 /* the swallowed app has a ParentRel bg
2710 * and the bg of the button is the bg */
2711 change_swallowed_window_colorset(
2712 b, True);
2716 return;
2719 recursive_change_colorset(
2720 UberButton->c, colorset, Event);
2723 static void handle_config_info_packet(unsigned long *body)
2725 char *tline, *token;
2726 int colorset;
2728 tline = (char*)&(body[3]);
2729 token = PeekToken(tline, &tline);
2730 if (StrEquals(token, "Colorset"))
2732 colorset = LoadColorset(tline);
2733 change_colorset(colorset, NULL);
2735 else if (StrEquals(token, XINERAMA_CONFIG_STRING))
2737 FScreenConfigureModule(tline);
2739 return;
2744 *** process_message()
2745 *** Process window list messages
2747 void process_message(unsigned long type, unsigned long *body)
2749 struct ConfigWinPacket *cfgpacket;
2751 switch (type)
2753 case M_NEW_DESK:
2754 new_desk = body[0];
2756 int button = -1;
2757 button_info *b, *ub;
2759 ub = UberButton;
2760 while (NextButton(&ub, &b, &button, 1))
2762 RedrawButton(b, DRAW_FORCE, NULL);
2765 break;
2766 case M_END_WINDOWLIST:
2767 SpawnSome();
2768 break;
2769 case M_MAP:
2770 swallow(body);
2771 break;
2772 case M_RES_NAME:
2773 case M_RES_CLASS:
2774 case M_WINDOW_NAME:
2775 CheckForHangon(body);
2776 break;
2777 case M_CONFIG_INFO:
2778 handle_config_info_packet((unsigned long*)body);
2779 break;
2780 case M_ADD_WINDOW:
2781 case M_CONFIGURE_WINDOW:
2782 cfgpacket = (void *) body;
2783 if (cfgpacket->w == MyWindow)
2785 button_lborder = button_rborder = button_tborder
2786 = button_bborder = cfgpacket->border_width;
2787 switch (GET_TITLE_DIR(cfgpacket))
2789 case DIR_N:
2790 button_tborder += cfgpacket->title_height;
2791 break;
2792 case DIR_S:
2793 button_bborder += cfgpacket->title_height;
2794 break;
2795 case DIR_W:
2796 button_lborder += cfgpacket->title_height;
2797 break;
2798 case DIR_E:
2799 button_rborder += cfgpacket->title_height;
2800 break;
2803 break;
2804 case MX_PROPERTY_CHANGE:
2805 if (body[0] == MX_PROPERTY_CHANGE_BACKGROUND &&
2806 ((!swallowed && body[2] == 0)
2807 || (swallowed && body[2] == MyWindow)))
2809 if (CSET_IS_TRANSPARENT_PR(UberButton->c->colorset))
2811 update_root_transparency(NULL);
2814 else if (body[0] == MX_PROPERTY_CHANGE_SWALLOW
2815 && body[2] == MyWindow)
2817 char *str;
2818 unsigned long u;
2819 Window s, swin;
2820 char cmd[256];
2821 button_info *b, *ub = UberButton;
2822 int button = -1;
2824 str = (char *)&body[3];
2825 swallowed = body[1];
2826 if (swallowed && str && sscanf(str, "%lu", &u) == 1)
2828 swallower_win = (Window)u;
2830 else
2832 swallower_win = 0;
2834 /* update the swallower */
2835 s = ((swallower_win && swallowed) ? swallower_win :
2836 MyWindow);
2837 while (NextButton(&ub, &b, &button, 0))
2839 swin = SwallowedWindow(b);
2840 if ((buttonSwallowCount(b) == 3) && swin)
2842 sprintf(cmd,
2843 "PropertyChange %u %u %lu %lu",
2844 MX_PROPERTY_CHANGE_SWALLOW, 1,
2845 swin, s);
2846 SendText(fd, cmd, 0);
2850 break;
2851 case M_STRING:
2852 parse_message_line((char *)&body[3]);
2853 break;
2854 default:
2855 break;
2860 /* --------------------------------- swallow code -------------------------- */
2863 *** CheckForHangon()
2864 *** Is the window here now?
2866 void CheckForHangon(unsigned long *body)
2868 button_info *b, *ub = UberButton;
2869 int button = -1;
2870 ushort d;
2871 char *cbody;
2872 cbody = (char *)&body[3];
2874 while (NextButton(&ub, &b, &button, 0))
2876 if (b->flags.b_Hangon && matchWildcards(b->hangon, cbody))
2878 /* Is this a swallowing button in state 1? */
2879 if (buttonSwallowCount(b) == 1)
2881 b->swallow &= ~b_Count;
2882 b->swallow |= 2;
2884 /* must grab the server here to make sure
2885 * the window is not swallowed by some
2886 * other application. */
2887 if (b->flags.b_Panel)
2889 b->PanelWin = (Window)body[0];
2891 else
2893 b->IconWin = (Window)body[0];
2895 b->flags.b_Hangon = 0;
2897 /* We get the parent of the window to compare
2898 * with later... */
2900 b->IconWinParent =
2901 GetRealGeometry(
2902 Dpy, SwallowedWindow(b),
2903 &b->x, &b->y, &b->w, &b->h,
2904 &b->bw, &d);
2906 #ifdef DEBUG_HANGON
2907 fprintf(stderr,
2908 "%s: Button 0x%06x %s 0x%lx \"%s\", "
2909 "parent 0x%lx\n", MyName, (ushort)b,
2910 "will swallow window", body[0], cbody,
2911 b->IconWinParent);
2912 #endif
2914 if (buttonSwallow(b)&b_UseOld)
2916 swallow(body);
2919 else
2921 /* Else it is an executing button with a
2922 * confirmed kill */
2924 #ifdef DEBUG_HANGON
2925 fprintf(stderr,
2926 "%s: Button 0x%06x %s 0x%lx "\"%s\", "
2927 "released\n", MyName, (int)b,
2928 "hung on window", body[0], cbody);
2929 #endif
2930 b->flags.b_Hangon = 0;
2931 free(b->hangon);
2932 b->hangon = NULL;
2933 RedrawButton(b, DRAW_FORCE, NULL);
2935 break;
2937 else if (buttonSwallowCount(b) >= 2
2938 && (Window)body[0] == SwallowedWindow(b))
2940 /* This window has already been swallowed by
2941 * someone else! */
2942 break;
2948 *** GetRealGeometry()
2949 *** Traverses window tree to find the real x,y of a window. Any simpler?
2950 *** Returns parent window, or None if failed.
2952 Window GetRealGeometry(
2953 Display *dpy, Window win, int *x, int *y, unsigned int *w,
2954 unsigned int *h, unsigned int *bw, unsigned int *d)
2956 Window root;
2957 Window rp = None;
2958 Window *children = None;
2959 unsigned int n;
2961 if (!XGetGeometry(dpy, win, &root, x, y, w, h, bw, d))
2963 return None;
2966 /* Now, x and y are not correct. They are parent relative, not root...
2967 * Hmm, ever heard of XTranslateCoordinates!? */
2968 XTranslateCoordinates(dpy, win, root, *x, *y, x, y, &rp);
2969 if (!XQueryTree(dpy, win, &root, &rp, &children, &n))
2971 return None;
2973 if (children)
2975 XFree(children);
2978 return rp;
2981 static void GetPanelGeometry(
2982 Bool get_big, button_info *b, int lb, int tb, int rb, int bb,
2983 int *x, int *y, int *w, int *h)
2985 int bx, by, bw, bh;
2986 int ftb = 0, fbb = 0, frb = 0, flb = 0;
2987 Window JunkWin;
2989 /* take in acount or not the FvwmButtons borders width */
2990 if (b->panel_flags.buttons_tbborder)
2992 ftb = button_tborder;
2993 fbb = button_bborder;
2995 if (b->panel_flags.buttons_lrborder)
2997 frb = button_rborder;
2998 flb = button_lborder;
3001 switch (b->slide_context)
3003 case SLIDE_CONTEXT_PB: /* slide relatively to the panel button */
3004 bx = buttonXPos(b, b->n);
3005 by = buttonYPos(b, b->n);
3006 bw = buttonWidth(b);
3007 bh = buttonHeight(b);
3008 XTranslateCoordinates(
3009 Dpy, MyWindow, Root, bx, by, &bx, &by, &JunkWin);
3010 break;
3011 case SLIDE_CONTEXT_MODULE: /* slide relatively to FvwmButtons */
3013 unsigned int dum, dum2;
3015 if (XGetGeometry(
3016 Dpy, MyWindow, &JunkWin, &bx, &by,
3017 (unsigned int *)&bw, (unsigned int *)&bh,
3018 (unsigned int *)&dum, &dum2))
3020 XTranslateCoordinates(
3021 Dpy, MyWindow, Root, bx, by, &bx, &by,
3022 &JunkWin);
3023 break;
3025 else
3027 /* fall thorugh to SLIDE_CONTEXT_ROOT */
3030 case SLIDE_CONTEXT_ROOT: /* slide relatively to the root windows */
3031 switch (b->slide_direction)
3033 case SLIDE_UP:
3034 bx = 0;
3035 by = dph;
3036 break;
3037 case SLIDE_DOWN:
3038 bx = 0;
3039 by = -dph;
3040 break;
3041 case SLIDE_RIGHT:
3042 bx = -dpw;
3043 by = 0;
3044 break;
3045 case SLIDE_LEFT:
3046 bx = dpw;
3047 by = 0;
3048 break;
3050 bw = dpw;
3051 bh = dph;
3052 break;
3055 /* relative corrections in % or pixel */
3056 *x = 0;
3057 *y = 0;
3058 if (b->relative_x != 0)
3060 if (b->panel_flags.relative_x_pixel)
3062 *x = b->relative_x;
3064 else
3066 *x = (int)(b->relative_x * bw) / 100;
3069 if (b->relative_y != 0)
3071 if (b->panel_flags.relative_y_pixel)
3073 *y = b->relative_y;
3075 else
3077 *y = (int)(b->relative_y * bh) / 100;
3081 switch (b->slide_direction)
3083 case SLIDE_UP:
3084 switch (b->slide_position)
3086 case SLIDE_POSITION_CENTER:
3087 *x += bx + (int)(bw - (b->w + (rb - lb))
3088 + (frb - flb)) / 2;
3089 break;
3090 case SLIDE_POSITION_LEFT_TOP:
3091 *x += bx + lb - flb;
3092 break;
3093 case SLIDE_POSITION_RIGHT_BOTTOM:
3094 *x += bx + bw - b->w - rb + frb;
3095 break;
3097 *w = b->w ;
3098 if (get_big)
3100 *y += by - (int)b->h - bb - ftb;
3101 *h = b->h;
3103 else
3105 *y += by - bb - ftb;
3106 *h = 0;
3108 break;
3109 case SLIDE_DOWN:
3110 switch (b->slide_position)
3112 case SLIDE_POSITION_CENTER:
3113 *x += bx + (int)(bw - (b->w + (rb - lb))
3114 + (frb - flb)) / 2;
3115 break;
3116 case SLIDE_POSITION_LEFT_TOP:
3117 *x += bx + lb - flb;
3118 break;
3119 case SLIDE_POSITION_RIGHT_BOTTOM:
3120 *x += bx + bw - b->w - rb + frb;
3121 break;
3123 *y += by + (int)bh + tb + fbb;
3124 *w = b->w;
3125 if (get_big)
3127 *h = b->h;
3129 else
3131 *h = 0;
3133 break;
3134 case SLIDE_LEFT:
3135 switch (b->slide_position)
3137 case SLIDE_POSITION_CENTER:
3138 *y += by + (int)(bh - b->h + (tb - bb)
3139 + (fbb - ftb)) / 2;
3140 break;
3141 case SLIDE_POSITION_LEFT_TOP:
3142 *y += by + tb - ftb;
3143 break;
3144 case SLIDE_POSITION_RIGHT_BOTTOM:
3145 *y += by + bh - b->h - bb + fbb;
3146 break;
3148 *h = b->h;
3149 if (get_big)
3151 *x += bx - (int)b->w - rb - flb;
3152 *w = b->w;
3154 else
3156 *x += bx - rb - flb;
3157 *w = 0;
3159 break;
3160 case SLIDE_RIGHT:
3161 switch (b->slide_position)
3163 case SLIDE_POSITION_CENTER:
3164 *y += by + (int)(bh - b->h + (tb - bb)
3165 + (fbb - ftb)) / 2;
3166 break;
3167 case SLIDE_POSITION_LEFT_TOP:
3168 *y += by + tb - ftb;
3169 break;
3170 case SLIDE_POSITION_RIGHT_BOTTOM:
3171 *y += by + bh - b->h - bb + fbb;
3172 break;
3174 *x += bx + (int)bw + lb + frb;
3175 *h = b->h;
3176 if (get_big)
3178 *w = b->w;
3180 else
3182 *w = 0;
3184 break;
3187 return;
3192 *** swallow()
3193 *** Executed when swallowed windows get mapped
3195 void swallow(unsigned long *body)
3197 button_info *ub = UberButton, *b;
3198 int button = -1;
3199 int i;
3200 unsigned int d;
3201 Window p;
3202 FlocaleNameString temp;
3203 temp.name = NULL;
3204 temp.name_list = NULL;
3206 while (NextButton(&ub, &b, &button, 0))
3208 Window swin = SwallowedWindow(b);
3210 if ((swin == (Window)body[0]) && (buttonSwallowCount(b) == 2))
3212 /* Store the geometry in case we need to unswallow.
3213 * Get parent */
3215 p = GetRealGeometry(
3216 Dpy, swin, &b->x, &b->y, &b->w, &b->h, &b->bw,
3217 &d);
3218 #ifdef DEBUG_HANGON
3219 fprintf(stderr,
3220 "%s: Button 0x%06x %s 0x%lx, parent 0x%lx\n",
3221 MyName, (ushort)b, "trying to swallow window",
3222 body[0], p);
3223 #endif
3225 if (p == None)
3226 /* This means the window is no more */ /* NO! wrong */
3228 fprintf(stderr,
3229 "%s: Window 0x%lx (\"%s\") disappeared"
3230 " before swallow complete\n",
3231 MyName, swin, b->hangon);
3232 /* Now what? For now: give up that button */
3233 b->flags.b_Hangon = 0;
3234 b->flags.b_Swallow = 0;
3235 b->flags.b_Panel = 0;
3236 return;
3239 if (p != b->IconWinParent)
3241 /* The window has been reparented */
3242 fprintf(stderr,
3243 "%s: Window 0x%lx (\"%s\") was grabbed"
3244 " by someone else (window 0x%lx)\n",
3245 MyName, swin, b->hangon, p);
3247 /* Request a new windowlist, we might have
3248 ignored another matching window.. */
3249 SendText(fd, "Send_WindowList", 0);
3251 /* Back one square and lose one turn */
3252 b->swallow &= ~b_Count;
3253 b->swallow |= 1;
3254 b->flags.b_Hangon = 1;
3255 return;
3258 #ifdef DEBUG_HANGON
3259 fprintf(stderr,
3260 "%s: Button 0x%06x swallowed window 0x%lx\n",
3261 MyName, (ushort)b, body[0]);
3262 #endif
3264 if (b->flags.b_Swallow)
3266 /* "Swallow" the window! Place it in the void
3267 * so we don't see it until it's MoveResize'd */
3269 MyXGrabServer(Dpy);
3270 do_allow_bad_access = True;
3271 XSelectInput(Dpy, swin, SW_EVENTS);
3272 XSync(Dpy, 0);
3273 do_allow_bad_access = False;
3274 if (was_bad_access)
3276 /* BadAccess means that the window is
3277 * already swallowed by someone else.
3278 * Wait for another window. */
3279 was_bad_access = False;
3280 /* Back one square and lose one turn */
3281 b->swallow &= ~b_Count;
3282 b->swallow |= 1;
3283 b->flags.b_Hangon = 1;
3284 MyXUngrabServer(Dpy);
3285 return;
3287 /*error checking*/
3288 for (i = 0;
3289 !b->flags.b_ActionIgnoresClientWindow
3291 i <= NUMBER_OF_EXTENDED_MOUSE_BUTTONS;
3292 i++)
3294 if (b->action != NULL && b->action[i] != NULL)
3296 XGrabButton(
3297 Dpy, i, 0, b->IconWin,
3298 False, ButtonPressMask
3299 | ButtonReleaseMask,
3300 GrabModeAsync,
3301 GrabModeAsync,
3302 None, None);
3303 if (i == 0)
3305 break;
3309 XUnmapWindow(Dpy, swin);
3310 XReparentWindow(
3311 Dpy, swin, MyWindow, -32768, -32768);
3312 fsm_proxy(Dpy, swin, getenv("SESSION_MANAGER"));
3313 XSync(Dpy, 0);
3314 MyXUngrabServer(Dpy);
3315 XSync(Dpy, 0);
3316 usleep(50000);
3317 b->swallow &= ~b_Count;
3318 b->swallow |= 3;
3319 if (buttonSwallow(b) & b_UseTitle)
3321 if (b->flags.b_Title)
3323 free(b->title);
3325 b->flags.b_Title = 1;
3326 FlocaleGetNameProperty(
3327 XGetWMName, Dpy, swin, &temp);
3328 if (temp.name != NULL)
3330 CopyString(
3331 &b->title, temp.name);
3332 FlocaleFreeNameProperty(&temp);
3334 else
3336 CopyString(&b->title, "");
3340 MakeButton(b);
3341 XMapWindow(Dpy, swin);
3342 XSync(Dpy, 0);
3343 if (b->swallow & b_FvwmModule)
3345 /* if we swallow a module we send an avertisment */
3346 char cmd[256];
3348 sprintf(cmd,
3349 "PropertyChange %u %u %lu %lu",
3350 MX_PROPERTY_CHANGE_SWALLOW, 1,
3351 SwallowedWindow(b),
3352 swallower_win ? swallower_win
3353 : MyWindow);
3354 SendText(fd, cmd, 0);
3356 if (b->flags.b_Colorset && !(b->swallow & b_FvwmModule))
3358 /* A short delay to give the application
3359 * the chance to set the background
3360 * itself, so we can override it.
3361 * If we don't do this, the application
3362 * may override our background. On the
3363 * other hand it may still override our
3364 * background, but our chances are a bit
3365 * better. */
3367 usleep(250000);
3368 XSync(Dpy, 0);
3369 change_swallowed_window_colorset(b, True);
3371 if (FShapesSupported)
3373 if (UberButton->c->flags.b_TransBack)
3375 SetTransparentBackground(
3376 UberButton, Width,
3377 Height);
3378 FShapeSelectInput(
3379 Dpy, swin,
3380 FShapeNotifyMask);
3383 /* Redraw and force cleaning the background to erase the old button
3384 * title. */
3385 RedrawButton(b, DRAW_FORCE, NULL);
3387 else /* (b->flags & b_Panel) */
3389 b->swallow &= ~b_Count;
3390 b->swallow |= 3;
3391 XSelectInput(Dpy, swin, PA_EVENTS);
3392 if (!XWithdrawWindow(Dpy, swin, screen))
3394 /* oops. what can we do now?
3395 * let's pretend the unmap worked. */
3397 b->newflags.panel_mapped = 0;
3399 break;
3405 void exec_swallow(char *action, button_info *b)
3407 static char *my_sm_env = NULL;
3408 static char *orig_sm_env = NULL;
3409 static int len = 0;
3410 static Bool sm_initialized = False;
3411 static Bool session_manager = False;
3412 char *cmd;
3414 if (!action)
3416 return;
3419 if (!sm_initialized)
3421 /* use sm only when needed */
3422 sm_initialized = True;
3423 orig_sm_env = getenv("SESSION_MANAGER");
3424 if (orig_sm_env && !StrEquals("", orig_sm_env))
3426 /* this set the new SESSION_MANAGER env */
3427 session_manager = fsm_init(MyName);
3431 if (!session_manager /*|| (buttonSwallow(b)&b_NoClose)*/)
3433 SendText(fd, action, 0);
3434 return;
3437 if (my_sm_env == NULL)
3439 my_sm_env = getenv("SESSION_MANAGER");
3440 len = 45 + strlen(my_sm_env) + strlen(orig_sm_env);
3443 cmd = safemalloc(len + strlen(action));
3444 sprintf(
3445 cmd,
3446 "FSMExecFuncWithSessionManagment \"%s\" \"%s\" \"%s\"",
3447 my_sm_env, action, orig_sm_env);
3448 SendText(fd, cmd, 0);
3449 free(cmd);