Fix segfault when creating a tearoff menu using a Pixmap background.
[fvwm.git] / fvwm / builtins.c
blob3c1223570c3441c152e8d0c5f2cfd02c1e6fc6c1
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * This module is all original code
19 * by Rob Nation
20 * Copyright 1993, Robert Nation
21 * You may use this code for any purpose, as long as the original
22 * copyright remains in the source code and all documentation
25 /* ---------------------------- included header files ---------------------- */
27 #include "config.h"
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <X11/keysym.h>
37 #include "libs/fvwmlib.h"
38 #include "libs/fvwmsignal.h"
39 #include "libs/setpgrp.h"
40 #include "libs/Grab.h"
41 #include "libs/Parse.h"
42 #include "libs/ColorUtils.h"
43 #include "libs/Graphics.h"
44 #include "libs/wild.h"
45 #include "libs/envvar.h"
46 #include "libs/ClientMsg.h"
47 #include "libs/Picture.h"
48 #include "libs/PictureUtils.h"
49 #include "libs/FGettext.h"
50 #include "libs/charmap.h"
51 #include "libs/wcontext.h"
52 #include "libs/Flocale.h"
53 #include "libs/Ficonv.h"
54 #include "fvwm.h"
55 #include "externs.h"
56 #include "colorset.h"
57 #include "bindings.h"
58 #include "misc.h"
59 #include "cursor.h"
60 #include "functions.h"
61 #include "commands.h"
62 #include "screen.h"
63 #include "builtins.h"
64 #include "module_interface.h"
65 #include "borders.h"
66 #include "frame.h"
67 #include "events.h"
68 #include "ewmh.h"
69 #include "virtual.h"
70 #include "decorations.h"
71 #include "add_window.h"
72 #include "update.h"
73 #include "style.h"
74 #include "move_resize.h"
75 #include "menus.h"
76 #ifdef HAVE_STROKE
77 #include "stroke.h"
78 #endif /* HAVE_STROKE */
80 /* ---------------------------- local definitions -------------------------- */
82 /* ---------------------------- local macros ------------------------------- */
84 /* ---------------------------- imports ------------------------------------ */
86 extern float rgpctMovementDefault[32];
87 extern int cpctMovementDefault;
88 extern int cmsDelayDefault;
90 /* ---------------------------- included code files ------------------------ */
92 /* ---------------------------- local types -------------------------------- */
93 typedef enum {FakeMouseEvent, FakeKeyEvent} FakeEventType;
94 /* ---------------------------- forward declarations ----------------------- */
96 /* ---------------------------- local variables ---------------------------- */
98 static char *exec_shell_name="/bin/sh";
100 /* button state strings must match the enumerated states */
101 static char *button_states[BS_MaxButtonStateName + 1] =
103 "ActiveUp",
104 "ActiveDown",
105 "InactiveUp",
106 "InactiveDown",
107 "ToggledActiveUp",
108 "ToggledActiveDown",
109 "ToggledInactiveUp",
110 "ToggledInactiveDown",
111 "Active",
112 "Inactive",
113 "ToggledActive",
114 "ToggledInactive",
115 "AllNormal",
116 "AllToggled",
117 "AllActive",
118 "AllInactive",
119 "AllUp",
120 "AllDown",
121 "AllActiveUp",
122 "AllActiveDown",
123 "AllInactiveUp",
124 "AllInactiveDown",
125 NULL
128 /* ---------------------------- exported variables (globals) --------------- */
130 char *ModulePath = FVWM_MODULEDIR;
131 int moduleTimeout = DEFAULT_MODULE_TIMEOUT;
133 /* ---------------------------- local functions ---------------------------- */
135 /** Prepend rather than replace the image path.
136 Used for obsolete PixmapPath and IconPath **/
137 static void obsolete_imagepaths( const char* pre_path )
139 char* tmp = stripcpy( pre_path );
140 char* path = alloca(strlen( tmp ) + strlen(PictureGetImagePath()) + 2 );
142 strcpy( path, tmp );
143 free( tmp );
145 strcat( path, ":" );
146 strcat( path, PictureGetImagePath() );
148 PictureSetImagePath( path );
150 return;
155 * Reads a title button description (veliaa@rpi.edu)
158 static char *ReadTitleButton(
159 char *s, TitleButton *tb, Boolean append, int button)
161 char *end = NULL;
162 char *spec;
163 char *t;
164 int i;
165 int bs;
166 int bs_start, bs_end;
167 int pstyle = 0;
168 DecorFace tmpdf;
170 Bool multiple;
171 int use_mask = 0;
172 int set_mask = 0;
174 s = SkipSpaces(s, NULL, 0);
175 t = GetNextTokenIndex(s, button_states, 0, &bs);
176 if (bs != BS_All)
178 s = SkipSpaces(t, NULL, 0);
181 if (bs == BS_All)
183 use_mask = 0;
184 set_mask = 0;
186 else if (bs == BS_Active)
188 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
189 set_mask = 0;
191 else if (bs == BS_Inactive)
193 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
194 set_mask = BS_MASK_INACTIVE;
196 else if (bs == BS_ToggledActive)
198 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
199 set_mask = BS_MASK_TOGGLED;
201 else if (bs == BS_ToggledInactive)
203 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
204 set_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
206 else if (bs == BS_AllNormal)
208 use_mask = BS_MASK_TOGGLED;
209 set_mask = 0;
211 else if (bs == BS_AllToggled)
213 use_mask = BS_MASK_TOGGLED;
214 set_mask = BS_MASK_TOGGLED;
216 else if (bs == BS_AllActive)
218 use_mask = BS_MASK_INACTIVE;
219 set_mask = 0;
221 else if (bs == BS_AllInactive)
223 use_mask = BS_MASK_INACTIVE;
224 set_mask = BS_MASK_INACTIVE;
226 else if (bs == BS_AllUp)
228 use_mask = BS_MASK_DOWN;
229 set_mask = 0;
231 else if (bs == BS_AllDown)
233 use_mask = BS_MASK_DOWN;
234 set_mask = BS_MASK_DOWN;
236 else if (bs == BS_AllActiveUp)
238 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
239 set_mask = 0;
241 else if (bs == BS_AllActiveDown)
243 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
244 set_mask = BS_MASK_DOWN;
246 else if (bs == BS_AllInactiveUp)
248 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
249 set_mask = BS_MASK_INACTIVE;
251 else if (bs == BS_AllInactiveDown)
253 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
254 set_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
257 if ((bs & BS_MaxButtonStateMask) == bs)
259 multiple = False;
260 bs_start = bs;
261 bs_end = bs;
263 else
265 multiple = True;
266 bs_start = 0;
267 bs_end = BS_MaxButtonState - 1;
268 for (i = bs_start; (i & use_mask) != set_mask && i <= bs_end;
269 i++)
271 bs_start++;
275 if (*s == '(')
277 int len;
278 pstyle = 1;
279 if (!(end = strchr(++s, ')')))
281 fvwm_msg(
282 ERR, "ReadTitleButton",
283 "missing parenthesis: %s", s);
284 return NULL;
286 s = SkipSpaces(s, NULL, 0);
287 len = end - s + 1;
288 spec = safemalloc(len);
289 strncpy(spec, s, len - 1);
290 spec[len - 1] = 0;
292 else
294 spec = s;
297 spec = SkipSpaces(spec, NULL, 0);
298 /* setup temporary in case button read fails */
299 memset(&tmpdf, 0, sizeof(DecorFace));
300 DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
302 if (strncmp(spec, "--", 2) == 0)
304 /* only change flags */
305 Bool verbose = True;
306 for (i = bs_start; i <= bs_end; ++i)
308 if (multiple && (i & use_mask) != set_mask)
310 continue;
312 ReadDecorFace(spec, &TB_STATE(*tb)[i], button, verbose);
313 verbose = False;
316 else if (ReadDecorFace(spec, &tmpdf, button, True))
318 if (append)
320 DecorFace *head = &TB_STATE(*tb)[bs_start];
321 DecorFace *tail = head;
322 DecorFace *next;
324 while (tail->next)
326 tail = tail->next;
328 tail->next = (DecorFace *)safemalloc(sizeof(DecorFace));
329 memcpy(tail->next, &tmpdf, sizeof(DecorFace));
330 if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
331 DFS_FACE_TYPE((&TB_STATE(*tb)[bs_start])->style) ==
332 DefaultVectorButton)
334 /* override the default vector style */
335 memcpy(
336 &tail->next->style, &head->style,
337 sizeof(DecorFaceStyle));
338 DFS_FACE_TYPE(tail->next->style) = VectorButton;
339 next = head->next;
340 head->next = NULL;
341 FreeDecorFace(dpy, head);
342 memcpy(head, next, sizeof(DecorFace));
343 free(next);
345 for (i = bs_start + 1; i <= bs_end; ++i)
347 if (multiple && (i & use_mask) != set_mask)
349 continue;
351 head = &TB_STATE(*tb)[i];
352 tail = head;
353 while (tail->next)
355 tail = tail->next;
357 tail->next = (DecorFace *)safemalloc(
358 sizeof(DecorFace));
359 memset(
360 &DFS_FLAGS(tail->next->style), 0,
361 sizeof(DFS_FLAGS(tail->next->style)));
362 DFS_FACE_TYPE(tail->next->style) =
363 SimpleButton;
364 tail->next->next = NULL;
365 ReadDecorFace(spec, tail->next, button, False);
366 if (DFS_FACE_TYPE(tail->next->style) ==
367 VectorButton &&
368 DFS_FACE_TYPE((&TB_STATE(*tb)[i])->style) ==
369 DefaultVectorButton)
371 /* override the default vector style */
372 memcpy(
373 &tail->next->style,
374 &head->style,
375 sizeof(DecorFaceStyle));
376 DFS_FACE_TYPE(tail->next->style) =
377 VectorButton;
378 next = head->next;
379 head->next = NULL;
380 FreeDecorFace(dpy, head);
381 memcpy(head, next, sizeof(DecorFace));
382 free(next);
386 else
388 FreeDecorFace(dpy, &TB_STATE(*tb)[bs_start]);
389 memcpy(
390 &(TB_STATE(*tb)[bs_start]), &tmpdf,
391 sizeof(DecorFace));
392 for (i = bs_start + 1; i <= bs_end; ++i)
394 if (multiple && (i & use_mask) != set_mask)
396 continue;
398 ReadDecorFace(
399 spec, &TB_STATE(*tb)[i], button, False);
403 if (pstyle)
405 free(spec);
406 end++;
407 end = SkipSpaces(end, NULL, 0);
410 return end;
413 /* Remove the given decor from all windows */
414 static void __remove_window_decors(F_CMD_ARGS, FvwmDecor *d)
416 const exec_context_t *exc2;
417 exec_context_changes_t ecc;
418 FvwmWindow *t;
420 for (t = Scr.FvwmRoot.next; t; t = t->next)
422 if (t->decor == d)
424 /* remove the extra title height now because we delete
425 * the current decor before calling ChangeDecor(). */
426 t->g.frame.height -= t->decor->title_height;
427 t->decor = NULL;
428 ecc.w.fw = t;
429 ecc.w.wcontext = C_WINDOW;
430 exc2 = exc_clone_context(
431 exc, &ecc, ECC_FW | ECC_WCONTEXT);
432 execute_function(
433 cond_rc, exc2, "ChangeDecor Default", 0);
434 exc_destroy_context(exc2);
438 return;
441 static void do_title_style(F_CMD_ARGS, Bool do_add)
443 char *parm;
444 char *prev;
445 #ifdef USEDECOR
446 FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
447 #else
448 FvwmDecor *decor = &Scr.DefaultDecor;
449 #endif
451 Scr.flags.do_need_window_update = 1;
452 decor->flags.has_changed = 1;
453 decor->titlebar.flags.has_changed = 1;
455 for (prev = action ; (parm = PeekToken(action, &action)); prev = action)
457 if (!do_add && StrEquals(parm,"centered"))
459 TB_JUSTIFICATION(decor->titlebar) = JUST_CENTER;
461 else if (!do_add && StrEquals(parm,"leftjustified"))
463 TB_JUSTIFICATION(decor->titlebar) = JUST_LEFT;
465 else if (!do_add && StrEquals(parm,"rightjustified"))
467 TB_JUSTIFICATION(decor->titlebar) = JUST_RIGHT;
469 else if (!do_add && StrEquals(parm,"height"))
471 int height = 0;
472 int next = 0;
474 if (!action ||
475 sscanf(action, "%d%n", &height, &next) <= 0 ||
476 height < MIN_FONT_HEIGHT ||
477 height > MAX_FONT_HEIGHT)
479 if (height != 0)
481 fvwm_msg(ERR, "do_title_style",
482 "bad height argument (height"
483 " must be from 5 to 256)");
484 height = 0;
487 if (decor->title_height != height ||
488 decor->min_title_height != 0)
490 decor->title_height = height;
491 decor->min_title_height = 0;
492 decor->flags.has_title_height_changed = 1;
494 if (action)
495 action += next;
497 else if (!do_add && StrEquals(parm,"MinHeight"))
499 int height = 0;
500 int next = 0;
502 if (!action ||
503 sscanf(action, "%d%n", &height, &next) <= 0 ||
504 height < MIN_FONT_HEIGHT ||
505 height > MAX_FONT_HEIGHT)
507 if (height < MIN_FONT_HEIGHT)
508 height = MIN_FONT_HEIGHT;
509 else if (height > MAX_FONT_HEIGHT)
510 height = 0;
512 if (decor->min_title_height != height)
514 decor->title_height = 0;
515 decor->min_title_height = height;
516 decor->flags.has_title_height_changed = 1;
518 if (action)
519 action += next;
521 else
523 action = ReadTitleButton(
524 prev, &decor->titlebar, do_add, -1);
528 return;
533 * Reads a multi-pixmap titlebar config. (tril@igs.net)
536 static char *ReadMultiPixmapDecor(char *s, DecorFace *df)
538 static char *pm_names[TBMP_NUM_PIXMAPS+1] =
540 "Main",
541 "LeftMain",
542 "RightMain",
543 "LeftButtons",
544 "RightButtons",
545 "UnderText",
546 "LeftOfText",
547 "RightOfText",
548 "LeftEnd",
549 "RightEnd",
550 "Buttons",
551 NULL
553 FvwmPicture **pm;
554 FvwmAcs *acs;
555 Pixel *pixels;
556 char *token;
557 Bool stretched;
558 Bool load_pixmap = False;
559 int pm_id, i = 0;
560 FvwmPictureAttributes fpa;
562 df->style.face_type = MultiPixmap;
563 df->u.mp.pixmaps = pm =
564 (FvwmPicture**)safecalloc(
565 TBMP_NUM_PIXMAPS, sizeof(FvwmPicture*));
566 df->u.mp.acs = acs =
567 (FvwmAcs *)safemalloc(TBMP_NUM_PIXMAPS * sizeof(FvwmAcs));
568 df->u.mp.pixels = pixels =
569 (Pixel *)safemalloc(TBMP_NUM_PIXMAPS * sizeof(Pixel));
570 for(i=0; i < TBMP_NUM_PIXMAPS; i++)
572 acs[i].cs = -1;
573 acs[i].alpha_percent = 100;
575 s = GetNextTokenIndex(s, pm_names, 0, &pm_id);
576 while (pm_id >= 0)
578 stretched = False;
579 load_pixmap = False;
580 s = DoPeekToken(s, &token, ",()", NULL, NULL);
581 if (StrEquals(token, "stretched"))
583 stretched = True;
584 s = DoPeekToken(s, &token, ",", NULL, NULL);
586 else if (StrEquals(token, "tiled"))
588 s = DoPeekToken(s, &token, ",", NULL, NULL);
590 if (!token)
592 break;
594 if (pm[pm_id] || acs[pm_id].cs >= 0 ||
595 (df->u.mp.solid_flags & (1 << pm_id)))
597 fvwm_msg(WARN, "ReadMultiPixmapDecor",
598 "Ignoring: already-specified %s",
599 pm_names[i]);
600 continue;
602 if (stretched)
604 df->u.mp.stretch_flags |= (1 << pm_id);
606 if (strncasecmp (token, "Colorset", 8) == 0)
608 int val;
609 char *tmp;
611 tmp = DoPeekToken(s, &token, ",", NULL, NULL);
612 if (!GetIntegerArguments(token, NULL, &val, 1) ||
613 val < 0)
615 fvwm_msg(
616 ERR, "ReadMultiPixmapDecor",
617 "Colorset shoule take one or two "
618 "positive integers as argument");
620 else
622 acs[pm_id].cs = val;
623 alloc_colorset(val);
624 s = tmp;
625 tmp = DoPeekToken(s, &token, ",", NULL, NULL);
626 if (GetIntegerArguments(token, NULL, &val, 1))
628 acs[pm_id].alpha_percent =
629 max(0, min(100,val));
630 s = tmp;
634 else if (strncasecmp(token, "TiledPixmap", 11) == 0)
636 s = DoPeekToken(s, &token, ",", NULL, NULL);
637 load_pixmap = True;
639 else if (strncasecmp(token, "AdjustedPixmap", 14) == 0)
641 s = DoPeekToken(s, &token, ",", NULL, NULL);
642 load_pixmap = True;
643 df->u.mp.stretch_flags |= (1 << pm_id);
645 else if (strncasecmp(token, "Solid", 5) == 0)
647 s = DoPeekToken(s, &token, ",", NULL, NULL);
648 if (token)
650 df->u.mp.pixels[pm_id] = GetColor(token);
651 df->u.mp.solid_flags |= (1 << pm_id);
654 else
656 load_pixmap = True;
658 if (load_pixmap && token)
660 fpa.mask = (Pdepth <= 8)? FPAM_DITHER:0; /* ? */
661 pm[pm_id] = PCacheFvwmPicture(
662 dpy, Scr.NoFocusWin, NULL, token, fpa);
663 if (!pm[pm_id])
665 fvwm_msg(ERR, "ReadMultiPixmapDecor",
666 "Pixmap '%s' could not be loaded",
667 token);
670 if (pm_id == TBMP_BUTTONS)
672 if (pm[TBMP_LEFT_BUTTONS])
674 PDestroyFvwmPicture(dpy, pm[TBMP_LEFT_BUTTONS]);
676 if (pm[TBMP_RIGHT_BUTTONS])
678 PDestroyFvwmPicture(dpy, pm[TBMP_RIGHT_BUTTONS]);
680 df->u.mp.stretch_flags &= ~(1 << TBMP_LEFT_BUTTONS);
681 df->u.mp.stretch_flags &= ~(1 << TBMP_RIGHT_BUTTONS);
682 df->u.mp.solid_flags &= ~(1 << TBMP_LEFT_BUTTONS);
683 df->u.mp.solid_flags &= ~(1 << TBMP_RIGHT_BUTTONS);
684 if (pm[TBMP_BUTTONS])
686 pm[TBMP_LEFT_BUTTONS] =
687 PCloneFvwmPicture(pm[TBMP_BUTTONS]);
688 acs[TBMP_LEFT_BUTTONS].cs = -1;
689 pm[TBMP_RIGHT_BUTTONS] =
690 PCloneFvwmPicture(pm[TBMP_BUTTONS]);
691 acs[TBMP_RIGHT_BUTTONS].cs = -1;
693 else
695 pm[TBMP_RIGHT_BUTTONS] =
696 pm[TBMP_LEFT_BUTTONS] = NULL;
697 acs[TBMP_RIGHT_BUTTONS].cs =
698 acs[TBMP_LEFT_BUTTONS].cs =
699 acs[TBMP_BUTTONS].cs;
700 acs[TBMP_RIGHT_BUTTONS].alpha_percent =
701 acs[TBMP_LEFT_BUTTONS].alpha_percent =
702 acs[TBMP_BUTTONS].alpha_percent;
703 pixels[TBMP_LEFT_BUTTONS] =
704 pixels[TBMP_RIGHT_BUTTONS] =
705 pixels[TBMP_BUTTONS];
707 if (stretched)
709 df->u.mp.stretch_flags |=
710 (1 << TBMP_LEFT_BUTTONS) |
711 (1 << TBMP_RIGHT_BUTTONS);
713 if (df->u.mp.solid_flags & (1 << TBMP_BUTTONS))
715 df->u.mp.solid_flags |=
716 (1 << TBMP_LEFT_BUTTONS);
717 df->u.mp.solid_flags |=
718 (1 << TBMP_RIGHT_BUTTONS);
720 if (pm[TBMP_BUTTONS])
722 PDestroyFvwmPicture(dpy, pm[TBMP_BUTTONS]);
723 pm[TBMP_BUTTONS] = NULL;
725 acs[TBMP_BUTTONS].cs = -1;
726 df->u.mp.solid_flags &= ~(1 << TBMP_BUTTONS);
728 s = SkipSpaces(s, NULL, 0);
729 s = GetNextTokenIndex(s, pm_names, 0, &pm_id);
732 if (!(pm[TBMP_MAIN] || acs[TBMP_MAIN].cs >= 0 ||
733 (df->u.mp.solid_flags & TBMP_MAIN))
735 !(pm[TBMP_LEFT_MAIN] || acs[TBMP_LEFT_MAIN].cs >= 0 ||
736 (df->u.mp.solid_flags & TBMP_LEFT_MAIN))
738 !(pm[TBMP_RIGHT_MAIN] || acs[TBMP_RIGHT_MAIN].cs >= 0 ||
739 (df->u.mp.solid_flags & TBMP_RIGHT_MAIN)))
741 fvwm_msg(ERR, "ReadMultiPixmapDecor",
742 "No Main pixmap/colorset/solid found for TitleStyle "
743 "MultiPixmap (you must specify either Main, "
744 "or both LeftMain and RightMain)");
745 for (i=0; i < TBMP_NUM_PIXMAPS; i++)
747 if (pm[i])
749 PDestroyFvwmPicture(dpy, pm[i]);
751 else if (!!(df->u.mp.solid_flags & i))
753 PictureFreeColors(
754 dpy, Pcmap, &df->u.mp.pixels[i], 1, 0,
755 False);
758 free(pm);
759 free(acs);
760 free(pixels);
761 return NULL;
764 return s;
769 * DestroyFvwmDecor -- frees all memory assocated with an FvwmDecor
770 * structure, but does not free the FvwmDecor itself
773 static void DestroyFvwmDecor(FvwmDecor *decor)
775 int i;
776 /* reset to default button set (frees allocated mem) */
777 DestroyAllButtons(decor);
778 for (i = 0; i < BS_MaxButtonState; ++i)
780 FreeDecorFace(dpy, &TB_STATE(decor->titlebar)[i]);
782 FreeDecorFace(dpy, &decor->BorderStyle.active);
783 FreeDecorFace(dpy, &decor->BorderStyle.inactive);
784 #ifdef USEDECOR
785 if (decor->tag)
787 free(decor->tag);
788 decor->tag = NULL;
790 #endif
792 return;
795 static void SetLayerButtonFlag(
796 int layer, int multi, int set, FvwmDecor *decor, TitleButton *tb)
798 int i;
799 int start = 0;
800 int add = 2;
802 if (multi)
804 if (multi == 2)
806 start = 1;
808 else if (multi == 3)
810 add = 1;
812 for (i = start; i < NUMBER_OF_TITLE_BUTTONS; i += add)
814 if (set)
816 TB_FLAGS(decor->buttons[i]).has_layer = 1;
817 TB_LAYER(decor->buttons[i]) = layer;
819 else
821 TB_FLAGS(decor->buttons[i]).has_layer = 0;
825 else
827 if (set)
829 TB_FLAGS(*tb).has_layer = 1;
830 TB_LAYER(*tb) = layer;
832 else
834 TB_FLAGS(*tb).has_layer = 0;
838 return;
843 * Changes a button decoration style (changes by veliaa@rpi.edu)
846 static void SetMWMButtonFlag(
847 mwm_flags flag, int multi, int set, FvwmDecor *decor, TitleButton *tb)
849 int i;
850 int start = 0;
851 int add = 2;
853 if (multi)
855 if (multi == 2)
857 start = 1;
859 else if (multi == 3)
861 add = 1;
863 for (i = start; i < NUMBER_OF_TITLE_BUTTONS; i += add)
865 if (set)
867 TB_MWM_DECOR_FLAGS(decor->buttons[i]) |= flag;
869 else
871 TB_MWM_DECOR_FLAGS(decor->buttons[i]) &= ~flag;
875 else
877 if (set)
879 TB_MWM_DECOR_FLAGS(*tb) |= flag;
881 else
883 TB_MWM_DECOR_FLAGS(*tb) &= ~flag;
887 return;
890 static void do_button_style(F_CMD_ARGS, Bool do_add)
892 int i;
893 int multi = 0;
894 int button = 0;
895 int do_return;
896 char *text = NULL;
897 char *prev = NULL;
898 char *parm = NULL;
899 TitleButton *tb = NULL;
900 #ifdef USEDECOR
901 FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
902 #else
903 FvwmDecor *decor = &Scr.DefaultDecor;
904 #endif
906 parm = PeekToken(action, &text);
907 if (parm && isdigit(*parm))
909 button = atoi(parm);
910 button = BUTTON_INDEX(button);
913 if (parm == NULL || button >= NUMBER_OF_TITLE_BUTTONS || button < 0)
915 fvwm_msg(
916 ERR, "ButtonStyle", "Bad button style (1) in line %s",
917 action);
918 return;
921 Scr.flags.do_need_window_update = 1;
923 do_return = 0;
924 if (!isdigit(*parm))
926 if (StrEquals(parm,"left"))
928 multi = 1; /* affect all left buttons */
930 else if (StrEquals(parm,"right"))
932 multi = 2; /* affect all right buttons */
934 else if (StrEquals(parm,"all"))
936 multi = 3; /* affect all buttons */
938 else
940 /* we're either resetting buttons or an invalid button
941 * set was specified */
942 if (StrEquals(parm,"reset"))
944 ResetAllButtons(decor);
946 else
948 fvwm_msg(
949 ERR, "ButtonStyle",
950 "Bad button style (2) in line %s",
951 action);
953 multi = 3;
954 do_return = 1;
957 /* mark button style and decor as changed */
958 decor->flags.has_changed = 1;
959 if (multi == 0)
961 /* a single button was specified */
962 tb = &decor->buttons[button];
963 TB_FLAGS(*tb).has_changed = 1;
965 else
967 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
969 if (((multi & 1) && !(i & 1)) ||
970 ((multi & 2) && (i & 1)))
972 TB_FLAGS(decor->buttons[i]).has_changed = 1;
976 if (do_return == 1)
978 return;
980 for (prev = text; (parm = PeekToken(text, &text)); prev = text)
982 if (!do_add && strcmp(parm,"-") == 0)
984 char *tok;
985 text = GetNextToken(text, &tok);
986 while (tok)
988 int set = 1;
989 char *old_tok = NULL;
991 if (*tok == '!')
993 /* flag negate */
994 set = 0;
995 old_tok = tok;
996 tok++;
998 if (StrEquals(tok,"Clear"))
1000 if (multi)
1002 for (i = 0;
1003 i < NUMBER_OF_TITLE_BUTTONS; ++i)
1005 if (((multi & 1) &&
1006 !(i & 1)) ||
1007 ((multi & 2) &&
1008 (i & 1)))
1010 TB_JUSTIFICATION(decor->buttons[i]) =
1011 (set) ? JUST_CENTER : JUST_RIGHT;
1012 memset(&TB_FLAGS(decor->buttons[i]), (set) ? 0 : 0xff,
1013 sizeof(TB_FLAGS(decor->buttons[i])));
1014 /* ? not very useful if set == 0 ? */
1018 else
1020 TB_JUSTIFICATION(*tb) = (set) ?
1021 JUST_CENTER :
1022 JUST_RIGHT;
1023 memset(&TB_FLAGS(*tb), (set) ?
1024 0 : 0xff,
1025 sizeof(TB_FLAGS(*tb)));
1026 /* ? not very useful if
1027 * set == 0 ? */
1030 else if (StrEquals(tok, "MWMDecorMenu"))
1032 SetMWMButtonFlag(
1033 MWM_DECOR_MENU, multi, set,
1034 decor, tb);
1036 else if (StrEquals(tok, "MWMDecorMin"))
1038 SetMWMButtonFlag(
1039 MWM_DECOR_MINIMIZE, multi, set,
1040 decor, tb);
1042 else if (StrEquals(tok, "MWMDecorMax"))
1044 SetMWMButtonFlag(
1045 MWM_DECOR_MAXIMIZE, multi, set,
1046 decor, tb);
1048 else if (StrEquals(tok, "MWMDecorShade"))
1050 SetMWMButtonFlag(
1051 MWM_DECOR_SHADE, multi, set,
1052 decor, tb);
1054 else if (StrEquals(tok, "MWMDecorStick"))
1056 SetMWMButtonFlag(
1057 MWM_DECOR_STICK, multi, set,
1058 decor, tb);
1060 else if (StrEquals(tok, "MwmDecorLayer"))
1062 int layer, got_number;
1063 char *ltok;
1064 text = GetNextToken(text, &ltok);
1065 if (ltok)
1067 got_number =
1068 (sscanf(ltok, "%d",
1069 &layer) == 1);
1070 free (ltok);
1072 else
1074 got_number = 0;
1076 if (!ltok || !got_number)
1078 fvwm_msg(ERR, "ButtonStyle",
1079 "could not read"
1080 " integer value for"
1081 " layer -- line: %s",
1082 text);
1084 else
1086 SetLayerButtonFlag(
1087 layer, multi, set,
1088 decor, tb);
1092 else
1094 fvwm_msg(ERR, "ButtonStyle",
1095 "unknown title button flag"
1096 " %s -- line: %s", tok, text);
1098 if (set)
1100 free(tok);
1102 else
1104 free(old_tok);
1106 text = GetNextToken(text, &tok);
1108 break;
1110 else
1112 if (multi)
1114 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
1116 if (((multi & 1) && !(i & 1)) ||
1117 ((multi & 2) && (i & 1)))
1119 text = ReadTitleButton(
1120 prev,
1121 &decor->buttons[i],
1122 do_add, i);
1126 else if (!(text = ReadTitleButton(
1127 prev, tb, do_add, button)))
1129 break;
1134 return;
1137 static
1138 int update_decorface_colorset(DecorFace *df, int cset)
1140 DecorFace *tdf;
1141 int has_changed = 0;
1143 for(tdf = df; tdf != NULL; tdf = tdf->next)
1145 if (DFS_FACE_TYPE(tdf->style) == ColorsetButton &&
1146 tdf->u.acs.cs == cset)
1148 tdf->flags.has_changed = 1;
1149 has_changed = 1;
1151 else if (DFS_FACE_TYPE(tdf->style) == MultiPixmap)
1153 int i;
1155 for (i = 0; i < TBMP_NUM_PIXMAPS; i++)
1157 if (tdf->u.mp.acs[i].cs == cset)
1159 tdf->flags.has_changed = 1;
1160 has_changed = 1;
1166 return has_changed;
1169 static
1170 int update_titlebutton_colorset(TitleButton *tb, int cset)
1172 int i;
1173 int has_changed = 0;
1175 for(i = 0; i < BS_MaxButtonState; i++)
1177 tb->state[i].flags.has_changed =
1178 update_decorface_colorset(&(tb->state[i]), cset);
1179 has_changed |= tb->state[i].flags.has_changed;
1181 return has_changed;
1184 static
1185 void update_decors_colorset(int cset)
1187 int i;
1188 int has_changed;
1189 FvwmDecor *decor = &Scr.DefaultDecor;
1191 #ifdef USEDECOR
1192 for(decor = &Scr.DefaultDecor; decor != NULL; decor = decor->next)
1193 #endif
1195 has_changed = 0;
1196 for(i = 0; i < NUMBER_OF_TITLE_BUTTONS; i++)
1198 decor->flags.has_changed |= update_titlebutton_colorset(
1199 &(decor->buttons[i]), cset);
1201 decor->flags.has_changed |= update_titlebutton_colorset(
1202 &(decor->titlebar), cset);
1203 decor->flags.has_changed |= update_decorface_colorset(
1204 &(decor->BorderStyle.active), cset);
1205 decor->flags.has_changed |= update_decorface_colorset(
1206 &(decor->BorderStyle.inactive), cset);
1207 if (decor->flags.has_changed)
1209 Scr.flags.do_need_window_update = 1;
1214 static Bool __parse_vector_line_one_coord(
1215 char **ret_action, int *pcoord, int *poff, char *action)
1217 int offset;
1218 int n;
1220 *ret_action = action;
1221 n = sscanf(action, "%d%n", pcoord, &offset);
1222 if (n < 1)
1224 return False;
1226 action += offset;
1227 /* check for offest */
1228 if (*action == '+' || *action == '-')
1230 n = sscanf(action, "%dp%n", poff, &offset);
1231 if (n < 1)
1233 return False;
1235 if (*poff < -128)
1237 *poff = -128;
1239 else if (*poff > 127)
1241 *poff = 127;
1243 action += offset;
1245 else
1247 *poff = 0;
1249 *ret_action = action;
1251 return True;
1254 static Bool __parse_vector_line(
1255 char **ret_action, int *px, int *py, int *pxoff, int *pyoff, int *pc,
1256 char *action)
1258 Bool is_valid = True;
1259 int offset;
1260 int n;
1262 *ret_action = action;
1263 if (__parse_vector_line_one_coord(&action, px, pxoff, action) == False)
1265 return False;
1267 if (*action != 'x')
1269 return False;
1271 action++;
1272 if (__parse_vector_line_one_coord(&action, py, pyoff, action) == False)
1274 return False;
1276 if (*action != '@')
1278 return False;
1280 action++;
1281 /* read the line style */
1282 n = sscanf(action, "%d%n", pc, &offset);
1283 if (n < 1)
1285 return False;
1287 action += offset;
1288 *ret_action = action;
1290 return is_valid;
1293 /* ---------------------------- interface functions ------------------------ */
1295 void refresh_window(Window w, Bool window_update)
1297 XSetWindowAttributes attributes;
1298 unsigned long valuemask;
1300 valuemask = CWOverrideRedirect | CWBackingStore | CWSaveUnder |
1301 CWBackPixmap;
1302 attributes.override_redirect = True;
1303 attributes.save_under = False;
1304 attributes.background_pixmap = None;
1305 attributes.backing_store = NotUseful;
1306 w = XCreateWindow(
1307 dpy, w, 0, 0, Scr.MyDisplayWidth, Scr.MyDisplayHeight, 0,
1308 CopyFromParent, CopyFromParent, CopyFromParent, valuemask,
1309 &attributes);
1310 XMapWindow(dpy, w);
1311 if (Scr.flags.do_need_window_update && window_update)
1313 flush_window_updates();
1315 XDestroyWindow(dpy, w);
1316 XSync(dpy, 0);
1317 handle_all_expose();
1319 return;
1322 void ApplyDefaultFontAndColors(void)
1324 XGCValues gcv;
1325 unsigned long gcm;
1326 int cset = Scr.DefaultColorset;
1328 /* make GC's */
1329 gcm = GCFunction|GCLineWidth|GCForeground|GCBackground;
1330 gcv.function = GXcopy;
1331 if (Scr.DefaultFont->font)
1333 gcm |= GCFont;
1334 gcv.font = Scr.DefaultFont->font->fid;
1336 gcv.line_width = 0;
1337 if (cset >= 0)
1339 gcv.foreground = Colorset[cset].fg;
1340 gcv.background = Colorset[cset].bg;
1342 else
1344 gcv.foreground = Scr.StdFore;
1345 gcv.background = Scr.StdBack;
1347 if (Scr.StdGC)
1349 XChangeGC(dpy, Scr.StdGC, gcm, &gcv);
1351 else
1353 Scr.StdGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1356 gcm = GCFunction|GCLineWidth|GCForeground;
1357 if (cset >= 0)
1359 gcv.foreground = Colorset[cset].hilite;
1361 else
1363 gcv.foreground = Scr.StdHilite;
1365 if (Scr.StdReliefGC)
1367 XChangeGC(dpy, Scr.StdReliefGC, gcm, &gcv);
1369 else
1371 Scr.StdReliefGC = fvwmlib_XCreateGC(
1372 dpy, Scr.NoFocusWin, gcm, &gcv);
1374 if (cset >= 0)
1376 gcv.foreground = Colorset[cset].shadow;
1378 else
1380 gcv.foreground = Scr.StdShadow;
1382 if (Scr.StdShadowGC)
1384 XChangeGC(dpy, Scr.StdShadowGC, gcm, &gcv);
1386 else
1388 Scr.StdShadowGC = fvwmlib_XCreateGC(
1389 dpy, Scr.NoFocusWin, gcm, &gcv);
1391 /* update the geometry window for move/resize */
1392 if (Scr.SizeWindow != None)
1394 resize_geometry_window();
1396 UpdateAllMenuStyles();
1398 return;
1401 void FreeDecorFace(Display *dpy, DecorFace *df)
1403 int i;
1405 switch (DFS_FACE_TYPE(df->style))
1407 case GradientButton:
1408 if (df->u.grad.d_pixels != NULL && df->u.grad.d_npixels)
1410 PictureFreeColors(
1411 dpy, Pcmap, df->u.grad.d_pixels,
1412 df->u.grad.d_npixels, 0, False);
1413 free(df->u.grad.d_pixels);
1415 else if (Pdepth <= 8 && df->u.grad.xcs != NULL &&
1416 df->u.grad.npixels > 0 && !df->u.grad.do_dither)
1418 Pixel *p;
1419 int i;
1421 p = (Pixel *)safemalloc(
1422 df->u.grad.npixels * sizeof(Pixel));
1423 for(i=0; i < df->u.grad.npixels; i++)
1425 p[i] = df->u.grad.xcs[i].pixel;
1427 PictureFreeColors(
1428 dpy, Pcmap, p, df->u.grad.npixels, 0, False);
1429 free(p);
1431 if (df->u.grad.xcs != NULL)
1433 free(df->u.grad.xcs);
1435 break;
1437 case PixmapButton:
1438 case TiledPixmapButton:
1439 case StretchedPixmapButton:
1440 case AdjustedPixmapButton:
1441 case ShrunkPixmapButton:
1442 if (df->u.p)
1444 PDestroyFvwmPicture(dpy, df->u.p);
1446 break;
1448 case MultiPixmap:
1449 if (df->u.mp.pixmaps)
1451 for (i = 0; i < TBMP_NUM_PIXMAPS; i++)
1453 if (df->u.mp.pixmaps[i])
1455 PDestroyFvwmPicture(
1456 dpy, df->u.mp.pixmaps[i]);
1458 else if (!!(df->u.mp.solid_flags & i))
1460 PictureFreeColors(
1461 dpy, Pcmap, &df->u.mp.pixels[i],
1462 1, 0, False);
1465 free(df->u.mp.pixmaps);
1467 if (df->u.mp.acs)
1469 free(df->u.mp.acs);
1471 if (df->u.mp.pixels)
1473 free(df->u.mp.pixels);
1475 break;
1476 case VectorButton:
1477 case DefaultVectorButton:
1478 if (df->u.vector.x)
1480 free (df->u.vector.x);
1482 if (df->u.vector.y)
1484 free (df->u.vector.y);
1486 /* free offsets for coord */
1487 if (df->u.vector.xoff)
1489 free(df->u.vector.xoff);
1491 if (df->u.vector.yoff)
1493 free(df->u.vector.yoff);
1495 if (df->u.vector.c)
1497 free (df->u.vector.c);
1499 break;
1500 default:
1501 /* see below */
1502 break;
1504 /* delete any compound styles */
1505 if (df->next)
1507 FreeDecorFace(dpy, df->next);
1508 free(df->next);
1510 df->next = NULL;
1511 memset(&df->style, 0, sizeof(df->style));
1512 memset(&df->u, 0, sizeof(df->u));
1513 DFS_FACE_TYPE(df->style) = SimpleButton;
1515 return;
1520 * Reads a button face line into a structure (veliaa@rpi.edu)
1523 Bool ReadDecorFace(char *s, DecorFace *df, int button, int verbose)
1525 int offset;
1526 char style[256], *file;
1527 char *action = s;
1529 /* some variants of scanf do not increase the assign count when %n is
1530 * used, so a return value of 1 is no error. */
1531 if (sscanf(s, "%255s%n", style, &offset) < 1)
1533 if (verbose)
1535 fvwm_msg(ERR, "ReadDecorFace", "error in face `%s'", s);
1537 return False;
1539 style[255] = 0;
1541 if (strncasecmp(style, "--", 2) != 0)
1543 s += offset;
1545 FreeDecorFace(dpy, df);
1547 /* determine button style */
1548 if (strncasecmp(style,"Simple",6)==0)
1550 memset(&df->style, 0, sizeof(df->style));
1551 DFS_FACE_TYPE(df->style) = SimpleButton;
1553 else if (strncasecmp(style,"Default",7)==0)
1555 int b = -1, n = sscanf(s, "%d%n", &b, &offset);
1557 if (n < 1)
1559 if (button == -1)
1561 if (verbose)
1563 fvwm_msg(
1564 ERR,"ReadDecorFace",
1565 "need default button"
1566 " number to load");
1568 return False;
1570 b = button;
1572 else
1574 b = BUTTON_INDEX(b);
1575 s += offset;
1577 if (b >= 0 && b < NUMBER_OF_TITLE_BUTTONS)
1579 LoadDefaultButton(df, b);
1581 else
1583 if (verbose)
1585 fvwm_msg(
1586 ERR, "ReadDecorFace",
1587 "button number out of range:"
1588 " %d", b);
1590 return False;
1593 else if (strncasecmp(style,"Vector",6)==0 ||
1594 (strlen(style)<=2 && isdigit(*style)))
1596 /* normal coordinate list button style */
1597 int i, num_coords, num;
1598 struct vector_coords *vc = &df->u.vector;
1600 /* get number of points */
1601 if (strncasecmp(style,"Vector",6)==0)
1603 num = sscanf(s,"%d%n",&num_coords,&offset);
1604 s += offset;
1606 else
1608 num = sscanf(style,"%d",&num_coords);
1611 if (num < 1 || num_coords<2 ||
1612 num_coords > MAX_TITLE_BUTTON_VECTOR_LINES)
1614 if (verbose)
1616 fvwm_msg(
1617 ERR, "ReadDecorFace",
1618 "Bad button style (2) in line:"
1619 " %s",action);
1621 return False;
1624 vc->num = num_coords;
1625 vc->use_fgbg = 0;
1626 vc->x = (signed char*)safemalloc(sizeof(char) *
1627 num_coords);
1628 vc->y = (signed char*)safemalloc(sizeof(char) *
1629 num_coords);
1630 vc->xoff = (signed char*)safemalloc(sizeof(char) *
1631 num_coords);
1632 vc->yoff = (signed char*)safemalloc(sizeof(char) *
1633 num_coords);
1634 vc->c = (signed char*)safemalloc(sizeof(char) *
1635 num_coords);
1637 /* get the points */
1638 for (i = 0; i < vc->num; ++i)
1640 int x;
1641 int y;
1642 int xoff = 0;
1643 int yoff = 0;
1644 int c;
1646 if (__parse_vector_line(
1647 &s, &x, &y, &xoff, &yoff, &c, s) ==
1648 False)
1650 break;
1652 if (x < 0)
1654 x = 0;
1656 if (x > 100)
1658 x = 100;
1660 if (y < 0)
1662 y = 0;
1664 if (y > 100)
1666 y = 100;
1668 if (c < 0 || c > 4)
1670 c = 4;
1672 vc->x[i] = x;
1673 vc->y[i] = y;
1674 vc->c[i] = c;
1675 vc->xoff[i] = xoff;
1676 vc->yoff[i] = yoff;
1677 if (c == 2 || c == 3)
1679 vc->use_fgbg = 1;
1682 if (i < vc->num)
1684 if (verbose)
1686 fvwm_msg(
1687 ERR, "ReadDecorFace",
1688 "Bad button style (3) in line"
1689 " %s", action);
1691 free(vc->x);
1692 free(vc->y);
1693 free(vc->c);
1694 free(vc->xoff);
1695 free(vc->yoff);
1696 vc->x = NULL;
1697 vc->y = NULL;
1698 vc->c = NULL;
1699 vc->xoff = NULL;
1700 vc->yoff = NULL;
1701 return False;
1703 memset(&df->style, 0, sizeof(df->style));
1704 DFS_FACE_TYPE(df->style) = VectorButton;
1706 else if (strncasecmp(style,"Solid",5)==0)
1708 s = GetNextToken(s, &file);
1709 if (file)
1711 memset(&df->style, 0, sizeof(df->style));
1712 DFS_FACE_TYPE(df->style) = SolidButton;
1713 df->u.back = GetColor(file);
1714 free(file);
1716 else
1718 if (verbose)
1720 fvwm_msg(
1721 ERR, "ReadDecorFace",
1722 "no color given for Solid"
1723 " face type: %s", action);
1725 return False;
1728 else if (strncasecmp(style+1, "Gradient", 8)==0)
1730 char **s_colors;
1731 int npixels, nsegs, *perc;
1732 XColor *xcs;
1733 Bool do_dither = False;
1735 if (!IsGradientTypeSupported(style[0]))
1737 return False;
1739 /* translate the gradient string into an array of
1740 * colors etc */
1741 npixels = ParseGradient(
1742 s, &s, &s_colors, &perc, &nsegs);
1743 while (*s && isspace(*s))
1745 s++;
1747 if (npixels <= 0)
1749 return False;
1751 /* grab the colors */
1752 if (Pdepth <= 16)
1754 do_dither = True;
1756 xcs = AllocAllGradientColors(
1757 s_colors, perc, nsegs, npixels, do_dither);
1758 if (xcs == None)
1759 return False;
1760 df->u.grad.xcs = xcs;
1761 df->u.grad.npixels = npixels;
1762 df->u.grad.do_dither = do_dither;
1763 df->u.grad.d_pixels = NULL;
1764 memset(&df->style, 0, sizeof(df->style));
1765 DFS_FACE_TYPE(df->style) = GradientButton;
1766 df->u.grad.gradient_type = toupper(style[0]);
1768 else if (strncasecmp(style,"Pixmap",6)==0
1769 || strncasecmp(style,"TiledPixmap",11)==0
1770 || strncasecmp(style,"StretchedPixmap",15)==0
1771 || strncasecmp(style,"AdjustedPixmap",14)==0
1772 || strncasecmp(style,"ShrunkPixmap",12)==0)
1774 FvwmPictureAttributes fpa;
1776 s = GetNextToken(s, &file);
1777 fpa.mask = (Pdepth <= 8)? FPAM_DITHER:0; /* ? */
1778 df->u.p = PCacheFvwmPicture(
1779 dpy, Scr.NoFocusWin, NULL, file, fpa);
1780 if (df->u.p == NULL)
1782 if (file)
1784 if (verbose)
1786 fvwm_msg(
1787 ERR, "ReadDecorFace",
1788 "couldn't load pixmap"
1789 " %s", file);
1791 free(file);
1793 return False;
1795 if (file)
1797 free(file);
1798 file = NULL;
1801 memset(&df->style, 0, sizeof(df->style));
1802 if (strncasecmp(style,"Tiled",5)==0)
1804 DFS_FACE_TYPE(df->style) = TiledPixmapButton;
1806 else if (strncasecmp(style,"Stretched",9)==0)
1808 DFS_FACE_TYPE(df->style) =
1809 StretchedPixmapButton;
1811 else if (strncasecmp(style,"Adjusted",8)==0)
1813 DFS_FACE_TYPE(df->style) =
1814 AdjustedPixmapButton;
1816 else if (strncasecmp(style,"Shrunk",6)==0)
1818 DFS_FACE_TYPE(df->style) =
1819 ShrunkPixmapButton;
1821 else
1823 DFS_FACE_TYPE(df->style) = PixmapButton;
1826 else if (strncasecmp(style,"MultiPixmap",11)==0)
1828 if (button != -1)
1830 if (verbose)
1832 fvwm_msg(
1833 ERR, "ReadDecorFace",
1834 "MultiPixmap is only valid"
1835 " for TitleStyle");
1837 return False;
1839 s = ReadMultiPixmapDecor(s, df);
1840 if (!s)
1842 return False;
1845 else if (FMiniIconsSupported &&
1846 strncasecmp (style, "MiniIcon", 8) == 0)
1848 memset(&df->style, 0, sizeof(df->style));
1849 DFS_FACE_TYPE(df->style) = MiniIconButton;
1850 /* pixmap read in when the window is created */
1851 df->u.p = NULL;
1853 else if (strncasecmp (style, "Colorset", 8) == 0)
1855 int val[2];
1856 int n;
1858 n = GetIntegerArguments(s, NULL, val, 2);
1859 if (n == 0)
1862 memset(&df->style, 0, sizeof(df->style));
1863 if (n > 0 && val[0] >= 0)
1866 df->u.acs.cs = val[0];
1867 alloc_colorset(val[0]);
1868 DFS_FACE_TYPE(df->style) = ColorsetButton;
1870 df->u.acs.alpha_percent = 100;
1871 if (n > 1)
1873 df->u.acs.alpha_percent =
1874 max(0, min(100,val[1]));
1876 s = SkipNTokens(s, n);
1878 else
1880 if (verbose)
1882 fvwm_msg(
1883 ERR, "ReadDecorFace",
1884 "unknown style %s: %s", style, action);
1886 return False;
1890 /* Process button flags ("--" signals start of flags,
1891 it is also checked for above) */
1892 s = GetNextToken(s, &file);
1893 if (file && (strcmp(file,"--")==0))
1895 char *tok;
1896 s = GetNextToken(s, &tok);
1897 while (tok && *tok)
1899 int set = 1;
1900 char *old_tok = NULL;
1902 if (*tok == '!')
1903 { /* flag negate */
1904 set = 0;
1905 old_tok = tok;
1906 tok++;
1908 if (StrEquals(tok,"Clear"))
1910 memset(&DFS_FLAGS(df->style), (set) ? 0 : 0xff,
1911 sizeof(DFS_FLAGS(df->style)));
1912 /* ? what is set == 0 good for ? */
1914 else if (StrEquals(tok,"Left"))
1916 if (set)
1918 DFS_H_JUSTIFICATION(df->style) =
1919 JUST_LEFT;
1921 else
1923 DFS_H_JUSTIFICATION(df->style) =
1924 JUST_RIGHT;
1927 else if (StrEquals(tok,"Right"))
1929 if (set)
1931 DFS_H_JUSTIFICATION(df->style) =
1932 JUST_RIGHT;
1934 else
1936 DFS_H_JUSTIFICATION(df->style) =
1937 JUST_LEFT;
1940 else if (StrEquals(tok,"Centered"))
1942 DFS_H_JUSTIFICATION(df->style) = JUST_CENTER;
1943 DFS_V_JUSTIFICATION(df->style) = JUST_CENTER;
1945 else if (StrEquals(tok,"Top"))
1947 if (set)
1949 DFS_V_JUSTIFICATION(df->style) =
1950 JUST_TOP;
1952 else
1954 DFS_V_JUSTIFICATION(df->style) =
1955 JUST_BOTTOM;
1958 else if (StrEquals(tok,"Bottom"))
1960 if (set)
1962 DFS_V_JUSTIFICATION(df->style) =
1963 JUST_BOTTOM;
1965 else
1967 DFS_V_JUSTIFICATION(df->style) =
1968 JUST_TOP;
1971 else if (StrEquals(tok,"Flat"))
1973 if (set)
1975 DFS_BUTTON_RELIEF(df->style) =
1976 DFS_BUTTON_IS_FLAT;
1978 else if (DFS_BUTTON_RELIEF(df->style) ==
1979 DFS_BUTTON_IS_FLAT)
1981 DFS_BUTTON_RELIEF(df->style) =
1982 DFS_BUTTON_IS_UP;
1985 else if (StrEquals(tok,"Sunk"))
1987 if (set)
1989 DFS_BUTTON_RELIEF(df->style) =
1990 DFS_BUTTON_IS_SUNK;
1992 else if (DFS_BUTTON_RELIEF(df->style) ==
1993 DFS_BUTTON_IS_SUNK)
1995 DFS_BUTTON_RELIEF(df->style) =
1996 DFS_BUTTON_IS_UP;
1999 else if (StrEquals(tok,"Raised"))
2001 if (set)
2003 DFS_BUTTON_RELIEF(df->style) =
2004 DFS_BUTTON_IS_UP;
2006 else
2008 DFS_BUTTON_RELIEF(df->style) =
2009 DFS_BUTTON_IS_SUNK;
2012 else if (StrEquals(tok,"UseTitleStyle"))
2014 if (set)
2016 DFS_USE_TITLE_STYLE(df->style) = 1;
2017 DFS_USE_BORDER_STYLE(df->style) = 0;
2019 else
2020 DFS_USE_TITLE_STYLE(df->style) = 0;
2022 else if (StrEquals(tok,"HiddenHandles"))
2024 DFS_HAS_HIDDEN_HANDLES(df->style) = !!set;
2026 else if (StrEquals(tok,"NoInset"))
2028 DFS_HAS_NO_INSET(df->style) = !!set;
2030 else if (StrEquals(tok,"UseBorderStyle"))
2032 if (set)
2034 DFS_USE_BORDER_STYLE(df->style) = 1;
2035 DFS_USE_TITLE_STYLE(df->style) = 0;
2037 else
2039 DFS_USE_BORDER_STYLE(df->style) = 0;
2042 else if (verbose)
2044 fvwm_msg(
2045 ERR, "ReadDecorFace",
2046 "unknown button face flag '%s'"
2047 " -- line: %s", tok, action);
2049 if (set)
2051 free(tok);
2053 else
2055 free(old_tok);
2057 s = GetNextToken(s, &tok);
2060 if (file)
2062 free(file);
2065 return True;
2068 #ifdef USEDECOR
2071 * Diverts a style definition to an FvwmDecor structure (veliaa@rpi.edu)
2074 void AddToDecor(F_CMD_ARGS, FvwmDecor *decor)
2076 if (!action)
2078 return;
2080 while (*action && isspace((unsigned char)*action))
2082 ++action;
2084 if (!*action)
2086 return;
2088 Scr.cur_decor = decor;
2089 execute_function(cond_rc, exc, action, 0);
2090 Scr.cur_decor = NULL;
2092 return;
2097 * InitFvwmDecor -- initializes an FvwmDecor structure to defaults
2100 void InitFvwmDecor(FvwmDecor *decor)
2102 int i;
2103 DecorFace tmpdf;
2105 /* zero out the structures */
2106 memset(decor, 0, sizeof (FvwmDecor));
2107 memset(&tmpdf, 0, sizeof(DecorFace));
2109 /* initialize title-bar button styles */
2110 DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
2111 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
2113 int j = 0;
2114 for (; j < BS_MaxButtonState; ++j)
2116 TB_STATE(decor->buttons[i])[j] = tmpdf;
2119 /* reset to default button set */
2120 ResetAllButtons(decor);
2121 /* initialize title-bar styles */
2122 for (i = 0; i < BS_MaxButtonState; ++i)
2124 DFS_FACE_TYPE(
2125 TB_STATE(decor->titlebar)[i].style) = SimpleButton;
2128 /* initialize border texture styles */
2129 DFS_FACE_TYPE(decor->BorderStyle.active.style) = SimpleButton;
2130 DFS_FACE_TYPE(decor->BorderStyle.inactive.style) = SimpleButton;
2132 return;
2135 void reset_decor_changes(void)
2137 #ifndef USEDECOR
2138 Scr.DefaultDecor.flags.has_changed = 0;
2139 Scr.DefaultDecor.flags.has_title_height_changed = 0;
2140 #else
2141 FvwmDecor *decor;
2142 for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
2144 decor->flags.has_changed = 0;
2145 decor->flags.has_title_height_changed = 0;
2147 /* todo: must reset individual change flags too */
2148 #endif
2150 return;
2153 void update_fvwm_colorset(int cset)
2155 if (cset == Scr.DefaultColorset)
2157 Scr.flags.do_need_window_update = 1;
2158 Scr.flags.has_default_color_changed = 1;
2160 UpdateMenuColorset(cset);
2161 update_style_colorset(cset);
2162 update_decors_colorset(cset);
2164 return;
2167 /* ---------------------------- builtin commands --------------------------- */
2169 void CMD_Beep(F_CMD_ARGS)
2171 #if 1 /*!!!*/
2172 parse_colorset(11, "RootTransparent");
2173 #endif
2174 XBell(dpy, 0);
2176 return;
2179 void CMD_Nop(F_CMD_ARGS)
2181 return;
2184 void CMD_EscapeFunc(F_CMD_ARGS)
2186 return;
2189 void CMD_CursorMove(F_CMD_ARGS)
2191 int x = 0, y = 0;
2192 int val1, val2, val1_unit, val2_unit;
2193 int virtual_x, virtual_y;
2194 int pan_x, pan_y;
2195 int x_pages, y_pages;
2197 if (GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit) != 2)
2199 fvwm_msg(ERR, "movecursor", "CursorMove needs 2 arguments");
2200 return;
2202 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,
2203 &x, &y, &JunkX, &JunkY, &JunkMask) == False)
2205 /* pointer is on a different screen */
2206 return;
2208 x = x + val1 * val1_unit / 100;
2209 y = y + val2 * val2_unit / 100;
2210 virtual_x = Scr.Vx;
2211 virtual_y = Scr.Vy;
2212 if (x >= 0)
2214 x_pages = x / Scr.MyDisplayWidth;
2216 else
2218 x_pages = ((x + 1) / Scr.MyDisplayWidth) - 1;
2220 virtual_x += x_pages * Scr.MyDisplayWidth;
2221 x -= x_pages * Scr.MyDisplayWidth;
2222 if (virtual_x < 0)
2224 x += virtual_x;
2225 virtual_x = 0;
2227 else if (virtual_x > Scr.VxMax)
2229 x += virtual_x - Scr.VxMax;
2230 virtual_x = Scr.VxMax;
2233 if (y >= 0)
2235 y_pages = y / Scr.MyDisplayHeight;
2237 else
2239 y_pages = ((y + 1) / Scr.MyDisplayHeight) - 1;
2241 virtual_y += y_pages * Scr.MyDisplayHeight;
2242 y -= y_pages * Scr.MyDisplayHeight;
2243 if (virtual_y < 0)
2245 y += virtual_y;
2246 virtual_y = 0;
2248 else if (virtual_y > Scr.VyMax)
2250 y += virtual_y - Scr.VyMax;
2251 virtual_y = Scr.VyMax;
2253 if (virtual_x != Scr.Vx || virtual_y != Scr.Vy)
2254 MoveViewport(virtual_x, virtual_y, True);
2255 pan_x = (Scr.EdgeScrollX != 0) ? 2 : 0;
2256 pan_y = (Scr.EdgeScrollY != 0) ? 2 : 0;
2257 /* prevent paging if EdgeScroll is active */
2258 if (x >= Scr.MyDisplayWidth - pan_x)
2260 x = Scr.MyDisplayWidth - pan_x -1;
2262 else if (x < pan_x)
2264 x = pan_x;
2266 if (y >= Scr.MyDisplayHeight - pan_y)
2268 y = Scr.MyDisplayHeight - pan_y - 1;
2270 else if (y < pan_y)
2272 y = pan_y;
2274 FWarpPointerUpdateEvpos(
2275 exc->x.elast, dpy, None, Scr.Root, 0, 0, Scr.MyDisplayWidth,
2276 Scr.MyDisplayHeight, x, y);
2278 return;
2281 void CMD_Delete(F_CMD_ARGS)
2283 FvwmWindow * const fw = exc->w.fw;
2285 if (!is_function_allowed(F_DELETE, NULL, fw, RQORIG_PROGRAM_US, True))
2287 XBell(dpy, 0);
2289 return;
2291 if (IS_TEAR_OFF_MENU(fw))
2293 /* 'soft' delete tear off menus. Note: we can't send the
2294 * message to the menu window directly because it was created
2295 * using a different display. The client message would never
2296 * be read from there. */
2297 send_clientmessage(
2298 dpy, FW_W_PARENT(fw), _XA_WM_DELETE_WINDOW,
2299 CurrentTime);
2301 return;
2303 if (WM_DELETES_WINDOW(fw))
2305 send_clientmessage(
2306 dpy, FW_W(fw), _XA_WM_DELETE_WINDOW, CurrentTime);
2308 return;
2310 else
2312 XBell(dpy, 0);
2314 XFlush(dpy);
2316 return;
2319 void CMD_Destroy(F_CMD_ARGS)
2321 FvwmWindow * const fw = exc->w.fw;
2323 if (IS_TEAR_OFF_MENU(fw))
2325 CMD_Delete(F_PASS_ARGS);
2326 return;
2328 if (!is_function_allowed(F_DESTROY, NULL, fw, True, True))
2330 XBell(dpy, 0);
2331 return;
2333 if (
2334 XGetGeometry(
2335 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
2336 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
2337 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth)
2338 != 0)
2340 XKillClient(dpy, FW_W(fw));
2342 destroy_window(fw);
2343 XFlush(dpy);
2345 return;
2348 void CMD_Close(F_CMD_ARGS)
2350 FvwmWindow * const fw = exc->w.fw;
2352 if (IS_TEAR_OFF_MENU(fw))
2354 CMD_Delete(F_PASS_ARGS);
2355 return;
2357 if (!is_function_allowed(F_CLOSE, NULL, fw, True, True))
2359 XBell(dpy, 0);
2360 return;
2362 if (WM_DELETES_WINDOW(fw))
2364 send_clientmessage(
2365 dpy, FW_W(fw), _XA_WM_DELETE_WINDOW, CurrentTime);
2366 return;
2368 if (
2369 XGetGeometry(
2370 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
2371 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
2372 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth)
2373 != 0)
2375 XKillClient(dpy, FW_W(fw));
2378 destroy_window(fw);
2379 XFlush(dpy);
2381 return;
2384 void CMD_Restart(F_CMD_ARGS)
2386 Done(1, action);
2388 return;
2391 void CMD_ExecUseShell(F_CMD_ARGS)
2393 char *arg=NULL;
2394 static char shell_set = 0;
2396 if (shell_set)
2398 free(exec_shell_name);
2400 shell_set = 1;
2401 action = GetNextToken(action,&arg);
2402 if (arg) /* specific shell was specified */
2404 exec_shell_name = arg;
2406 else /* no arg, so use $SHELL -- not working??? */
2408 if (getenv("SHELL"))
2410 exec_shell_name = safestrdup(getenv("SHELL"));
2412 else
2414 /* if $SHELL not set, use default */
2415 exec_shell_name = safestrdup("/bin/sh");
2420 void CMD_Exec(F_CMD_ARGS)
2422 char *cmd=NULL;
2424 /* if it doesn't already have an 'exec' as the first word, add that
2425 * to keep down number of procs started */
2426 /* need to parse string better to do this right though, so not doing
2427 * this for now... */
2428 #if 0
2429 if (strncasecmp(action,"exec",4)!=0)
2431 cmd = (char *)safemalloc(strlen(action)+6);
2432 strcpy(cmd,"exec ");
2433 strcat(cmd,action);
2435 else
2436 #endif
2438 cmd = safestrdup(action);
2440 if (!cmd)
2442 return;
2444 /* Use to grab the pointer here, but the fork guarantees that
2445 * we wont be held up waiting for the function to finish,
2446 * so the pointer-gram just caused needless delay and flashing
2447 * on the screen */
2448 /* Thought I'd try vfork and _exit() instead of regular fork().
2449 * The man page says that its better. */
2450 /* Not everyone has vfork! */
2451 /* According to the man page, vfork should never be used at all.
2454 if (!(fork())) /* child process */
2456 /* This is for fixing a problem with rox filer */
2457 int fd;
2459 fvmm_deinstall_signals();
2460 fd = open("/dev/null", O_RDONLY, 0);
2461 dup2(fd,STDIN_FILENO);
2462 if (fvwm_setpgrp() == -1)
2464 fvwm_msg(ERR, "exec_function", "setpgrp failed (%s)",
2465 strerror(errno));
2466 exit(100);
2468 if (execl(exec_shell_name, exec_shell_name, "-c", cmd, NULL) ==
2471 fvwm_msg(ERR, "exec_function", "execl failed (%s)",
2472 strerror(errno));
2473 exit(100);
2476 free(cmd);
2478 return;
2481 void CMD_Refresh(F_CMD_ARGS)
2483 refresh_window(Scr.Root, True);
2485 return;
2488 void CMD_RefreshWindow(F_CMD_ARGS)
2490 FvwmWindow * const fw = exc->w.fw;
2492 refresh_window(
2493 (exc->w.wcontext == C_ICON) ?
2494 FW_W_ICON_TITLE(fw) : FW_W_FRAME(fw), True);
2496 return;
2499 void CMD_Wait(F_CMD_ARGS)
2501 Bool done = False;
2502 Bool redefine_cursor = False;
2503 Bool is_ungrabbed;
2504 char *escape;
2505 Window nonewin = None;
2506 char *wait_string, *rest;
2507 FvwmWindow *t;
2509 /* try to get a single token */
2510 rest = GetNextToken(action, &wait_string);
2511 if (wait_string)
2513 while (*rest && isspace((unsigned char)*rest))
2515 rest++;
2517 if (*rest)
2519 int i;
2520 char *temp;
2522 /* nope, multiple tokens - try old syntax */
2524 /* strip leading and trailing whitespace */
2525 temp = action;
2526 while (*temp && isspace((unsigned char)*temp))
2528 temp++;
2530 wait_string = safestrdup(temp);
2531 for (i = strlen(wait_string) - 1; i >= 0 &&
2532 isspace(wait_string[i]); i--)
2534 wait_string[i] = 0;
2538 else
2540 wait_string = safestrdup("");
2543 is_ungrabbed = UngrabEm(GRAB_NORMAL);
2544 while (!done && !isTerminated)
2546 XEvent e;
2547 if (BUSY_WAIT & Scr.BusyCursor)
2549 XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_WAIT]);
2550 redefine_cursor = True;
2552 if (My_XNextEvent(dpy, &e))
2554 dispatch_event(&e);
2555 if (XFindContext(
2556 dpy, e.xmap.window, FvwmContext,
2557 (caddr_t *)&t) == XCNOENT)
2559 t = NULL;
2562 if (e.type == MapNotify && e.xmap.event == Scr.Root)
2564 if (!*wait_string)
2566 done = True;
2568 if (t && matchWildcards(
2569 wait_string, t->name.name) == True)
2571 done = True;
2573 else if (t && t->class.res_class &&
2574 matchWildcards(
2575 wait_string,
2576 t->class.res_class) == True)
2578 done = True;
2580 else if (t && t->class.res_name &&
2581 matchWildcards(
2582 wait_string,
2583 t->class.res_name) == True)
2585 done = True;
2588 else if (e.type == KeyPress)
2590 /* should I be using <t> or <exc->w.fw>?
2591 * DV: t
2593 int context;
2594 XClassHint *class;
2595 char *name;
2597 context = GetContext(&t, t, &e, &nonewin);
2598 if (t != NULL)
2600 class = &(t->class);
2601 name = t->name.name;
2603 else
2605 class = NULL;
2606 name = NULL;
2608 escape = CheckBinding(
2609 Scr.AllBindings, STROKE_ARG(0)
2610 e.xkey.keycode, e.xkey.state,
2611 GetUnusedModifiers(), context,
2612 BIND_KEYPRESS, class, name);
2613 if (escape != NULL)
2615 if (!strcasecmp(escape,"escapefunc"))
2617 done = True;
2623 if (redefine_cursor)
2625 XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_ROOT]);
2627 if (is_ungrabbed)
2629 GrabEm(CRS_NONE, GRAB_NORMAL);
2631 free(wait_string);
2633 return;
2636 void CMD_Quit(F_CMD_ARGS)
2638 if (master_pid != getpid())
2640 kill(master_pid, SIGTERM);
2642 Done(0,NULL);
2644 return;
2647 void CMD_QuitScreen(F_CMD_ARGS)
2649 Done(0,NULL);
2651 return;
2654 void CMD_Echo(F_CMD_ARGS)
2656 int len;
2658 if (!action)
2660 action = "";
2662 len = strlen(action);
2663 if (len != 0)
2665 if (action[len-1]=='\n')
2667 action[len-1]='\0';
2670 fvwm_msg(ECHO,"Echo",action);
2672 return;
2675 void CMD_PrintInfo(F_CMD_ARGS)
2677 int verbose;
2678 char *rest, *subject = NULL;
2680 rest = GetNextToken(action, &subject);
2681 if (!rest || GetIntegerArguments(rest, NULL, &verbose, 1) != 1)
2683 verbose = 0;
2685 if (StrEquals(subject, "Colors"))
2687 PicturePrintColorInfo(verbose);
2689 else if (StrEquals(subject, "Locale"))
2691 FlocalePrintLocaleInfo(dpy, verbose);
2693 else if (StrEquals(subject, "NLS"))
2695 FGettextPrintLocalePath(verbose);
2697 else if (StrEquals(subject, "style"))
2699 print_styles(verbose);
2701 else if (StrEquals(subject, "ImageCache"))
2703 PicturePrintImageCache(verbose);
2705 else if (StrEquals(subject, "Bindings"))
2707 print_bindings();
2709 else
2711 fvwm_msg(ERR, "PrintInfo",
2712 "Unknown subject '%s'", action);
2714 if (subject)
2716 free(subject);
2718 return;
2721 void CMD_ColormapFocus(F_CMD_ARGS)
2723 if (MatchToken(action,"FollowsFocus"))
2725 Scr.ColormapFocus = COLORMAP_FOLLOWS_FOCUS;
2727 else if (MatchToken(action,"FollowsMouse"))
2729 Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;
2731 else
2733 fvwm_msg(ERR, "SetColormapFocus",
2734 "ColormapFocus requires 1 arg: FollowsFocus or"
2735 " FollowsMouse");
2736 return;
2739 return;
2742 void CMD_ClickTime(F_CMD_ARGS)
2744 int val;
2746 if (GetIntegerArguments(action, NULL, &val, 1) != 1)
2748 Scr.ClickTime = DEFAULT_CLICKTIME;
2750 else
2752 Scr.ClickTime = (val < 0)? 0 : val;
2755 /* Use a negative value during startup and change sign afterwards. This
2756 * speeds things up quite a bit. */
2757 if (fFvwmInStartup)
2759 Scr.ClickTime = -Scr.ClickTime;
2762 return;
2766 void CMD_ImagePath(F_CMD_ARGS)
2768 PictureSetImagePath( action );
2770 return;
2773 void CMD_IconPath(F_CMD_ARGS)
2775 fvwm_msg(ERR, "iconPath_function",
2776 "IconPath is deprecated since 2.3.0; use ImagePath instead.");
2777 obsolete_imagepaths( action );
2779 return;
2782 void CMD_PixmapPath(F_CMD_ARGS)
2784 fvwm_msg(ERR, "pixmapPath_function",
2785 "PixmapPath is deprecated since 2.3.0; use ImagePath"
2786 " instead." );
2787 obsolete_imagepaths( action );
2789 return;
2792 void CMD_LocalePath(F_CMD_ARGS)
2794 FGettextSetLocalePath( action );
2796 return;
2799 void CMD_ModulePath(F_CMD_ARGS)
2801 static int need_to_free = 0;
2803 setPath( &ModulePath, action, need_to_free );
2804 need_to_free = 1;
2806 return;
2809 void CMD_ModuleTimeout(F_CMD_ARGS)
2811 int timeout;
2813 moduleTimeout = DEFAULT_MODULE_TIMEOUT;
2814 if (GetIntegerArguments(action, NULL, &timeout, 1) == 1 && timeout > 0)
2816 moduleTimeout = timeout;
2819 return;
2822 void CMD_HilightColor(F_CMD_ARGS)
2824 char *fore;
2825 char *back;
2826 #ifdef USEDECOR
2827 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
2829 fvwm_msg(
2830 ERR, "SetHiColor",
2831 "Decors do not support the HilightColor command"
2832 " anymore. Please use"
2833 " 'Style <stylename> HilightFore <forecolor>' and"
2834 " 'Style <stylename> HilightBack <backcolor>' instead."
2835 " Sorry for the inconvenience.");
2836 return;
2838 #endif
2839 action = GetNextToken(action, &fore);
2840 GetNextToken(action, &back);
2841 if (fore && back)
2843 action = safemalloc(strlen(fore) + strlen(back) + 29);
2844 sprintf(action, "* HilightFore %s, HilightBack %s", fore, back);
2845 CMD_Style(F_PASS_ARGS);
2847 if (fore)
2849 free(fore);
2851 if (back)
2853 free(back);
2856 return;
2859 void CMD_HilightColorset(F_CMD_ARGS)
2861 char *newaction;
2863 #ifdef USEDECOR
2864 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
2866 fvwm_msg(
2867 ERR, "SetHiColorset",
2868 "Decors do not support the HilightColorset command "
2869 "anymore. Please use "
2870 "'Style <stylename> HilightColorset <colorset>'"
2871 " instead. Sorry for the inconvenience.");
2872 return;
2874 #endif
2875 if (action)
2877 newaction = safemalloc(strlen(action) + 32);
2878 sprintf(newaction, "* HilightColorset %s", action);
2879 action = newaction;
2880 CMD_Style(F_PASS_ARGS);
2881 free(newaction);
2884 return;
2887 void CMD_TitleStyle(F_CMD_ARGS)
2889 do_title_style(F_PASS_ARGS, False);
2891 return;
2892 } /* SetTitleStyle */
2896 * Appends a titlestyle (veliaa@rpi.edu)
2899 void CMD_AddTitleStyle(F_CMD_ARGS)
2901 do_title_style(F_PASS_ARGS, True);
2903 return;
2906 void CMD_PropertyChange(F_CMD_ARGS)
2908 char string[256];
2909 char *token;
2910 char *rest;
2911 int ret;
2912 unsigned long argument;
2913 unsigned long data1;
2914 unsigned long data2;
2916 /* argument */
2917 token = PeekToken(action, &rest);
2918 if (token == NULL)
2920 return;
2922 ret = sscanf(token, "%lu", &argument);
2923 if (ret < 1)
2925 return;
2927 /* data1 */
2928 data1 = 0;
2929 token = PeekToken(rest, &rest);
2930 if (token != NULL)
2932 ret = sscanf(token, "%lu", &data1);
2933 if (ret < 1)
2935 rest = NULL;
2938 /* data2 */
2939 data2 = 0;
2940 token = PeekToken(rest, &rest);
2941 if (token != NULL)
2943 ret = sscanf(token, "%lu", &data2);
2944 if (ret < 1)
2946 rest = NULL;
2949 /* string */
2950 memset(string, 0, 256);
2951 if (rest != NULL)
2953 ret = sscanf(rest, "%255c", &(string[0]));
2955 BroadcastPropertyChange(argument, data1, data2, string);
2957 return;
2960 void CMD_DefaultIcon(F_CMD_ARGS)
2962 if (Scr.DefaultIcon)
2964 free(Scr.DefaultIcon);
2966 GetNextToken(action, &Scr.DefaultIcon);
2968 return;
2971 void CMD_DefaultColorset(F_CMD_ARGS)
2973 int cset;
2975 if (GetIntegerArguments(action, NULL, &cset, 1) != 1)
2977 return;
2979 Scr.DefaultColorset = cset;
2980 if (Scr.DefaultColorset < 0)
2982 Scr.DefaultColorset = -1;
2984 alloc_colorset(Scr.DefaultColorset);
2985 Scr.flags.do_need_window_update = 1;
2986 Scr.flags.has_default_color_changed = 1;
2988 return;
2991 void CMD_DefaultColors(F_CMD_ARGS)
2993 char *fore = NULL;
2994 char *back = NULL;
2996 action = GetNextToken(action, &fore);
2997 if (action)
2999 action = GetNextToken(action, &back);
3001 if (!back)
3003 back = safestrdup(DEFAULT_BACK_COLOR);
3005 if (!fore)
3007 fore = safestrdup(DEFAULT_FORE_COLOR);
3009 if (!StrEquals(fore, "-"))
3011 PictureFreeColors(dpy, Pcmap, &Scr.StdFore, 1, 0, True);
3012 Scr.StdFore = GetColor(fore);
3014 if (!StrEquals(back, "-"))
3016 PictureFreeColors(dpy, Pcmap, &Scr.StdBack, 3, 0, True);
3017 Scr.StdBack = GetColor(back);
3018 Scr.StdHilite = GetHilite(Scr.StdBack);
3019 Scr.StdShadow = GetShadow(Scr.StdBack);
3021 free(fore);
3022 free(back);
3024 Scr.DefaultColorset = -1;
3025 Scr.flags.do_need_window_update = 1;
3026 Scr.flags.has_default_color_changed = 1;
3028 return;
3031 void CMD_DefaultFont(F_CMD_ARGS)
3033 char *font;
3034 FlocaleFont *new_font;
3035 FvwmWindow *t;
3037 font = PeekToken(action, &action);
3038 if (!font)
3040 /* Try 'fixed', pass NULL font name */
3042 if (!(new_font = FlocaleLoadFont(dpy, font, "fvwm")))
3044 if (Scr.DefaultFont == NULL)
3046 exit(1);
3048 else
3050 return;
3053 FlocaleUnloadFont(dpy, Scr.DefaultFont);
3054 Scr.DefaultFont = new_font;
3055 /* we should do that here because a redraw can happen before
3056 flush_window_updates is called ... */
3057 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3059 if (USING_DEFAULT_ICON_FONT(t))
3061 t->icon_font = Scr.DefaultFont;
3063 if (USING_DEFAULT_WINDOW_FONT(t))
3065 t->title_font = Scr.DefaultFont;
3068 /* set flags to indicate that the font has changed */
3069 Scr.flags.do_need_window_update = 1;
3070 Scr.flags.has_default_font_changed = 1;
3072 return;
3075 void CMD_IconFont(F_CMD_ARGS)
3077 char *newaction;
3079 #ifdef USEDECOR
3080 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
3082 fvwm_msg(
3083 ERR, "LoadIconFont",
3084 "Decors do not support the IconFont command anymore."
3085 " Please use 'Style <stylename> IconFont <fontname>'"
3086 " instead. Sorry for the inconvenience.");
3087 return;
3089 #endif
3090 if (action)
3092 newaction = safemalloc(strlen(action) + 16);
3093 sprintf(newaction, "* IconFont %s", action);
3094 action = newaction;
3095 CMD_Style(F_PASS_ARGS);
3096 free(newaction);
3099 return;
3102 void CMD_WindowFont(F_CMD_ARGS)
3104 char *newaction;
3106 #ifdef USEDECOR
3107 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
3109 fvwm_msg(
3110 ERR, "LoadWindowFont",
3111 "Decors do not support the WindowFont command anymore."
3112 " Please use 'Style <stylename> Font <fontname>'"
3113 " instead. Sorry for the inconvenience.");
3114 return;
3116 #endif
3117 if (action)
3119 newaction = safemalloc(strlen(action) + 16);
3120 sprintf(newaction, "* Font %s", action);
3121 action = newaction;
3122 CMD_Style(F_PASS_ARGS);
3123 free(newaction);
3126 return;
3131 * Changes the window's FvwmDecor pointer (veliaa@rpi.edu)
3134 void CMD_ChangeDecor(F_CMD_ARGS)
3136 char *item;
3137 int old_height;
3138 FvwmDecor *decor = &Scr.DefaultDecor;
3139 FvwmDecor *found = NULL;
3140 FvwmWindow * const fw = exc->w.fw;
3142 item = PeekToken(action, &action);
3143 if (!action || !item)
3145 return;
3147 /* search for tag */
3148 for (; decor; decor = decor->next)
3150 if (decor->tag && StrEquals(item, decor->tag))
3152 found = decor;
3153 break;
3156 if (!found)
3158 XBell(dpy, 0);
3159 return;
3161 SET_DECOR_CHANGED(fw, 1);
3162 old_height = (fw->decor) ? fw->decor->title_height : 0;
3163 fw->decor = found;
3164 apply_decor_change(fw);
3166 return;
3171 * Destroys an FvwmDecor (veliaa@rpi.edu)
3174 void CMD_DestroyDecor(F_CMD_ARGS)
3176 char *item;
3177 FvwmDecor *decor = Scr.DefaultDecor.next;
3178 FvwmDecor *prev = &Scr.DefaultDecor, *found = NULL;
3179 Bool do_recreate = False;
3181 item = PeekToken(action, &action);
3182 if (!item)
3184 return;
3186 if (StrEquals(item, "recreate"))
3188 do_recreate = True;
3189 item = PeekToken(action, NULL);
3191 if (!item)
3193 return;
3196 /* search for tag */
3197 for (; decor; decor = decor->next)
3199 if (decor->tag && StrEquals(item, decor->tag))
3201 found = decor;
3202 break;
3204 prev = decor;
3207 if (found && (found != &Scr.DefaultDecor || do_recreate))
3209 if (!do_recreate)
3211 __remove_window_decors(F_PASS_ARGS, found);
3213 DestroyFvwmDecor(found);
3214 if (do_recreate)
3216 int i;
3218 InitFvwmDecor(found);
3219 found->tag = safestrdup(item);
3220 Scr.flags.do_need_window_update = 1;
3221 found->flags.has_changed = 1;
3222 found->flags.has_title_height_changed = 0;
3223 found->titlebar.flags.has_changed = 1;
3224 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
3226 TB_FLAGS(found->buttons[i]).has_changed = 1;
3229 else
3231 prev->next = found->next;
3232 free(found);
3236 return;
3241 * Initiates an AddToDecor (veliaa@rpi.edu)
3244 void CMD_AddToDecor(F_CMD_ARGS)
3246 FvwmDecor *decor;
3247 FvwmDecor *found = NULL;
3248 char *item = NULL;
3250 action = GetNextToken(action, &item);
3251 if (!item)
3253 return;
3255 if (!action)
3257 free(item);
3258 return;
3260 /* search for tag */
3261 for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
3263 if (decor->tag && StrEquals(item, decor->tag))
3265 found = decor;
3266 break;
3269 if (!found)
3271 /* then make a new one */
3272 found = (FvwmDecor *)safemalloc(sizeof( FvwmDecor ));
3273 InitFvwmDecor(found);
3274 found->tag = item; /* tag it */
3275 /* add it to list */
3276 for (decor = &Scr.DefaultDecor; decor->next;
3277 decor = decor->next)
3279 /* nop */
3281 decor->next = found;
3283 else
3285 free(item);
3288 if (found)
3290 AddToDecor(F_PASS_ARGS, found);
3291 /* Set + state to last decor */
3292 set_last_added_item(ADDED_DECOR, found);
3295 return;
3297 #endif /* USEDECOR */
3302 * Updates window decoration styles (veliaa@rpi.edu)
3305 void CMD_UpdateDecor(F_CMD_ARGS)
3307 FvwmWindow *fw2;
3308 #ifdef USEDECOR
3309 FvwmDecor *decor, *found = NULL;
3310 FvwmWindow *hilight = Scr.Hilite;
3311 char *item = NULL;
3313 action = GetNextToken(action, &item);
3314 if (item)
3316 /* search for tag */
3317 for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
3319 if (decor->tag && StrEquals(item, decor->tag))
3321 found = decor;
3322 break;
3325 free(item);
3327 #endif
3329 for (fw2 = Scr.FvwmRoot.next; fw2; fw2 = fw2->next)
3331 #ifdef USEDECOR
3332 /* update specific decor, or all */
3333 if (found)
3335 if (fw2->decor == found)
3337 border_draw_decorations(
3338 fw2, PART_ALL, True, True, CLEAR_ALL,
3339 NULL, NULL);
3340 border_draw_decorations(
3341 fw2, PART_ALL, False, True, CLEAR_ALL,
3342 NULL, NULL);
3345 else
3346 #endif
3348 border_draw_decorations(
3349 fw2, PART_ALL, True, True, CLEAR_ALL, NULL,
3350 NULL);
3351 border_draw_decorations(
3352 fw2, PART_ALL, False, True, CLEAR_ALL, NULL,
3353 NULL);
3356 border_draw_decorations(
3357 hilight, PART_ALL, True, True, CLEAR_ALL, NULL, NULL);
3360 void CMD_ButtonStyle(F_CMD_ARGS)
3362 do_button_style(F_PASS_ARGS, False);
3364 return;
3369 * Appends a button decoration style (veliaa@rpi.edu)
3372 void CMD_AddButtonStyle(F_CMD_ARGS)
3374 do_button_style(F_PASS_ARGS, True);
3376 return;
3379 void CMD_SetEnv(F_CMD_ARGS)
3381 char *szVar = NULL;
3382 char *szValue = NULL;
3383 char *szPutenv = NULL;
3385 action = GetNextToken(action, &szVar);
3386 if (!szVar)
3388 return;
3390 action = GetNextToken(action, &szValue);
3391 if (!szValue)
3393 szValue = safestrdup("");
3395 szPutenv = safemalloc(strlen(szVar) + strlen(szValue) + 2);
3396 sprintf(szPutenv,"%s=%s", szVar, szValue);
3397 flib_putenv(szVar, szPutenv);
3398 free(szVar);
3399 free(szPutenv);
3400 free(szValue);
3402 return;
3405 void CMD_UnsetEnv(F_CMD_ARGS)
3407 char *szVar = NULL;
3409 szVar = PeekToken(action, &action);
3410 if (!szVar)
3412 return;
3414 flib_unsetenv(szVar);
3416 return;
3419 void CMD_GlobalOpts(F_CMD_ARGS)
3421 char *opt;
3422 char *replace;
3423 char buf[64];
3424 int i;
3425 Bool is_bugopt;
3426 char *optlist[] = {
3427 "WindowShadeShrinks",
3428 "WindowShadeScrolls",
3429 "SmartPlacementIsReallySmart",
3430 "SmartPlacementIsNormal",
3431 "ClickToFocusDoesntPassClick",
3432 "ClickToFocusPassesClick",
3433 "ClickToFocusDoesntRaise",
3434 "ClickToFocusRaises",
3435 "MouseFocusClickDoesntRaise",
3436 "MouseFocusClickRaises",
3437 "NoStipledTitles",
3438 "StipledTitles",
3439 "CaptureHonorsStartsOnPage",
3440 "CaptureIgnoresStartsOnPage",
3441 "RecaptureHonorsStartsOnPage",
3442 "RecaptureIgnoresStartsOnPage",
3443 "ActivePlacementHonorsStartsOnPage",
3444 "ActivePlacementIgnoresStartsOnPage",
3445 "RaiseOverNativeWindows",
3446 "IgnoreNativeWindows",
3447 NULL
3449 char *replacelist[] = {
3450 /* These options are mapped to the Style * command */
3451 NULL, /* NULL means to use "Style * <optionname>" */
3452 NULL,
3453 "* MinOverlapPlacement",
3454 "* TileCascadePlacement",
3455 "* ClickToFocusPassesClickOff",
3456 "* ClickToFocusPassesClick",
3457 "* ClickToFocusRaisesOff",
3458 "* ClickToFocusRaises",
3459 "* MouseFocusClickRaisesOff",
3460 "* MouseFocusClickRaises",
3461 "* StippledTitleOff",
3462 "* StippledTitle",
3463 NULL,
3464 NULL,
3465 NULL,
3466 NULL,
3467 "* ManualPlacementHonorsStartsOnPage",
3468 "* ManualPlacementIgnoresStartsOnPage",
3469 /* These options are mapped to the BugOpts command */
3470 "RaiseOverNativeWindows on",
3471 "RaiseOverNativeWindows off"
3474 fvwm_msg(ERR, "SetGlobalOptions",
3475 "The GlobalOpts command is obsolete.");
3476 for (action = GetNextSimpleOption(action, &opt); opt;
3477 action = GetNextSimpleOption(action, &opt))
3479 replace = NULL;
3480 is_bugopt = False;
3482 i = GetTokenIndex(opt, optlist, 0, NULL);
3483 if (i > -1)
3485 char *cmd;
3486 char *tmp;
3488 replace = replacelist[i];
3489 if (replace == NULL)
3491 replace = &(buf[0]);
3492 sprintf(buf, "* %s", opt);
3494 else if (*replace != '*')
3496 is_bugopt = True;
3498 tmp = action;
3499 action = replace;
3500 if (!is_bugopt)
3502 CMD_Style(F_PASS_ARGS);
3503 cmd = "Style";
3505 else
3507 CMD_BugOpts(F_PASS_ARGS);
3508 cmd = "BugOpts";
3510 action = tmp;
3511 fvwm_msg(
3512 ERR, "SetGlobalOptions",
3513 "Please replace 'GlobalOpts %s' with '%s %s'.",
3514 opt, cmd, replace);
3516 else
3518 fvwm_msg(ERR, "SetGlobalOptions",
3519 "Unknown Global Option '%s'", opt);
3521 /* should never be null, but checking anyways... */
3522 if (opt)
3524 free(opt);
3527 if (opt)
3529 free(opt);
3532 return;
3535 void CMD_BugOpts(F_CMD_ARGS)
3537 char *opt;
3538 int toggle;
3539 char *optstring;
3541 /* fvwm_msg(DBG,"SetGlobalOptions","init action == '%s'\n",action); */
3542 while (action && *action && *action != '\n')
3544 action = GetNextFullOption(action, &optstring);
3545 if (!optstring)
3547 /* no more options */
3548 return;
3550 toggle = ParseToggleArgument(
3551 SkipNTokens(optstring,1), NULL, 2, False);
3552 opt = PeekToken(optstring, NULL);
3553 free(optstring);
3555 if (!opt)
3557 return;
3559 /* toggle = ParseToggleArgument(rest, &rest, 2, False);*/
3561 if (StrEquals(opt, "FlickeringMoveWorkaround"))
3563 switch (toggle)
3565 case -1:
3566 Scr.bo.do_disable_configure_notify ^= 1;
3567 break;
3568 case 0:
3569 case 1:
3570 Scr.bo.do_disable_configure_notify = toggle;
3571 break;
3572 default:
3573 Scr.bo.do_disable_configure_notify = 0;
3574 break;
3577 else if (StrEquals(opt, "MixedVisualWorkaround"))
3579 switch (toggle)
3581 case -1:
3582 Scr.bo.do_install_root_cmap ^= 1;
3583 break;
3584 case 0:
3585 case 1:
3586 Scr.bo.do_install_root_cmap = toggle;
3587 break;
3588 default:
3589 Scr.bo.do_install_root_cmap = 0;
3590 break;
3593 else if (StrEquals(opt, "ModalityIsEvil"))
3595 switch (toggle)
3597 case -1:
3598 Scr.bo.is_modality_evil ^= 1;
3599 break;
3600 case 0:
3601 case 1:
3602 Scr.bo.is_modality_evil = toggle;
3603 break;
3604 default:
3605 Scr.bo.is_modality_evil = 0;
3606 break;
3608 if (Scr.bo.is_modality_evil)
3610 SetMWM_INFO(Scr.NoFocusWin);
3613 else if (StrEquals(opt, "RaiseOverNativeWindows"))
3615 switch (toggle)
3617 case -1:
3618 Scr.bo.is_raise_hack_needed ^= 1;
3619 break;
3620 case 0:
3621 case 1:
3622 Scr.bo.is_raise_hack_needed = toggle;
3623 break;
3624 default:
3625 Scr.bo.is_raise_hack_needed = 0;
3626 break;
3629 else if (StrEquals(opt, "RaiseOverUnmanaged"))
3631 switch (toggle)
3633 case -1:
3634 Scr.bo.do_raise_over_unmanaged ^= 1;
3635 break;
3636 case 0:
3637 case 1:
3638 Scr.bo.do_raise_over_unmanaged = toggle;
3639 break;
3640 default:
3641 Scr.bo.do_raise_over_unmanaged = 0;
3642 break;
3645 else if (StrEquals(opt, "FlickeringQtDialogsWorkaround"))
3647 switch (toggle)
3649 case -1:
3650 Scr.bo.do_enable_flickering_qt_dialogs_workaround ^= 1;
3651 break;
3652 case 0:
3653 case 1:
3654 Scr.bo.do_enable_flickering_qt_dialogs_workaround = toggle;
3655 break;
3656 default:
3657 Scr.bo.do_enable_flickering_qt_dialogs_workaround = 0;
3658 break;
3661 else if (StrEquals(opt, "QtDragnDropWorkaround") )
3663 switch (toggle)
3665 case -1:
3666 Scr.bo.do_enable_qt_drag_n_drop_workaround ^= 1;
3667 break;
3668 case 0:
3669 case 1:
3670 Scr.bo.do_enable_qt_drag_n_drop_workaround = toggle;
3671 break;
3672 default:
3673 Scr.bo.do_enable_qt_drag_n_drop_workaround = 0;
3674 break;
3677 else if (EWMH_BugOpts(opt, toggle))
3679 /* work is done in EWMH_BugOpts */
3681 else if (StrEquals(opt, "DisplayNewWindowNames"))
3683 switch (toggle)
3685 case -1:
3686 Scr.bo.do_display_new_window_names ^= 1;
3687 break;
3688 case 0:
3689 case 1:
3690 Scr.bo.do_display_new_window_names = toggle;
3691 break;
3692 default:
3693 Scr.bo.do_display_new_window_names = 0;
3694 break;
3697 else if (StrEquals(opt, "ExplainWindowPlacement"))
3699 switch (toggle)
3701 case -1:
3702 Scr.bo.do_explain_window_placement ^= 1;
3703 break;
3704 case 0:
3705 case 1:
3706 Scr.bo.do_explain_window_placement = toggle;
3707 break;
3708 default:
3709 Scr.bo.do_explain_window_placement = 0;
3710 break;
3713 else if (StrEquals(opt, "DebugCRMotionMethod"))
3715 switch (toggle)
3717 case -1:
3718 Scr.bo.do_debug_cr_motion_method ^= 1;
3719 break;
3720 case 0:
3721 case 1:
3722 Scr.bo.do_debug_cr_motion_method = toggle;
3723 break;
3724 default:
3725 Scr.bo.do_debug_cr_motion_method = 0;
3726 break;
3729 else if (StrEquals(opt, "TransliterateUtf8"))
3731 FiconvSetTransliterateUtf8(toggle);
3733 else
3735 fvwm_msg(ERR, "SetBugOptions",
3736 "Unknown Bug Option '%s'", opt);
3740 return;
3743 void CMD_Emulate(F_CMD_ARGS)
3745 char *style;
3747 style = PeekToken(action, NULL);
3748 if (!style || StrEquals(style, "fvwm"))
3750 Scr.gs.do_emulate_mwm = False;
3751 Scr.gs.do_emulate_win = False;
3753 else if (StrEquals(style, "mwm"))
3755 Scr.gs.do_emulate_mwm = True;
3756 Scr.gs.do_emulate_win = False;
3758 else if (StrEquals(style, "win"))
3760 Scr.gs.do_emulate_mwm = False;
3761 Scr.gs.do_emulate_win = True;
3763 else
3765 fvwm_msg(ERR, "Emulate", "Unknown style '%s'", style);
3766 return;
3768 Scr.flags.do_need_window_update = 1;
3769 Scr.flags.has_default_font_changed = 1;
3770 Scr.flags.has_default_color_changed = 1;
3772 return;
3775 void CMD_ColorLimit(F_CMD_ARGS)
3777 fvwm_msg(
3778 WARN, "ColorLimit", "ColorLimit is obsolete,\n\tuse the "
3779 "fvwm -color-limit option");
3781 return;
3785 /* set animation parameters */
3786 void CMD_SetAnimation(F_CMD_ARGS)
3788 char *opt;
3789 int delay;
3790 float pct;
3791 int i = 0;
3793 opt = PeekToken(action, &action);
3794 if (!opt || sscanf(opt,"%d",&delay) != 1)
3796 fvwm_msg(ERR,"SetAnimation",
3797 "Improper milli-second delay as first argument");
3798 return;
3800 if (delay > 500)
3802 fvwm_msg(WARN,"SetAnimation",
3803 "Using longer than .5 seconds as between frame"
3804 " animation delay");
3806 cmsDelayDefault = delay;
3807 for (opt = PeekToken(action, &action); opt;
3808 opt = PeekToken(action, &action))
3810 if (sscanf(opt,"%f",&pct) != 1)
3812 fvwm_msg(ERR,"SetAnimation",
3813 "Use fractional values ending in 1.0 as args"
3814 " 2 and on");
3815 return;
3817 rgpctMovementDefault[i++] = pct;
3819 /* No pct entries means don't change them at all */
3820 if (i > 0 && rgpctMovementDefault[i-1] != 1.0)
3822 rgpctMovementDefault[i++] = 1.0;
3825 return;
3828 /* Determine which modifiers are required with a keycode to make <keysym>. */
3829 static Bool FKeysymToKeycode (Display *dpy, KeySym keysym,
3830 unsigned int *keycode, unsigned int *modifiers)
3832 int m;
3834 *keycode = XKeysymToKeycode(dpy, keysym);
3835 *modifiers = 0;
3837 for (m = 0; m <= 8; ++m)
3839 KeySym ks = XKeycodeToKeysym(dpy, *keycode, m);
3840 if (ks == keysym)
3842 switch (m)
3844 case 0: /* No modifiers */
3845 break;
3846 case 1: /* Shift modifier */
3847 *modifiers |= ShiftMask;
3848 break;
3849 default:
3850 fvwm_msg(ERR, "FKeysymToKeycode",
3851 "Unhandled modifier %d", m);
3852 break;
3854 return True;
3857 return False;
3860 static void __fake_event(F_CMD_ARGS, FakeEventType type)
3862 char *token;
3863 char *optlist[] = {
3864 "press", "p",
3865 "release", "r",
3866 "wait", "w",
3867 "modifiers", "m",
3868 "depth", "d",
3869 NULL
3871 unsigned int mask = 0;
3872 Window root = Scr.Root;
3873 int maxdepth = 0;
3874 static char args[128];
3875 strncpy(args, action, sizeof(args) - 1);
3877 /* get the mask of pressed/released buttons/keys */
3878 FQueryPointer(
3879 dpy, Scr.Root, &root, &JunkRoot, &JunkX, &JunkY, &JunkX,
3880 &JunkY, &mask);
3882 token = PeekToken(action, &action);
3883 while (token && action)
3885 int index = GetTokenIndex(token, optlist, 0, NULL);
3886 int val, depth;
3887 XEvent e;
3888 Window w;
3889 Window child_w;
3890 int x = 0;
3891 int y = 0;
3892 int rx = 0;
3893 int ry = 0;
3894 Bool do_unset;
3895 long add_mask = 0;
3896 KeySym keysym = NoSymbol;
3898 XFlush(dpy);
3899 do_unset = True;
3900 switch (index)
3902 case 0:
3903 case 1:
3904 do_unset = False;
3905 /* fall through */
3906 case 2:
3907 case 3:
3908 /* key/button press or release */
3909 if (type == FakeMouseEvent)
3911 if ((GetIntegerArguments(
3912 action, &action, &val, 1) != 1) ||
3913 val < 1 ||
3914 val > NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
3916 fvwm_msg(
3917 ERR, "__fake_event",
3918 "Invalid button specifier in"
3919 " \"%s\" for FakeClick.", args);
3920 return; /* error */
3923 else /* type == FakeKeyEvent */
3925 char *key = PeekToken(action, &action);
3926 if (key == NULL)
3928 fvwm_msg(
3929 ERR, "__fake_event",
3930 "No keysym specifier in \"%s\""
3931 " for FakeKeypress.", args);
3932 return;
3935 /* Do *NOT* use FvwmStringToKeysym() as it is
3936 * case insensitive. */
3937 keysym = XStringToKeysym(key);
3938 if (keysym == NoSymbol)
3940 fvwm_msg(
3941 ERR, "__fake_event",
3942 "Invalid keysym specifier (%s)"
3943 " in \"%s\" for FakeKeypress.",
3944 key, args);
3945 return;
3949 w = None;
3950 child_w = root;
3951 for (depth = 1;
3952 depth != maxdepth &&
3953 w != child_w && child_w != None;
3954 depth++)
3956 w = child_w;
3957 if (FQueryPointer(
3958 dpy, w, &root, &child_w,
3959 &rx, &ry, &x, &y,
3960 &JunkMask) == False)
3962 /* pointer is on a different
3963 * screen - that's okay here */
3967 if (type == FakeMouseEvent)
3969 e.type = (do_unset) ?
3970 ButtonRelease : ButtonPress;
3971 e.xbutton.display = dpy;
3972 e.xbutton.window = w;
3973 e.xbutton.subwindow = None;
3974 e.xbutton.root = root;
3975 e.xbutton.time = fev_get_evtime();
3976 e.xbutton.x = x;
3977 e.xbutton.y = y;
3978 e.xbutton.x_root = rx;
3979 e.xbutton.y_root = ry;
3980 e.xbutton.button = val;
3981 e.xbutton.state = mask;
3982 e.xbutton.same_screen = (Scr.Root == root);
3983 /* SS: I think this mask handling code is
3984 * buggy.
3985 * The value of <mask> is overridden during a
3986 * "wait" operation. Also why are we only using
3987 * Button1Mask? What if the user has requested
3988 * a FakeClick using some other button? */
3989 /* DV: Button1Mask is actually a bit. Shifting
3990 * it by (val -1) bits to the left gives
3991 * Button2Mask, Button3Mask etc. */
3992 if (do_unset)
3994 mask &= ~(Button1Mask << (val - 1));
3996 else
3998 mask |= (Button1Mask << (val - 1));
4000 add_mask = (do_unset) ?
4001 ButtonPressMask : ButtonReleaseMask;
4003 else
4005 /* type == FakeKeyEvent */
4006 e.type = (do_unset ? KeyRelease : KeyPress);
4007 e.xkey.display = dpy;
4008 e.xkey.subwindow = None;
4009 e.xkey.root = root;
4010 e.xkey.time = fev_get_evtime();
4011 e.xkey.x = x;
4012 e.xkey.y = y;
4013 e.xkey.x_root = rx;
4014 e.xkey.y_root = ry;
4015 e.xkey.same_screen = (Scr.Root == root);
4017 w = e.xkey.window = exc->w.w;
4019 if (FKeysymToKeycode(
4020 dpy, keysym, &(e.xkey.keycode),
4021 &(e.xkey.state)) != True)
4023 fvwm_msg(DBG, "__fake_event",
4024 "FKeysymToKeycode failed");
4025 return;
4027 e.xkey.state |= mask;
4028 add_mask = (do_unset) ?
4029 KeyReleaseMask : KeyPressMask;
4031 FSendEvent(dpy, w, True,
4032 SubstructureNotifyMask | add_mask, &e);
4033 XFlush(dpy);
4034 break;
4035 case 4:
4036 case 5:
4037 /* wait */
4038 if ((GetIntegerArguments(
4039 action, &action, &val, 1) != 1) ||
4040 val <= 0 || val > 1000000)
4042 fvwm_msg(ERR, "__fake_event",
4043 "Invalid wait value in \"%s\"", args);
4044 return;
4047 usleep(1000 * val);
4048 if (FQueryPointer(
4049 dpy, Scr.Root, &root, &JunkRoot,
4050 &JunkX, &JunkY, &JunkX, &JunkY,
4051 &mask) == False)
4053 /* pointer is on a different screen -
4054 * that's okay here */
4056 break;
4057 case 6:
4058 case 7:
4059 /* set modifier */
4060 if (GetIntegerArguments(action, &action, &val, 1) != 1)
4062 fvwm_msg(
4063 ERR, "__fake_event",
4064 "Invalid modifier value in \"%s\"",
4065 args);
4066 return;
4068 do_unset = False;
4069 if (val < 0)
4071 do_unset = True;
4072 val = -val;
4074 if (val == 6)
4076 val = ShiftMask;
4078 else if (val == 7)
4080 val = LockMask;
4082 else if (val == 8)
4084 val = ControlMask;
4086 else if (val >=1 && val <= 5)
4088 val = (Mod1Mask << (val - 1));
4090 else
4092 /* error */
4093 return;
4095 /* SS: Could be buggy if a "modifier" operation
4096 * preceeds a "wait" operation. */
4097 if (do_unset)
4099 mask &= ~val;
4101 else
4103 mask |= val;
4105 break;
4106 case 8:
4107 case 9:
4108 /* new max depth */
4109 if (GetIntegerArguments(action, &action, &val, 1) != 1)
4111 fvwm_msg(ERR, "__fake_event",
4112 "Invalid depth value in \"%s\"", args);
4113 return;
4115 maxdepth = val;
4116 break;
4117 default:
4118 fvwm_msg(ERR, "__fake_event",
4119 "Invalid command (%s) in \"%s\"", token, args);
4120 return;
4122 if (action)
4124 token = PeekToken(action, &action);
4128 return;
4131 void CMD_FakeClick(F_CMD_ARGS)
4133 __fake_event(F_PASS_ARGS, FakeMouseEvent);
4135 return;
4138 void CMD_FakeKeypress(F_CMD_ARGS)
4140 __fake_event(F_PASS_ARGS, FakeKeyEvent);
4142 return;
4145 /* A function to handle stroke (olicha Nov 11, 1999) */
4146 #ifdef HAVE_STROKE
4147 void CMD_StrokeFunc(F_CMD_ARGS)
4149 int finished = 0;
4150 int abort = 0;
4151 int modifiers = exc->x.etrigger->xbutton.state;
4152 int start_event_type = exc->x.etrigger->type;
4153 char sequence[STROKE_MAX_SEQUENCE + 1];
4154 char *stroke_action, *name;
4155 char *opt = NULL;
4156 Bool finish_on_release = True;
4157 KeySym keysym;
4158 Bool restore_repeat = False;
4159 Bool echo_sequence = False;
4160 Bool draw_motion = False;
4161 int i = 0;
4162 int *x = NULL;
4163 int *y = NULL;
4164 const int STROKE_CHUNK_SIZE = 0xff;
4165 int coords_size = STROKE_CHUNK_SIZE;
4166 Window JunkRoot, JunkChild;
4167 int JunkX, JunkY;
4168 int tmpx, tmpy;
4169 unsigned int JunkMask;
4170 Bool feed_back = False;
4171 int stroke_width = 1;
4172 XEvent e;
4173 XClassHint *class;
4177 if (!GrabEm(CRS_STROKE, GRAB_NORMAL))
4179 XBell(dpy, 0);
4180 return;
4182 x = (int*)safemalloc(coords_size * sizeof(int));
4183 y = (int*)safemalloc(coords_size * sizeof(int));
4184 e = *exc->x.etrigger;
4185 /* set the default option */
4186 if (e.type == KeyPress || e.type == ButtonPress)
4188 finish_on_release = True;
4190 else
4192 finish_on_release = False;
4195 /* parse the option */
4196 for (action = GetNextSimpleOption(action, &opt); opt;
4197 action = GetNextSimpleOption(action, &opt))
4199 if (StrEquals("NotStayPressed",opt))
4201 finish_on_release = False;
4203 else if (StrEquals("EchoSequence",opt))
4205 echo_sequence = True;
4207 else if (StrEquals("DrawMotion",opt))
4209 draw_motion = True;
4211 else if (StrEquals("FeedBack",opt))
4213 feed_back = True;
4215 else if (StrEquals("StrokeWidth",opt))
4217 /* stroke width takes a positive integer argument */
4218 if (opt)
4220 free(opt);
4222 action = GetNextToken(action, &opt);
4223 if (!opt)
4225 fvwm_msg(
4226 WARN, "StrokeWidth",
4227 "needs an integer argument");
4229 /* we allow stroke_width == 0 which means drawing a
4230 * `fast' line of width 1; the upper level of 100 is
4231 * arbitrary */
4232 else if (!sscanf(opt, "%d", &stroke_width) ||
4233 stroke_width < 0 || stroke_width > 100)
4235 fvwm_msg(
4236 WARN, "StrokeWidth",
4237 "Bad integer argument %d",
4238 stroke_width);
4239 stroke_width = 1;
4242 else
4244 fvwm_msg(WARN,"StrokeFunc","Unknown option %s", opt);
4246 if (opt)
4248 free(opt);
4251 if (opt)
4253 free(opt);
4256 /* Force auto repeat off and grab the Keyboard to get proper
4257 * KeyRelease events if we need it.
4258 * Some computers do not support KeyRelease events, can we
4259 * check this here ? No ? */
4260 if (start_event_type == KeyPress && finish_on_release)
4262 XKeyboardState kstate;
4264 XGetKeyboardControl(dpy, &kstate);
4265 if (kstate.global_auto_repeat == AutoRepeatModeOn)
4267 XAutoRepeatOff(dpy);
4268 restore_repeat = True;
4270 MyXGrabKeyboard(dpy);
4273 /* be ready to get a stroke sequence */
4274 stroke_init();
4276 if (draw_motion)
4278 MyXGrabServer(dpy);
4279 if (FQueryPointer(
4280 dpy, Scr.Root, &JunkRoot, &JunkChild, &x[0], &y[0],
4281 &JunkX, &JunkY, &JunkMask) == False)
4283 /* pointer is on a different screen */
4284 x[0] = 0;
4285 y[0] = 0;
4287 XSetLineAttributes(
4288 dpy,Scr.XorGC,stroke_width,LineSolid,CapButt,JoinMiter);
4291 while (!finished && !abort)
4293 /* block until there is an event */
4294 FMaskEvent(
4295 dpy, ButtonPressMask | ButtonReleaseMask |
4296 KeyPressMask | KeyReleaseMask | ButtonMotionMask |
4297 PointerMotionMask, &e);
4298 switch (e.type)
4300 case MotionNotify:
4301 if (e.xmotion.same_screen == False)
4303 continue;
4305 if (e.xany.window != Scr.Root)
4307 if (FQueryPointer(
4308 dpy, Scr.Root, &JunkRoot,
4309 &JunkChild, &tmpx, &tmpy, &JunkX,
4310 &JunkY, &JunkMask) == False)
4312 /* pointer is on a different screen */
4313 tmpx = 0;
4314 tmpy = 0;
4317 else
4319 tmpx = e.xmotion.x;
4320 tmpy = e.xmotion.y;
4322 stroke_record(tmpx,tmpy);
4323 if (draw_motion && (x[i] != tmpx || y[i] != tmpy))
4325 i++;
4326 if (i >= coords_size)
4328 coords_size += STROKE_CHUNK_SIZE;
4329 x = (int*)saferealloc(
4330 (void *)x, coords_size *
4331 sizeof(int));
4332 y = (int*)saferealloc(
4333 (void *)y, coords_size *
4334 sizeof(int));
4336 x[i] = tmpx;
4337 y[i] = tmpy;
4338 XDrawLine(
4339 dpy, Scr.Root, Scr.XorGC, x[i-1],
4340 y[i-1], x[i], y[i]);
4342 break;
4343 case ButtonRelease:
4344 if (finish_on_release && start_event_type ==
4345 ButtonPress)
4347 finished = 1;
4349 break;
4350 case KeyRelease:
4351 if (finish_on_release && start_event_type == KeyPress)
4353 finished = 1;
4355 break;
4356 case KeyPress:
4357 keysym = XLookupKeysym(&e.xkey, 0);
4358 /* abort if Escape or Delete is pressed (as in menus.c)
4360 if (keysym == XK_Escape || keysym == XK_Delete ||
4361 keysym == XK_KP_Separator)
4363 abort = 1;
4365 /* finish on enter or space (as in menus.c) */
4366 if (keysym == XK_Return || keysym == XK_KP_Enter ||
4367 keysym == XK_space)
4369 finished = 1;
4371 break;
4372 case ButtonPress:
4373 if (!finish_on_release)
4375 finished = 1;
4377 break;
4378 default:
4379 break;
4383 if (draw_motion)
4385 while (i > 0)
4387 XDrawLine(
4388 dpy, Scr.Root, Scr.XorGC, x[i-1], y[i-1], x[i],
4389 y[i]);
4390 i--;
4392 XSetLineAttributes(dpy,Scr.XorGC,0,LineSolid,CapButt,JoinMiter);
4393 MyXUngrabServer(dpy);
4395 if (x != NULL)
4397 free(x);
4398 free(y);
4400 if (start_event_type == KeyPress && finish_on_release)
4402 MyXUngrabKeyboard(dpy);
4404 UngrabEm(GRAB_NORMAL);
4405 if (restore_repeat)
4407 XAutoRepeatOn(dpy);
4410 /* get the stroke sequence */
4411 stroke_trans(sequence);
4413 if (echo_sequence)
4415 char num_seq[STROKE_MAX_SEQUENCE + 1];
4417 for (i = 0; sequence[i] != '\0';i++)
4419 /* Telephone to numeric pad */
4420 if ('7' <= sequence[i] && sequence[i] <= '9')
4422 num_seq[i] = sequence[i]-6;
4424 else if ('1' <= sequence[i] && sequence[i] <= '3')
4426 num_seq[i] = sequence[i]+6;
4428 else
4430 num_seq[i] = sequence[i];
4433 num_seq[i++] = '\0';
4434 fvwm_msg(INFO, "StrokeFunc", "stroke sequence: %s (N%s)",
4435 sequence, num_seq);
4437 if (abort)
4439 return;
4441 if (exc->w.fw == NULL)
4443 class = NULL;
4444 name = NULL;
4446 else
4448 class = &exc->w.fw->class;
4449 name = exc->w.fw->name.name;
4451 /* check for a binding */
4452 stroke_action = CheckBinding(
4453 Scr.AllBindings, sequence, 0, modifiers, GetUnusedModifiers(),
4454 exc->w.wcontext, BIND_STROKE, class, name);
4456 /* execute the action */
4457 if (stroke_action != NULL)
4459 const exec_context_t *exc2;
4460 exec_context_changes_t ecc;
4462 if (feed_back && atoi(sequence) != 0)
4464 GrabEm(CRS_WAIT, GRAB_BUSY);
4465 usleep(200000);
4466 UngrabEm(GRAB_BUSY);
4468 ecc.x.etrigger = &e;
4469 exc2 = exc_clone_context(exc, &ecc, ECC_ETRIGGER);
4470 execute_function(cond_rc, exc2, stroke_action, 0);
4471 exc_destroy_context(exc2);
4474 return;
4476 #endif /* HAVE_STROKE */
4478 void CMD_State(F_CMD_ARGS)
4480 unsigned int state;
4481 int toggle;
4482 int n;
4483 FvwmWindow * const fw = exc->w.fw;
4485 n = GetIntegerArguments(action, &action, (int *)&state, 1);
4486 if (n <= 0)
4488 return;
4490 if (state < 0 || state > 31)
4492 fvwm_msg(ERR, "CMD_State", "Illegal state %d\n", state);
4493 return;
4495 toggle = ParseToggleArgument(action, NULL, -1, 0);
4496 state = (1 << state);
4497 switch (toggle)
4499 case -1:
4500 TOGGLE_USER_STATES(fw, state);
4501 break;
4502 case 0:
4503 CLEAR_USER_STATES(fw, state);
4504 break;
4505 case 1:
4506 default:
4507 SET_USER_STATES(fw, state);
4508 break;
4511 return;