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.
6 *
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.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 /* ---------------------------- included header files ---------------------- */
17
18 #include "config.h"
19
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <errno.h>
26
27 #include "libs/fvwm_x11.h"
28 #include "libs/fvwmlib.h"
29 #include "libs/fvwmsignal.h"
30 #include "libs/cJSON.h"
31 #include "libs/setpgrp.h"
32 #include "libs/Grab.h"
33 #include "libs/Parse.h"
34 #include "libs/ColorUtils.h"
35 #include "libs/Graphics.h"
36 #include "libs/wild.h"
37 #include "libs/envvar.h"
38 #include "libs/ClientMsg.h"
39 #include "libs/Picture.h"
40 #include "libs/PictureUtils.h"
41 #include "libs/FGettext.h"
42 #include "libs/charmap.h"
43 #include "libs/wcontext.h"
44 #include "libs/Flocale.h"
45 #include "libs/FEvent.h"
46 #include "libs/Ficonv.h"
47 #include "fvwm.h"
48 #include "externs.h"
49 #include "colorset.h"
50 #include "bindings.h"
51 #include "misc.h"
52 #include "cursor.h"
53 #include "functions.h"
54 #include "commands.h"
55 #include "screen.h"
56 #include "builtins.h"
57 #include "module_interface.h"
58 #include "borders.h"
59 #include "frame.h"
60 #include "events.h"
61 #include "ewmh.h"
62 #include "virtual.h"
63 #include "decorations.h"
64 #include "add_window.h"
65 #include "update.h"
66 #include "style.h"
67 #include "move_resize.h"
68 #include "menus.h"
69 #include "infostore.h"
70 #include "focus.h"
71
72 /* ---------------------------- local definitions -------------------------- */
73
74 static void status_init_pipe(void);
75
76 /* ---------------------------- local macros ------------------------------- */
77
78 /* ---------------------------- imports ------------------------------------ */
79
80 extern float rgpctMovementDefault[32];
81 extern int cpctMovementDefault;
82 extern int cmsDelayDefault;
83
84 /* ---------------------------- included code files ------------------------ */
85
86 /* ---------------------------- local types -------------------------------- */
87 typedef enum {FakeMouseEvent, FakeKeyEvent} FakeEventType;
88 /* ---------------------------- forward declarations ----------------------- */
89
90 /* ---------------------------- local variables ---------------------------- */
91
92 static char *exec_shell_name="/bin/sh";
93
94 /* button state strings must match the enumerated states */
95 static char *button_states[BS_MaxButtonStateName + 1] =
96 {
97 "ActiveUp",
98 "ActiveDown",
99 "InactiveUp",
100 "InactiveDown",
101 "ToggledActiveUp",
102 "ToggledActiveDown",
103 "ToggledInactiveUp",
104 "ToggledInactiveDown",
105 "Active",
106 "Inactive",
107 "ToggledActive",
108 "ToggledInactive",
109 "AllNormal",
110 "AllToggled",
111 "AllActive",
112 "AllInactive",
113 "AllUp",
114 "AllDown",
115 "AllActiveUp",
116 "AllActiveDown",
117 "AllInactiveUp",
118 "AllInactiveDown",
119 NULL
120 };
121
122 /* ---------------------------- exported variables (globals) --------------- */
123
124 char *ModulePath = FVWM_MODULEDIR;
125 int moduleTimeout = DEFAULT_MODULE_TIMEOUT;
126 static FILE *status_fp;
127
128 /* ---------------------------- local functions ---------------------------- */
129
130 void
status_send(void)131 status_send(void)
132 {
133 cJSON *msg = NULL, *screens = NULL;
134 cJSON *desk_doc[1024], *individual_d[1024];
135 int m_count, d_count;
136 FvwmWindow *fw_cur;
137 DesktopsInfo *di;
138 struct monitor *m, *m_cur;
139 char *as_json = NULL;
140
141 if (status_fp == NULL || !Scr.flags.are_windows_captured)
142 return;
143
144 memset(individual_d, 0, sizeof(cJSON));
145 memset(desk_doc, 0, sizeof(cJSON));
146
147 if ((msg = cJSON_CreateObject()) == NULL)
148 return;
149
150 m_cur = monitor_get_current();
151
152 cJSON_AddNumberToObject(msg, "version", 2);
153 cJSON_AddStringToObject(msg, "current_screen", m_cur->si->name);
154 cJSON_AddStringToObject(msg, "desktop_mode",
155 monitor_mode == MONITOR_TRACKING_G && is_tracking_shared ? "shared" :
156 monitor_mode == MONITOR_TRACKING_G ? "global" :
157 monitor_mode == MONITOR_TRACKING_M ? "per-monitor" :
158 "unknown");
159
160 screens = cJSON_AddObjectToObject(msg, "screens");
161
162 d_count = 0, m_count = 0;
163 TAILQ_FOREACH(m, &monitor_q, entry) {
164 cJSON *this_desktop;
165 if ((desk_doc[d_count] = cJSON_CreateObject()) == NULL)
166 goto out;
167
168 this_desktop = cJSON_AddObjectToObject(desk_doc[d_count],
169 "desktops");
170
171 di = m->Desktops->next;
172 while (di != NULL) {
173 if ((individual_d[d_count] = cJSON_CreateObject()) == NULL)
174 goto out;
175 cJSON_AddNumberToObject(individual_d[d_count], "number",
176 di->desk);
177 cJSON_AddBoolToObject(individual_d[d_count], "is_current",
178 di->desk == m->virtual_scr.CurrentDesk);
179 cJSON_AddBoolToObject(individual_d[d_count], "is_urgent",
180 desk_get_fw_urgent(m, di->desk));
181 cJSON_AddNumberToObject(individual_d[d_count],
182 "number_of_clients", desk_get_fw_count(m, di->desk));
183 cJSON_AddItemToObject(this_desktop, di->name, individual_d[d_count]);
184
185 di = di->next;
186 }
187
188 fw_cur = get_focus_window();
189 if (fw_cur != NULL && fw_cur->m == m) {
190 cJSON_AddStringToObject(desk_doc[d_count],
191 "current_client", fw_cur->visible_name);
192 }
193
194 cJSON_AddItemToObject(screens, m->si->name, desk_doc[d_count]);
195
196 d_count++;
197 m_count++;
198 }
199 if ((as_json = cJSON_PrintUnformatted(msg)) == NULL)
200 goto out;
201
202 fflush(status_fp);
203 fprintf(status_fp, "%s\n", as_json);
204 fflush(status_fp);
205
206 out:
207 cJSON_free(as_json);
208 cJSON_Delete(msg);
209 }
210
211 #define PIPENAME "fvwm3.pipe"
212 #define STATUSENV "FVWM_STATUS_PIPE"
status_init_pipe(void)213 static void status_init_pipe(void)
214 {
215 char *tmpdir, *pipename, *addenv;
216 int status_fd = -1;
217
218 if ((tmpdir = getenv("TMPDIR")) == NULL)
219 tmpdir = "/tmp";
220
221 asprintf(&pipename, "%s/%s", tmpdir, PIPENAME);
222
223 unlink(pipename);
224 if ((mkfifo(pipename, 0666) == -1)) {
225 fvwm_debug(__func__, "mkfifo: %s", strerror(errno));
226 free(pipename);
227 return;
228 }
229
230 if ((status_fd = open(pipename, O_RDWR|O_NONBLOCK)) == -1) {
231 fvwm_debug(__func__, "Couldn't open pipe '%s': %s", pipename,
232 strerror(errno));
233 free(pipename);
234 return;
235 }
236
237 if ((status_fp = fdopen(status_fd, "w")) == NULL) {
238 fvwm_debug(__func__, "Error opening pipe: %s", strerror(errno));
239 free(pipename);
240 return;
241 }
242
243 xasprintf(&addenv, "%s=%s", STATUSENV, pipename);
244 flib_putenv(STATUSENV, addenv);
245 free(addenv);
246
247 fvwm_debug(__func__, "Pipe opened: %s (fd: %d)", pipename,
248 fileno(status_fp));
249
250 free(pipename);
251 }
252
253 /** Prepend rather than replace the image path.
254 Used for obsolete PixmapPath and IconPath **/
obsolete_imagepaths(const char * pre_path)255 static void obsolete_imagepaths( const char* pre_path )
256 {
257 char* tmp = stripcpy( pre_path );
258 char* path = alloca(strlen( tmp ) + strlen(PictureGetImagePath()) + 2 );
259
260 strcpy( path, tmp );
261 free( tmp );
262
263 strcat( path, ":" );
264 strcat( path, PictureGetImagePath() );
265
266 PictureSetImagePath( path );
267
268 return;
269 }
270
271 /*
272 *
273 * Reads a title button description (veliaa@rpi.edu)
274 *
275 */
ReadTitleButton(char * s,TitleButton * tb,Bool append,int button)276 static char *ReadTitleButton(
277 char *s, TitleButton *tb, Bool append, int button)
278 {
279 char *end = NULL;
280 char *spec;
281 char *t;
282 int i;
283 int bs;
284 int bs_start, bs_end;
285 int pstyle = 0;
286 DecorFace tmpdf;
287
288 Bool multiple;
289 int use_mask = 0;
290 int set_mask = 0;
291
292 s = SkipSpaces(s, NULL, 0);
293 t = GetNextTokenIndex(s, button_states, 0, &bs);
294 if (bs != BS_All)
295 {
296 s = SkipSpaces(t, NULL, 0);
297 }
298
299 if (bs == BS_All)
300 {
301 use_mask = 0;
302 set_mask = 0;
303 }
304 else if (bs == BS_Active)
305 {
306 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
307 set_mask = 0;
308 }
309 else if (bs == BS_Inactive)
310 {
311 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
312 set_mask = BS_MASK_INACTIVE;
313 }
314 else if (bs == BS_ToggledActive)
315 {
316 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
317 set_mask = BS_MASK_TOGGLED;
318 }
319 else if (bs == BS_ToggledInactive)
320 {
321 use_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
322 set_mask = BS_MASK_INACTIVE | BS_MASK_TOGGLED;
323 }
324 else if (bs == BS_AllNormal)
325 {
326 use_mask = BS_MASK_TOGGLED;
327 set_mask = 0;
328 }
329 else if (bs == BS_AllToggled)
330 {
331 use_mask = BS_MASK_TOGGLED;
332 set_mask = BS_MASK_TOGGLED;
333 }
334 else if (bs == BS_AllActive)
335 {
336 use_mask = BS_MASK_INACTIVE;
337 set_mask = 0;
338 }
339 else if (bs == BS_AllInactive)
340 {
341 use_mask = BS_MASK_INACTIVE;
342 set_mask = BS_MASK_INACTIVE;
343 }
344 else if (bs == BS_AllUp)
345 {
346 use_mask = BS_MASK_DOWN;
347 set_mask = 0;
348 }
349 else if (bs == BS_AllDown)
350 {
351 use_mask = BS_MASK_DOWN;
352 set_mask = BS_MASK_DOWN;
353 }
354 else if (bs == BS_AllActiveUp)
355 {
356 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
357 set_mask = 0;
358 }
359 else if (bs == BS_AllActiveDown)
360 {
361 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
362 set_mask = BS_MASK_DOWN;
363 }
364 else if (bs == BS_AllInactiveUp)
365 {
366 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
367 set_mask = BS_MASK_INACTIVE;
368 }
369 else if (bs == BS_AllInactiveDown)
370 {
371 use_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
372 set_mask = BS_MASK_INACTIVE | BS_MASK_DOWN;
373 }
374
375 if ((bs & BS_MaxButtonStateMask) == bs)
376 {
377 multiple = False;
378 bs_start = bs;
379 bs_end = bs;
380 }
381 else
382 {
383 multiple = True;
384 bs_start = 0;
385 bs_end = BS_MaxButtonState - 1;
386 for (i = bs_start; (i & use_mask) != set_mask && i <= bs_end;
387 i++)
388 {
389 bs_start++;
390 }
391 }
392
393 if (*s == '(')
394 {
395 int len;
396 pstyle = 1;
397 if (!(end = strchr(++s, ')')))
398 {
399 fvwm_debug(__func__,
400 "missing parenthesis: %s", s);
401 return NULL;
402 }
403 s = SkipSpaces(s, NULL, 0);
404 len = end - s + 1;
405 /* TA: FIXME! xasprintf() */
406 spec = fxmalloc(len);
407 strncpy(spec, s, len - 1);
408 spec[len - 1] = 0;
409 }
410 else
411 {
412 spec = s;
413 }
414
415 spec = SkipSpaces(spec, NULL, 0);
416 /* setup temporary in case button read fails */
417 memset(&tmpdf, 0, sizeof(DecorFace));
418 DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
419
420 if (strncmp(spec, "--", 2) == 0)
421 {
422 /* only change flags */
423 Bool verbose = True;
424 for (i = bs_start; i <= bs_end; ++i)
425 {
426 if (multiple && (i & use_mask) != set_mask)
427 {
428 continue;
429 }
430 ReadDecorFace(spec, &TB_STATE(*tb)[i], button, verbose);
431 verbose = False;
432 }
433 }
434 else if (ReadDecorFace(spec, &tmpdf, button, True))
435 {
436 if (append)
437 {
438 DecorFace *head = &TB_STATE(*tb)[bs_start];
439 DecorFace *tail = head;
440 DecorFace *next;
441
442 while (tail->next)
443 {
444 tail = tail->next;
445 }
446 tail->next = fxmalloc(sizeof(DecorFace));
447 memcpy(tail->next, &tmpdf, sizeof(DecorFace));
448 if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
449 DFS_FACE_TYPE((&TB_STATE(*tb)[bs_start])->style) ==
450 DefaultVectorButton)
451 {
452 /* override the default vector style */
453 memcpy(
454 &tail->next->style, &head->style,
455 sizeof(DecorFaceStyle));
456 DFS_FACE_TYPE(tail->next->style) = VectorButton;
457 next = head->next;
458 head->next = NULL;
459 FreeDecorFace(dpy, head);
460 memcpy(head, next, sizeof(DecorFace));
461 free(next);
462 }
463 for (i = bs_start + 1; i <= bs_end; ++i)
464 {
465 if (multiple && (i & use_mask) != set_mask)
466 {
467 continue;
468 }
469 head = &TB_STATE(*tb)[i];
470 tail = head;
471 while (tail->next)
472 {
473 tail = tail->next;
474 }
475 tail->next = fxcalloc(1, sizeof(DecorFace));
476 DFS_FACE_TYPE(tail->next->style) =
477 SimpleButton;
478 tail->next->next = NULL;
479 ReadDecorFace(spec, tail->next, button, False);
480 if (DFS_FACE_TYPE(tail->next->style) ==
481 VectorButton &&
482 DFS_FACE_TYPE((&TB_STATE(*tb)[i])->style) ==
483 DefaultVectorButton)
484 {
485 /* override the default vector style */
486 memcpy(
487 &tail->next->style,
488 &head->style,
489 sizeof(DecorFaceStyle));
490 DFS_FACE_TYPE(tail->next->style) =
491 VectorButton;
492 next = head->next;
493 head->next = NULL;
494 FreeDecorFace(dpy, head);
495 memcpy(head, next, sizeof(DecorFace));
496 free(next);
497 }
498 }
499 }
500 else
501 {
502 FreeDecorFace(dpy, &TB_STATE(*tb)[bs_start]);
503 memcpy(
504 &(TB_STATE(*tb)[bs_start]), &tmpdf,
505 sizeof(DecorFace));
506 for (i = bs_start + 1; i <= bs_end; ++i)
507 {
508 if (multiple && (i & use_mask) != set_mask)
509 {
510 continue;
511 }
512 ReadDecorFace(
513 spec, &TB_STATE(*tb)[i], button, False);
514 }
515 }
516 }
517 if (pstyle)
518 {
519 free(spec);
520 end++;
521 end = SkipSpaces(end, NULL, 0);
522 }
523
524 return end;
525 }
526
527 /* Remove the given decor from all windows */
__remove_window_decors(F_CMD_ARGS,FvwmDecor * d)528 static void __remove_window_decors(F_CMD_ARGS, FvwmDecor *d)
529 {
530 const exec_context_t *exc2;
531 exec_context_changes_t ecc;
532 FvwmWindow *t;
533
534 for (t = Scr.FvwmRoot.next; t; t = t->next)
535 {
536 if (t->decor == d)
537 {
538 /* remove the extra title height now because we delete
539 * the current decor before calling ChangeDecor(). */
540 t->g.frame.height -= t->decor->title_height;
541 t->decor = NULL;
542 ecc.w.fw = t;
543 ecc.w.wcontext = C_WINDOW;
544 exc2 = exc_clone_context(
545 exc, &ecc, ECC_FW | ECC_WCONTEXT);
546 execute_function(
547 cond_rc, exc2, "ChangeDecor Default", 0);
548 exc_destroy_context(exc2);
549 }
550 }
551
552 return;
553 }
554
do_title_style(F_CMD_ARGS,Bool do_add)555 static void do_title_style(F_CMD_ARGS, Bool do_add)
556 {
557 char *parm;
558 char *prev;
559 FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
560
561 Scr.flags.do_need_window_update = 1;
562 decor->flags.has_changed = 1;
563 decor->titlebar.flags.has_changed = 1;
564
565 for (prev = action ; (parm = PeekToken(action, &action)); prev = action)
566 {
567 if (!do_add && StrEquals(parm,"centered"))
568 {
569 TB_JUSTIFICATION(decor->titlebar) = JUST_CENTER;
570 }
571 else if (!do_add && StrEquals(parm,"leftjustified"))
572 {
573 TB_JUSTIFICATION(decor->titlebar) = JUST_LEFT;
574 }
575 else if (!do_add && StrEquals(parm,"rightjustified"))
576 {
577 TB_JUSTIFICATION(decor->titlebar) = JUST_RIGHT;
578 }
579 else if (!do_add && StrEquals(parm,"height"))
580 {
581 int height = 0;
582 int next = 0;
583
584 if (!action ||
585 sscanf(action, "%d%n", &height, &next) <= 0 ||
586 height < MIN_FONT_HEIGHT ||
587 height > MAX_FONT_HEIGHT)
588 {
589 if (height != 0)
590 {
591 fvwm_debug(__func__,
592 "bad height argument (height"
593 " must be from 5 to 256)");
594 height = 0;
595 }
596 }
597 if (decor->title_height != height ||
598 decor->min_title_height != 0)
599 {
600 decor->title_height = height;
601 decor->min_title_height = 0;
602 decor->flags.has_title_height_changed = 1;
603 }
604 if (action)
605 action += next;
606 }
607 else if (!do_add && StrEquals(parm,"MinHeight"))
608 {
609 int height = 0;
610 int next = 0;
611
612 if (!action ||
613 sscanf(action, "%d%n", &height, &next) <= 0 ||
614 height < MIN_FONT_HEIGHT ||
615 height > MAX_FONT_HEIGHT)
616 {
617 if (height < MIN_FONT_HEIGHT)
618 height = MIN_FONT_HEIGHT;
619 else if (height > MAX_FONT_HEIGHT)
620 height = 0;
621 }
622 if (decor->min_title_height != height)
623 {
624 decor->title_height = 0;
625 decor->min_title_height = height;
626 decor->flags.has_title_height_changed = 1;
627 }
628 if (action)
629 action += next;
630 }
631 else
632 {
633 action = ReadTitleButton(
634 prev, &decor->titlebar, do_add, -1);
635 }
636 }
637
638 return;
639 }
640
641 /*
642 *
643 * Reads a multi-pixmap titlebar config. (tril@igs.net)
644 *
645 */
ReadMultiPixmapDecor(char * s,DecorFace * df)646 static char *ReadMultiPixmapDecor(char *s, DecorFace *df)
647 {
648 static char *pm_names[TBMP_NUM_PIXMAPS+1] =
649 {
650 "Main",
651 "LeftMain",
652 "RightMain",
653 "LeftButtons",
654 "RightButtons",
655 "UnderText",
656 "LeftOfText",
657 "RightOfText",
658 "LeftEnd",
659 "RightEnd",
660 "Buttons",
661 NULL
662 };
663 FvwmPicture **pm;
664 FvwmAcs *acs;
665 Pixel *pixels;
666 char *token;
667 Bool stretched;
668 Bool load_pixmap = False;
669 int pm_id, i = 0;
670 FvwmPictureAttributes fpa;
671
672 df->style.face_type = MultiPixmap;
673 df->u.mp.pixmaps = pm = fxcalloc(TBMP_NUM_PIXMAPS, sizeof(FvwmPicture*));
674 df->u.mp.acs = acs = fxmalloc(TBMP_NUM_PIXMAPS * sizeof(FvwmAcs));
675 df->u.mp.pixels = pixels = fxmalloc(TBMP_NUM_PIXMAPS * sizeof(Pixel));
676 for(i=0; i < TBMP_NUM_PIXMAPS; i++)
677 {
678 acs[i].cs = -1;
679 acs[i].alpha_percent = 100;
680 }
681 s = GetNextTokenIndex(s, pm_names, 0, &pm_id);
682 while (pm_id >= 0)
683 {
684 stretched = False;
685 load_pixmap = False;
686 s = DoPeekToken(s, &token, ",()", NULL, NULL);
687 if (StrEquals(token, "stretched"))
688 {
689 stretched = True;
690 s = DoPeekToken(s, &token, ",", NULL, NULL);
691 }
692 else if (StrEquals(token, "tiled"))
693 {
694 s = DoPeekToken(s, &token, ",", NULL, NULL);
695 }
696 if (!token)
697 {
698 break;
699 }
700 if (pm[pm_id] || acs[pm_id].cs >= 0 ||
701 (df->u.mp.solid_flags & (1 << pm_id)))
702 {
703 fvwm_debug(__func__,
704 "Ignoring: already-specified %s",
705 pm_names[i]);
706 continue;
707 }
708 if (stretched)
709 {
710 df->u.mp.stretch_flags |= (1 << pm_id);
711 }
712 if (strncasecmp (token, "Colorset", 8) == 0)
713 {
714 int val;
715 char *tmp;
716
717 tmp = DoPeekToken(s, &token, ",", NULL, NULL);
718 if (!GetIntegerArguments(token, NULL, &val, 1) ||
719 val < 0)
720 {
721 fvwm_debug(__func__,
722 "Colorset should take one or two "
723 "positive integers as argument");
724 }
725 else
726 {
727 acs[pm_id].cs = val;
728 alloc_colorset(val);
729 s = tmp;
730 tmp = DoPeekToken(s, &token, ",", NULL, NULL);
731 if (GetIntegerArguments(token, NULL, &val, 1))
732 {
733 acs[pm_id].alpha_percent =
734 max(0, min(100,val));
735 s = tmp;
736 }
737 }
738 }
739 else if (strncasecmp(token, "TiledPixmap", 11) == 0)
740 {
741 s = DoPeekToken(s, &token, ",", NULL, NULL);
742 load_pixmap = True;
743 }
744 else if (strncasecmp(token, "AdjustedPixmap", 14) == 0)
745 {
746 s = DoPeekToken(s, &token, ",", NULL, NULL);
747 load_pixmap = True;
748 df->u.mp.stretch_flags |= (1 << pm_id);
749 }
750 else if (strncasecmp(token, "Solid", 5) == 0)
751 {
752 s = DoPeekToken(s, &token, ",", NULL, NULL);
753 if (token)
754 {
755 df->u.mp.pixels[pm_id] = GetColor(token);
756 df->u.mp.solid_flags |= (1 << pm_id);
757 }
758 }
759 else
760 {
761 load_pixmap = True;
762 }
763 if (load_pixmap && token)
764 {
765 fpa.mask = (Pdepth <= 8)? FPAM_DITHER:0; /* ? */
766 pm[pm_id] = PCacheFvwmPicture(
767 dpy, Scr.NoFocusWin, NULL, token, fpa);
768 if (!pm[pm_id])
769 {
770 fvwm_debug(__func__,
771 "Pixmap '%s' could not be loaded",
772 token);
773 }
774 }
775 if (pm_id == TBMP_BUTTONS)
776 {
777 if (pm[TBMP_LEFT_BUTTONS])
778 {
779 PDestroyFvwmPicture(dpy, pm[TBMP_LEFT_BUTTONS]);
780 }
781 if (pm[TBMP_RIGHT_BUTTONS])
782 {
783 PDestroyFvwmPicture(dpy, pm[TBMP_RIGHT_BUTTONS]);
784 }
785 df->u.mp.stretch_flags &= ~(1 << TBMP_LEFT_BUTTONS);
786 df->u.mp.stretch_flags &= ~(1 << TBMP_RIGHT_BUTTONS);
787 df->u.mp.solid_flags &= ~(1 << TBMP_LEFT_BUTTONS);
788 df->u.mp.solid_flags &= ~(1 << TBMP_RIGHT_BUTTONS);
789 if (pm[TBMP_BUTTONS])
790 {
791 pm[TBMP_LEFT_BUTTONS] =
792 PCloneFvwmPicture(pm[TBMP_BUTTONS]);
793 acs[TBMP_LEFT_BUTTONS].cs = -1;
794 pm[TBMP_RIGHT_BUTTONS] =
795 PCloneFvwmPicture(pm[TBMP_BUTTONS]);
796 acs[TBMP_RIGHT_BUTTONS].cs = -1;
797 }
798 else
799 {
800 pm[TBMP_RIGHT_BUTTONS] =
801 pm[TBMP_LEFT_BUTTONS] = NULL;
802 acs[TBMP_RIGHT_BUTTONS].cs =
803 acs[TBMP_LEFT_BUTTONS].cs =
804 acs[TBMP_BUTTONS].cs;
805 acs[TBMP_RIGHT_BUTTONS].alpha_percent =
806 acs[TBMP_LEFT_BUTTONS].alpha_percent =
807 acs[TBMP_BUTTONS].alpha_percent;
808 pixels[TBMP_LEFT_BUTTONS] =
809 pixels[TBMP_RIGHT_BUTTONS] =
810 pixels[TBMP_BUTTONS];
811 }
812 if (stretched)
813 {
814 df->u.mp.stretch_flags |=
815 (1 << TBMP_LEFT_BUTTONS) |
816 (1 << TBMP_RIGHT_BUTTONS);
817 }
818 if (df->u.mp.solid_flags & (1 << TBMP_BUTTONS))
819 {
820 df->u.mp.solid_flags |=
821 (1 << TBMP_LEFT_BUTTONS);
822 df->u.mp.solid_flags |=
823 (1 << TBMP_RIGHT_BUTTONS);
824 }
825 if (pm[TBMP_BUTTONS])
826 {
827 PDestroyFvwmPicture(dpy, pm[TBMP_BUTTONS]);
828 pm[TBMP_BUTTONS] = NULL;
829 }
830 acs[TBMP_BUTTONS].cs = -1;
831 df->u.mp.solid_flags &= ~(1 << TBMP_BUTTONS);
832 }
833 s = SkipSpaces(s, NULL, 0);
834 s = GetNextTokenIndex(s, pm_names, 0, &pm_id);
835 }
836
837 if (!(pm[TBMP_MAIN] || acs[TBMP_MAIN].cs >= 0 ||
838 (df->u.mp.solid_flags & TBMP_MAIN))
839 &&
840 !(pm[TBMP_LEFT_MAIN] || acs[TBMP_LEFT_MAIN].cs >= 0 ||
841 (df->u.mp.solid_flags & TBMP_LEFT_MAIN))
842 &&
843 !(pm[TBMP_RIGHT_MAIN] || acs[TBMP_RIGHT_MAIN].cs >= 0 ||
844 (df->u.mp.solid_flags & TBMP_RIGHT_MAIN)))
845 {
846 fvwm_debug(__func__,
847 "No Main pixmap/colorset/solid found for TitleStyle "
848 "MultiPixmap (you must specify either Main, "
849 "or both LeftMain and RightMain)");
850 for (i=0; i < TBMP_NUM_PIXMAPS; i++)
851 {
852 if (pm[i])
853 {
854 PDestroyFvwmPicture(dpy, pm[i]);
855 }
856 else if (!!(df->u.mp.solid_flags & i))
857 {
858 PictureFreeColors(
859 dpy, Pcmap, &df->u.mp.pixels[i], 1, 0,
860 False);
861 }
862 }
863 free(pm);
864 free(acs);
865 free(pixels);
866 return NULL;
867 }
868
869 return s;
870 }
871
872 /*
873 *
874 * DestroyFvwmDecor -- frees all memory assocated with an FvwmDecor
875 * structure, but does not free the FvwmDecor itself
876 *
877 */
DestroyFvwmDecor(FvwmDecor * decor)878 static void DestroyFvwmDecor(FvwmDecor *decor)
879 {
880 int i;
881 /* reset to default button set (frees allocated mem) */
882 DestroyAllButtons(decor);
883 for (i = 0; i < BS_MaxButtonState; ++i)
884 {
885 FreeDecorFace(dpy, &TB_STATE(decor->titlebar)[i]);
886 }
887 FreeDecorFace(dpy, &decor->BorderStyle.active);
888 FreeDecorFace(dpy, &decor->BorderStyle.inactive);
889 if (decor->tag)
890 {
891 free(decor->tag);
892 decor->tag = NULL;
893 }
894
895 return;
896 }
897
SetLayerButtonFlag(int layer,int multi,int set,FvwmDecor * decor,TitleButton * tb)898 static void SetLayerButtonFlag(
899 int layer, int multi, int set, FvwmDecor *decor, TitleButton *tb)
900 {
901 int i;
902 int start = 0;
903 int add = 2;
904
905 if (multi)
906 {
907 if (multi == 2)
908 {
909 start = 1;
910 }
911 else if (multi == 3)
912 {
913 add = 1;
914 }
915 for (i = start; i < NUMBER_OF_TITLE_BUTTONS; i += add)
916 {
917 if (set)
918 {
919 TB_FLAGS(decor->buttons[i]).has_layer = 1;
920 TB_LAYER(decor->buttons[i]) = layer;
921 }
922 else
923 {
924 TB_FLAGS(decor->buttons[i]).has_layer = 0;
925 }
926 }
927 }
928 else
929 {
930 if (set)
931 {
932 TB_FLAGS(*tb).has_layer = 1;
933 TB_LAYER(*tb) = layer;
934 }
935 else
936 {
937 TB_FLAGS(*tb).has_layer = 0;
938 }
939 }
940
941 return;
942 }
943
944 /*
945 *
946 * Changes a button decoration style (changes by veliaa@rpi.edu)
947 *
948 */
SetMWMButtonFlag(mwm_flags flag,int multi,int set,FvwmDecor * decor,TitleButton * tb)949 static void SetMWMButtonFlag(
950 mwm_flags flag, int multi, int set, FvwmDecor *decor, TitleButton *tb)
951 {
952 int i;
953 int start = 0;
954 int add = 2;
955
956 if (multi)
957 {
958 if (multi == 2)
959 {
960 start = 1;
961 }
962 else if (multi == 3)
963 {
964 add = 1;
965 }
966 for (i = start; i < NUMBER_OF_TITLE_BUTTONS; i += add)
967 {
968 if (set)
969 {
970 TB_MWM_DECOR_FLAGS(decor->buttons[i]) |= flag;
971 }
972 else
973 {
974 TB_MWM_DECOR_FLAGS(decor->buttons[i]) &= ~flag;
975 }
976 }
977 }
978 else
979 {
980 if (set)
981 {
982 TB_MWM_DECOR_FLAGS(*tb) |= flag;
983 }
984 else
985 {
986 TB_MWM_DECOR_FLAGS(*tb) &= ~flag;
987 }
988 }
989
990 return;
991 }
992
do_button_style(F_CMD_ARGS,Bool do_add)993 static void do_button_style(F_CMD_ARGS, Bool do_add)
994 {
995 int i;
996 int multi = 0;
997 int button = 0;
998 int do_return;
999 char *text = NULL;
1000 char *prev = NULL;
1001 char *parm = NULL;
1002 TitleButton *tb = NULL;
1003 FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
1004
1005 parm = PeekToken(action, &text);
1006 if (parm && isdigit(*parm))
1007 {
1008 button = atoi(parm);
1009 button = BUTTON_INDEX(button);
1010 }
1011
1012 if (parm == NULL || button >= NUMBER_OF_TITLE_BUTTONS || button < 0)
1013 {
1014 fvwm_debug(__func__, "Bad button style (1) in line %s",
1015 action);
1016 return;
1017 }
1018
1019 Scr.flags.do_need_window_update = 1;
1020
1021 do_return = 0;
1022 if (!isdigit(*parm))
1023 {
1024 if (StrEquals(parm,"left"))
1025 {
1026 multi = 1; /* affect all left buttons */
1027 }
1028 else if (StrEquals(parm,"right"))
1029 {
1030 multi = 2; /* affect all right buttons */
1031 }
1032 else if (StrEquals(parm,"all"))
1033 {
1034 multi = 3; /* affect all buttons */
1035 }
1036 else
1037 {
1038 /* we're either resetting buttons or an invalid button
1039 * set was specified */
1040 if (StrEquals(parm,"reset"))
1041 {
1042 ResetAllButtons(decor);
1043 }
1044 else
1045 {
1046 fvwm_debug(__func__,
1047 "Bad button style (2) in line %s",
1048 action);
1049 }
1050 multi = 3;
1051 do_return = 1;
1052 }
1053 }
1054 /* mark button style and decor as changed */
1055 decor->flags.has_changed = 1;
1056 if (multi == 0)
1057 {
1058 /* a single button was specified */
1059 tb = &decor->buttons[button];
1060 TB_FLAGS(*tb).has_changed = 1;
1061 }
1062 else
1063 {
1064 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
1065 {
1066 if (((multi & 1) && !(i & 1)) ||
1067 ((multi & 2) && (i & 1)))
1068 {
1069 TB_FLAGS(decor->buttons[i]).has_changed = 1;
1070 }
1071 }
1072 }
1073 if (do_return == 1)
1074 {
1075 return;
1076 }
1077 for (prev = text; (parm = PeekToken(text, &text)); prev = text)
1078 {
1079 if (!do_add && strcmp(parm,"-") == 0)
1080 {
1081 char *tok;
1082 text = GetNextToken(text, &tok);
1083 while (tok)
1084 {
1085 int set = 1;
1086 char *old_tok = NULL;
1087
1088 if (*tok == '!')
1089 {
1090 /* flag negate */
1091 set = 0;
1092 old_tok = tok;
1093 tok++;
1094 }
1095 if (StrEquals(tok,"Clear"))
1096 {
1097 if (multi)
1098 {
1099 for (i = 0;
1100 i < NUMBER_OF_TITLE_BUTTONS; ++i)
1101 {
1102 if (((multi & 1) &&
1103 !(i & 1)) ||
1104 ((multi & 2) &&
1105 (i & 1)))
1106 {
1107 TB_JUSTIFICATION(decor->buttons[i]) =
1108 (set) ? JUST_CENTER : JUST_RIGHT;
1109 memset(&TB_FLAGS(decor->buttons[i]), (set) ? 0 : 0xff,
1110 sizeof(TB_FLAGS(decor->buttons[i])));
1111 /* ? not very useful if set == 0 ? */
1112 }
1113 }
1114 }
1115 else
1116 {
1117 TB_JUSTIFICATION(*tb) = (set) ?
1118 JUST_CENTER :
1119 JUST_RIGHT;
1120 memset(&TB_FLAGS(*tb), (set) ?
1121 0 : 0xff,
1122 sizeof(TB_FLAGS(*tb)));
1123 /* ? not very useful if
1124 * set == 0 ? */
1125 }
1126 }
1127 else if (StrEquals(tok, "MWMDecorMenu"))
1128 {
1129 SetMWMButtonFlag(
1130 MWM_DECOR_MENU, multi, set,
1131 decor, tb);
1132 }
1133 else if (StrEquals(tok, "MWMDecorMin"))
1134 {
1135 SetMWMButtonFlag(
1136 MWM_DECOR_MINIMIZE, multi, set,
1137 decor, tb);
1138 }
1139 else if (StrEquals(tok, "MWMDecorMax"))
1140 {
1141 SetMWMButtonFlag(
1142 MWM_DECOR_MAXIMIZE, multi, set,
1143 decor, tb);
1144 }
1145 else if (StrEquals(tok, "MWMDecorShade"))
1146 {
1147 SetMWMButtonFlag(
1148 MWM_DECOR_SHADE, multi, set,
1149 decor, tb);
1150 }
1151 else if (StrEquals(tok, "MWMDecorStick"))
1152 {
1153 SetMWMButtonFlag(
1154 MWM_DECOR_STICK, multi, set,
1155 decor, tb);
1156 }
1157 else if (StrEquals(tok, "MwmDecorLayer"))
1158 {
1159 int layer, got_number;
1160 char *ltok;
1161 text = GetNextToken(text, <ok);
1162 if (ltok)
1163 {
1164 got_number =
1165 (sscanf(ltok, "%d",
1166 &layer) == 1);
1167 free (ltok);
1168 }
1169 else
1170 {
1171 got_number = 0;
1172 }
1173 if (!ltok || !got_number)
1174 {
1175 fvwm_debug(__func__,
1176 "could not read"
1177 " integer value for"
1178 " layer -- line: %s",
1179 text);
1180 }
1181 else
1182 {
1183 SetLayerButtonFlag(
1184 layer, multi, set,
1185 decor, tb);
1186 }
1187
1188 }
1189 else
1190 {
1191 fvwm_debug(__func__,
1192 "unknown title button flag"
1193 " %s -- line: %s", tok,
1194 text);
1195 }
1196 if (set)
1197 {
1198 free(tok);
1199 }
1200 else
1201 {
1202 free(old_tok);
1203 }
1204 text = GetNextToken(text, &tok);
1205 }
1206 break;
1207 }
1208 else
1209 {
1210 if (multi)
1211 {
1212 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
1213 {
1214 if (((multi & 1) && !(i & 1)) ||
1215 ((multi & 2) && (i & 1)))
1216 {
1217 text = ReadTitleButton(
1218 prev,
1219 &decor->buttons[i],
1220 do_add, i);
1221 }
1222 }
1223 }
1224 else if (!(text = ReadTitleButton(
1225 prev, tb, do_add, button)))
1226 {
1227 break;
1228 }
1229 }
1230 }
1231
1232 return;
1233 }
1234
1235 static
update_decorface_colorset(DecorFace * df,int cset)1236 int update_decorface_colorset(DecorFace *df, int cset)
1237 {
1238 DecorFace *tdf;
1239 int has_changed = 0;
1240
1241 for(tdf = df; tdf != NULL; tdf = tdf->next)
1242 {
1243 if (DFS_FACE_TYPE(tdf->style) == ColorsetButton &&
1244 tdf->u.acs.cs == cset)
1245 {
1246 tdf->flags.has_changed = 1;
1247 has_changed = 1;
1248 }
1249 else if (DFS_FACE_TYPE(tdf->style) == MultiPixmap)
1250 {
1251 int i;
1252
1253 for (i = 0; i < TBMP_NUM_PIXMAPS; i++)
1254 {
1255 if (tdf->u.mp.acs[i].cs == cset)
1256 {
1257 tdf->flags.has_changed = 1;
1258 has_changed = 1;
1259 }
1260 }
1261 }
1262 }
1263
1264 return has_changed;
1265 }
1266
1267 static
update_titlebutton_colorset(TitleButton * tb,int cset)1268 int update_titlebutton_colorset(TitleButton *tb, int cset)
1269 {
1270 int i;
1271 int has_changed = 0;
1272
1273 for(i = 0; i < BS_MaxButtonState; i++)
1274 {
1275 tb->state[i].flags.has_changed =
1276 update_decorface_colorset(&(tb->state[i]), cset);
1277 has_changed |= tb->state[i].flags.has_changed;
1278 }
1279 return has_changed;
1280 }
1281
1282 static
update_decors_colorset(int cset)1283 void update_decors_colorset(int cset)
1284 {
1285 int i;
1286 FvwmDecor *decor = &Scr.DefaultDecor;
1287
1288 for(decor = &Scr.DefaultDecor; decor != NULL; decor = decor->next)
1289 {
1290 for(i = 0; i < NUMBER_OF_TITLE_BUTTONS; i++)
1291 {
1292 decor->flags.has_changed |= update_titlebutton_colorset(
1293 &(decor->buttons[i]), cset);
1294 }
1295 decor->flags.has_changed |= update_titlebutton_colorset(
1296 &(decor->titlebar), cset);
1297 decor->flags.has_changed |= update_decorface_colorset(
1298 &(decor->BorderStyle.active), cset);
1299 decor->flags.has_changed |= update_decorface_colorset(
1300 &(decor->BorderStyle.inactive), cset);
1301 if (decor->flags.has_changed)
1302 {
1303 Scr.flags.do_need_window_update = 1;
1304 }
1305 }
1306 }
1307
__parse_vector_line_one_coord(char ** ret_action,int * pcoord,int * poff,char * action)1308 static Bool __parse_vector_line_one_coord(
1309 char **ret_action, int *pcoord, int *poff, char *action)
1310 {
1311 int offset;
1312 int n;
1313
1314 *ret_action = action;
1315 n = sscanf(action, "%d%n", pcoord, &offset);
1316 if (n < 1)
1317 {
1318 return False;
1319 }
1320 action += offset;
1321 /* check for offest */
1322 if (*action == '+' || *action == '-')
1323 {
1324 n = sscanf(action, "%dp%n", poff, &offset);
1325 if (n < 1)
1326 {
1327 return False;
1328 }
1329 if (*poff < -128)
1330 {
1331 *poff = -128;
1332 }
1333 else if (*poff > 127)
1334 {
1335 *poff = 127;
1336 }
1337 action += offset;
1338 }
1339 else
1340 {
1341 *poff = 0;
1342 }
1343 *ret_action = action;
1344
1345 return True;
1346 }
1347
__parse_vector_line(char ** ret_action,int * px,int * py,int * pxoff,int * pyoff,int * pc,char * action)1348 static Bool __parse_vector_line(
1349 char **ret_action, int *px, int *py, int *pxoff, int *pyoff, int *pc,
1350 char *action)
1351 {
1352 Bool is_valid = True;
1353 int offset;
1354 int n;
1355
1356 *ret_action = action;
1357 if (__parse_vector_line_one_coord(&action, px, pxoff, action) == False)
1358 {
1359 return False;
1360 }
1361 if (*action != 'x')
1362 {
1363 return False;
1364 }
1365 action++;
1366 if (__parse_vector_line_one_coord(&action, py, pyoff, action) == False)
1367 {
1368 return False;
1369 }
1370 if (*action != '@')
1371 {
1372 return False;
1373 }
1374 action++;
1375 /* read the line style */
1376 n = sscanf(action, "%d%n", pc, &offset);
1377 if (n < 1)
1378 {
1379 return False;
1380 }
1381 action += offset;
1382 *ret_action = action;
1383
1384 return is_valid;
1385 }
1386
1387 /* ---------------------------- interface functions ------------------------ */
1388
refresh_window(Window w,Bool window_update)1389 void refresh_window(Window w, Bool window_update)
1390 {
1391 XSetWindowAttributes attributes;
1392 unsigned long valuemask;
1393
1394 valuemask = CWOverrideRedirect | CWBackingStore | CWSaveUnder |
1395 CWBackPixmap;
1396 attributes.override_redirect = True;
1397 attributes.save_under = False;
1398 attributes.background_pixmap = None;
1399 attributes.backing_store = NotUseful;
1400 w = XCreateWindow(
1401 dpy, w, 0, 0, monitor_get_all_widths(),
1402 monitor_get_all_heights(), 0,
1403 CopyFromParent, CopyFromParent, CopyFromParent, valuemask,
1404 &attributes);
1405 XMapWindow(dpy, w);
1406 if (Scr.flags.do_need_window_update && window_update)
1407 {
1408 flush_window_updates();
1409 }
1410 XDestroyWindow(dpy, w);
1411 XSync(dpy, 0);
1412 handle_all_expose();
1413
1414 return;
1415 }
1416
ApplyDefaultFontAndColors(void)1417 void ApplyDefaultFontAndColors(void)
1418 {
1419 XGCValues gcv;
1420 unsigned long gcm;
1421 int cset = Scr.DefaultColorset;
1422
1423 /* make GC's */
1424 gcm = GCFunction|GCLineWidth|GCForeground|GCBackground;
1425 gcv.function = GXcopy;
1426 if (Scr.DefaultFont->font)
1427 {
1428 gcm |= GCFont;
1429 gcv.font = Scr.DefaultFont->font->fid;
1430 }
1431 gcv.line_width = 0;
1432 if (cset >= 0)
1433 {
1434 gcv.foreground = Colorset[cset].fg;
1435 gcv.background = Colorset[cset].bg;
1436 }
1437 else
1438 {
1439 gcv.foreground = Scr.StdFore;
1440 gcv.background = Scr.StdBack;
1441 }
1442 if (Scr.StdGC)
1443 {
1444 XChangeGC(dpy, Scr.StdGC, gcm, &gcv);
1445 }
1446 else
1447 {
1448 Scr.StdGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
1449 }
1450
1451 gcm = GCFunction|GCLineWidth|GCForeground;
1452 if (cset >= 0)
1453 {
1454 gcv.foreground = Colorset[cset].hilite;
1455 }
1456 else
1457 {
1458 gcv.foreground = Scr.StdHilite;
1459 }
1460 if (Scr.StdReliefGC)
1461 {
1462 XChangeGC(dpy, Scr.StdReliefGC, gcm, &gcv);
1463 }
1464 else
1465 {
1466 Scr.StdReliefGC = fvwmlib_XCreateGC(
1467 dpy, Scr.NoFocusWin, gcm, &gcv);
1468 }
1469 if (cset >= 0)
1470 {
1471 gcv.foreground = Colorset[cset].shadow;
1472 }
1473 else
1474 {
1475 gcv.foreground = Scr.StdShadow;
1476 }
1477 if (Scr.StdShadowGC)
1478 {
1479 XChangeGC(dpy, Scr.StdShadowGC, gcm, &gcv);
1480 }
1481 else
1482 {
1483 Scr.StdShadowGC = fvwmlib_XCreateGC(
1484 dpy, Scr.NoFocusWin, gcm, &gcv);
1485 }
1486 /* update the geometry window for move/resize */
1487 if (Scr.SizeWindow.win != None)
1488 {
1489 resize_geometry_window();
1490 }
1491 UpdateAllMenuStyles();
1492
1493 return;
1494 }
1495
FreeDecorFace(Display * dpy,DecorFace * df)1496 void FreeDecorFace(Display *dpy, DecorFace *df)
1497 {
1498 int i;
1499
1500 switch (DFS_FACE_TYPE(df->style))
1501 {
1502 case GradientButton:
1503 if (df->u.grad.d_pixels != NULL && df->u.grad.d_npixels)
1504 {
1505 PictureFreeColors(
1506 dpy, Pcmap, df->u.grad.d_pixels,
1507 df->u.grad.d_npixels, 0, False);
1508 free(df->u.grad.d_pixels);
1509 }
1510 else if (Pdepth <= 8 && df->u.grad.xcs != NULL &&
1511 df->u.grad.npixels > 0 && !df->u.grad.do_dither)
1512 {
1513 Pixel *p;
1514 int i;
1515
1516 p = fxmalloc(df->u.grad.npixels * sizeof(Pixel));
1517 for(i=0; i < df->u.grad.npixels; i++)
1518 {
1519 p[i] = df->u.grad.xcs[i].pixel;
1520 }
1521 PictureFreeColors(
1522 dpy, Pcmap, p, df->u.grad.npixels, 0, False);
1523 free(p);
1524 }
1525 if (df->u.grad.xcs != NULL)
1526 {
1527 free(df->u.grad.xcs);
1528 }
1529 break;
1530
1531 case PixmapButton:
1532 case TiledPixmapButton:
1533 case StretchedPixmapButton:
1534 case AdjustedPixmapButton:
1535 case ShrunkPixmapButton:
1536 if (df->u.p)
1537 {
1538 PDestroyFvwmPicture(dpy, df->u.p);
1539 }
1540 break;
1541
1542 case MultiPixmap:
1543 if (df->u.mp.pixmaps)
1544 {
1545 for (i = 0; i < TBMP_NUM_PIXMAPS; i++)
1546 {
1547 if (df->u.mp.pixmaps[i])
1548 {
1549 PDestroyFvwmPicture(
1550 dpy, df->u.mp.pixmaps[i]);
1551 }
1552 else if (!!(df->u.mp.solid_flags & i))
1553 {
1554 PictureFreeColors(
1555 dpy, Pcmap, &df->u.mp.pixels[i],
1556 1, 0, False);
1557 }
1558 }
1559 free(df->u.mp.pixmaps);
1560 }
1561 if (df->u.mp.acs)
1562 {
1563 free(df->u.mp.acs);
1564 }
1565 if (df->u.mp.pixels)
1566 {
1567 free(df->u.mp.pixels);
1568 }
1569 break;
1570 case VectorButton:
1571 case DefaultVectorButton:
1572 if (df->u.vector.x)
1573 {
1574 free (df->u.vector.x);
1575 }
1576 if (df->u.vector.y)
1577 {
1578 free (df->u.vector.y);
1579 }
1580 /* free offsets for coord */
1581 if (df->u.vector.xoff)
1582 {
1583 free(df->u.vector.xoff);
1584 }
1585 if (df->u.vector.yoff)
1586 {
1587 free(df->u.vector.yoff);
1588 }
1589 if (df->u.vector.c)
1590 {
1591 free (df->u.vector.c);
1592 }
1593 break;
1594 default:
1595 /* see below */
1596 break;
1597 }
1598 /* delete any compound styles */
1599 if (df->next)
1600 {
1601 FreeDecorFace(dpy, df->next);
1602 free(df->next);
1603 }
1604 df->next = NULL;
1605 memset(&df->style, 0, sizeof(df->style));
1606 memset(&df->u, 0, sizeof(df->u));
1607 DFS_FACE_TYPE(df->style) = SimpleButton;
1608
1609 return;
1610 }
1611
1612 /*
1613 *
1614 * Reads a button face line into a structure (veliaa@rpi.edu)
1615 *
1616 */
ReadDecorFace(char * s,DecorFace * df,int button,int verbose)1617 Bool ReadDecorFace(char *s, DecorFace *df, int button, int verbose)
1618 {
1619 int offset;
1620 char style[256], *file;
1621 char *action = s;
1622
1623 /* some variants of scanf do not increase the assign count when %n is
1624 * used, so a return value of 1 is no error. */
1625 if (sscanf(s, "%255s%n", style, &offset) < 1)
1626 {
1627 if (verbose)
1628 {
1629 fvwm_debug(__func__, "error in face `%s'", s);
1630 }
1631 return False;
1632 }
1633 style[255] = 0;
1634
1635 if (strncasecmp(style, "--", 2) != 0)
1636 {
1637 s += offset;
1638
1639 FreeDecorFace(dpy, df);
1640
1641 /* determine button style */
1642 if (strncasecmp(style,"Simple",6)==0)
1643 {
1644 memset(&df->style, 0, sizeof(df->style));
1645 DFS_FACE_TYPE(df->style) = SimpleButton;
1646 }
1647 else if (strncasecmp(style,"Default",7)==0)
1648 {
1649 int b = -1, n = sscanf(s, "%d%n", &b, &offset);
1650
1651 if (n < 1)
1652 {
1653 if (button == -1)
1654 {
1655 if (verbose)
1656 {
1657 fvwm_debug(__func__,
1658 "need default button"
1659 " number to load");
1660 }
1661 return False;
1662 }
1663 b = button;
1664 }
1665 else
1666 {
1667 b = BUTTON_INDEX(b);
1668 s += offset;
1669 }
1670 if (b >= 0 && b < NUMBER_OF_TITLE_BUTTONS)
1671 {
1672 LoadDefaultButton(df, b);
1673 }
1674 else
1675 {
1676 if (verbose)
1677 {
1678 fvwm_debug(__func__,
1679 "button number out of range:"
1680 " %d", b);
1681 }
1682 return False;
1683 }
1684 }
1685 else if (strncasecmp(style,"Vector",6)==0 ||
1686 (strlen(style)<=2 && isdigit(*style)))
1687 {
1688 /* normal coordinate list button style */
1689 int i, num_coords, num;
1690 struct vector_coords *vc = &df->u.vector;
1691
1692 /* get number of points */
1693 if (strncasecmp(style,"Vector",6)==0)
1694 {
1695 num = sscanf(s,"%d%n",&num_coords,&offset);
1696 s += offset;
1697 }
1698 else
1699 {
1700 num = sscanf(style,"%d",&num_coords);
1701 }
1702
1703 if (num < 1 || num_coords<2 ||
1704 num_coords > MAX_TITLE_BUTTON_VECTOR_LINES)
1705 {
1706 if (verbose)
1707 {
1708 fvwm_debug(__func__,
1709 "Bad button style (2) in line:"
1710 " %s",action);
1711 }
1712 return False;
1713 }
1714
1715 vc->num = num_coords;
1716 vc->use_fgbg = 0;
1717 vc->x = fxmalloc(sizeof(char) * num_coords);
1718 vc->y = fxmalloc(sizeof(char) * num_coords);
1719 vc->xoff = fxmalloc(sizeof(char) * num_coords);
1720 vc->yoff = fxmalloc(sizeof(char) * num_coords);
1721 vc->c = fxmalloc(sizeof(char) * num_coords);
1722
1723 /* get the points */
1724 for (i = 0; i < vc->num; ++i)
1725 {
1726 int x;
1727 int y;
1728 int xoff = 0;
1729 int yoff = 0;
1730 int c;
1731
1732 if (__parse_vector_line(
1733 &s, &x, &y, &xoff, &yoff, &c, s) ==
1734 False)
1735 {
1736 break;
1737 }
1738 if (x < 0)
1739 {
1740 x = 0;
1741 }
1742 if (x > 100)
1743 {
1744 x = 100;
1745 }
1746 if (y < 0)
1747 {
1748 y = 0;
1749 }
1750 if (y > 100)
1751 {
1752 y = 100;
1753 }
1754 if (c < 0 || c > 4)
1755 {
1756 c = 4;
1757 }
1758 vc->x[i] = x;
1759 vc->y[i] = y;
1760 vc->c[i] = c;
1761 vc->xoff[i] = xoff;
1762 vc->yoff[i] = yoff;
1763 if (c == 2 || c == 3)
1764 {
1765 vc->use_fgbg = 1;
1766 }
1767 }
1768 if (i < vc->num)
1769 {
1770 if (verbose)
1771 {
1772 fvwm_debug(__func__,
1773 "Bad button style (3) in line"
1774 " %s", action);
1775 }
1776 free(vc->x);
1777 free(vc->y);
1778 free(vc->c);
1779 free(vc->xoff);
1780 free(vc->yoff);
1781 vc->x = NULL;
1782 vc->y = NULL;
1783 vc->c = NULL;
1784 vc->xoff = NULL;
1785 vc->yoff = NULL;
1786 return False;
1787 }
1788 memset(&df->style, 0, sizeof(df->style));
1789 DFS_FACE_TYPE(df->style) = VectorButton;
1790 }
1791 else if (strncasecmp(style,"Solid",5)==0)
1792 {
1793 s = GetNextToken(s, &file);
1794 if (file)
1795 {
1796 memset(&df->style, 0, sizeof(df->style));
1797 DFS_FACE_TYPE(df->style) = SolidButton;
1798 df->u.back = GetColor(file);
1799 free(file);
1800 }
1801 else
1802 {
1803 if (verbose)
1804 {
1805 fvwm_debug(__func__,
1806 "no color given for Solid"
1807 " face type: %s", action);
1808 }
1809 return False;
1810 }
1811 }
1812 else if (strncasecmp(style+1, "Gradient", 8)==0)
1813 {
1814 char **s_colors;
1815 int npixels, nsegs, *perc;
1816 XColor *xcs;
1817 Bool do_dither = False;
1818
1819 if (!IsGradientTypeSupported(style[0]))
1820 {
1821 return False;
1822 }
1823 /* translate the gradient string into an array of
1824 * colors etc */
1825 npixels = ParseGradient(
1826 s, &s, &s_colors, &perc, &nsegs);
1827 while (*s && isspace(*s))
1828 {
1829 s++;
1830 }
1831 if (npixels <= 0)
1832 {
1833 return False;
1834 }
1835 /* grab the colors */
1836 if (Pdepth <= 16)
1837 {
1838 do_dither = True;
1839 }
1840 xcs = AllocAllGradientColors(
1841 s_colors, perc, nsegs, npixels, do_dither);
1842 if (xcs == None)
1843 return False;
1844 df->u.grad.xcs = xcs;
1845 df->u.grad.npixels = npixels;
1846 df->u.grad.do_dither = do_dither;
1847 df->u.grad.d_pixels = NULL;
1848 memset(&df->style, 0, sizeof(df->style));
1849 DFS_FACE_TYPE(df->style) = GradientButton;
1850 df->u.grad.gradient_type = toupper(style[0]);
1851 }
1852 else if (strncasecmp(style,"Pixmap",6)==0
1853 || strncasecmp(style,"TiledPixmap",11)==0
1854 || strncasecmp(style,"StretchedPixmap",15)==0
1855 || strncasecmp(style,"AdjustedPixmap",14)==0
1856 || strncasecmp(style,"ShrunkPixmap",12)==0)
1857 {
1858 FvwmPictureAttributes fpa;
1859
1860 s = GetNextToken(s, &file);
1861 fpa.mask = (Pdepth <= 8)? FPAM_DITHER:0; /* ? */
1862 df->u.p = PCacheFvwmPicture(
1863 dpy, Scr.NoFocusWin, NULL, file, fpa);
1864 if (df->u.p == NULL)
1865 {
1866 if (file)
1867 {
1868 if (verbose)
1869 {
1870 fvwm_debug(__func__,
1871 "couldn't load pixmap"
1872 " %s", file);
1873 }
1874 free(file);
1875 }
1876 return False;
1877 }
1878 if (file)
1879 {
1880 free(file);
1881 file = NULL;
1882 }
1883
1884 memset(&df->style, 0, sizeof(df->style));
1885 if (strncasecmp(style,"Tiled",5)==0)
1886 {
1887 DFS_FACE_TYPE(df->style) = TiledPixmapButton;
1888 }
1889 else if (strncasecmp(style,"Stretched",9)==0)
1890 {
1891 DFS_FACE_TYPE(df->style) =
1892 StretchedPixmapButton;
1893 }
1894 else if (strncasecmp(style,"Adjusted",8)==0)
1895 {
1896 DFS_FACE_TYPE(df->style) =
1897 AdjustedPixmapButton;
1898 }
1899 else if (strncasecmp(style,"Shrunk",6)==0)
1900 {
1901 DFS_FACE_TYPE(df->style) =
1902 ShrunkPixmapButton;
1903 }
1904 else
1905 {
1906 DFS_FACE_TYPE(df->style) = PixmapButton;
1907 }
1908 }
1909 else if (strncasecmp(style,"MultiPixmap",11)==0)
1910 {
1911 if (button != -1)
1912 {
1913 if (verbose)
1914 {
1915 fvwm_debug(__func__,
1916 "MultiPixmap is only valid"
1917 " for TitleStyle");
1918 }
1919 return False;
1920 }
1921 s = ReadMultiPixmapDecor(s, df);
1922 if (!s)
1923 {
1924 return False;
1925 }
1926 }
1927 else if (FMiniIconsSupported &&
1928 strncasecmp (style, "MiniIcon", 8) == 0)
1929 {
1930 memset(&df->style, 0, sizeof(df->style));
1931 DFS_FACE_TYPE(df->style) = MiniIconButton;
1932 /* pixmap read in when the window is created */
1933 df->u.p = NULL;
1934 }
1935 else if (strncasecmp (style, "Colorset", 8) == 0)
1936 {
1937 int val[2];
1938 int n;
1939
1940 n = GetIntegerArguments(s, NULL, val, 2);
1941 if (n == 0)
1942 {
1943 }
1944 memset(&df->style, 0, sizeof(df->style));
1945 if (n > 0 && val[0] >= 0)
1946 {
1947
1948 df->u.acs.cs = val[0];
1949 alloc_colorset(val[0]);
1950 DFS_FACE_TYPE(df->style) = ColorsetButton;
1951 }
1952 df->u.acs.alpha_percent = 100;
1953 if (n > 1)
1954 {
1955 df->u.acs.alpha_percent =
1956 max(0, min(100,val[1]));
1957 }
1958 s = SkipNTokens(s, n);
1959 }
1960 else
1961 {
1962 if (verbose)
1963 {
1964 fvwm_debug(__func__,
1965 "unknown style %s: %s", style,
1966 action);
1967 }
1968 return False;
1969 }
1970 }
1971
1972 /* Process button flags ("--" signals start of flags,
1973 it is also checked for above) */
1974 s = GetNextToken(s, &file);
1975 if (file && (strcmp(file,"--")==0))
1976 {
1977 char *tok;
1978 s = GetNextToken(s, &tok);
1979 while (tok && *tok)
1980 {
1981 int set = 1;
1982 char *old_tok = NULL;
1983
1984 if (*tok == '!')
1985 { /* flag negate */
1986 set = 0;
1987 old_tok = tok;
1988 tok++;
1989 }
1990 if (StrEquals(tok,"Clear"))
1991 {
1992 memset(&DFS_FLAGS(df->style), (set) ? 0 : 0xff,
1993 sizeof(DFS_FLAGS(df->style)));
1994 /* ? what is set == 0 good for ? */
1995 }
1996 else if (StrEquals(tok,"Left"))
1997 {
1998 if (set)
1999 {
2000 DFS_H_JUSTIFICATION(df->style) =
2001 JUST_LEFT;
2002 }
2003 else
2004 {
2005 DFS_H_JUSTIFICATION(df->style) =
2006 JUST_RIGHT;
2007 }
2008 }
2009 else if (StrEquals(tok,"Right"))
2010 {
2011 if (set)
2012 {
2013 DFS_H_JUSTIFICATION(df->style) =
2014 JUST_RIGHT;
2015 }
2016 else
2017 {
2018 DFS_H_JUSTIFICATION(df->style) =
2019 JUST_LEFT;
2020 }
2021 }
2022 else if (StrEquals(tok,"Centered"))
2023 {
2024 DFS_H_JUSTIFICATION(df->style) = JUST_CENTER;
2025 DFS_V_JUSTIFICATION(df->style) = JUST_CENTER;
2026 }
2027 else if (StrEquals(tok,"Top"))
2028 {
2029 if (set)
2030 {
2031 DFS_V_JUSTIFICATION(df->style) =
2032 JUST_TOP;
2033 }
2034 else
2035 {
2036 DFS_V_JUSTIFICATION(df->style) =
2037 JUST_BOTTOM;
2038 }
2039 }
2040 else if (StrEquals(tok,"Bottom"))
2041 {
2042 if (set)
2043 {
2044 DFS_V_JUSTIFICATION(df->style) =
2045 JUST_BOTTOM;
2046 }
2047 else
2048 {
2049 DFS_V_JUSTIFICATION(df->style) =
2050 JUST_TOP;
2051 }
2052 }
2053 else if (StrEquals(tok,"Flat"))
2054 {
2055 if (set)
2056 {
2057 DFS_BUTTON_RELIEF(df->style) =
2058 DFS_BUTTON_IS_FLAT;
2059 }
2060 else if (DFS_BUTTON_RELIEF(df->style) ==
2061 DFS_BUTTON_IS_FLAT)
2062 {
2063 DFS_BUTTON_RELIEF(df->style) =
2064 DFS_BUTTON_IS_UP;
2065 }
2066 }
2067 else if (StrEquals(tok,"Sunk"))
2068 {
2069 if (set)
2070 {
2071 DFS_BUTTON_RELIEF(df->style) =
2072 DFS_BUTTON_IS_SUNK;
2073 }
2074 else if (DFS_BUTTON_RELIEF(df->style) ==
2075 DFS_BUTTON_IS_SUNK)
2076 {
2077 DFS_BUTTON_RELIEF(df->style) =
2078 DFS_BUTTON_IS_UP;
2079 }
2080 }
2081 else if (StrEquals(tok,"Raised"))
2082 {
2083 if (set)
2084 {
2085 DFS_BUTTON_RELIEF(df->style) =
2086 DFS_BUTTON_IS_UP;
2087 }
2088 else
2089 {
2090 DFS_BUTTON_RELIEF(df->style) =
2091 DFS_BUTTON_IS_SUNK;
2092 }
2093 }
2094 else if (StrEquals(tok,"UseTitleStyle"))
2095 {
2096 if (set)
2097 {
2098 DFS_USE_TITLE_STYLE(df->style) = 1;
2099 DFS_USE_BORDER_STYLE(df->style) = 0;
2100 }
2101 else
2102 DFS_USE_TITLE_STYLE(df->style) = 0;
2103 }
2104 else if (StrEquals(tok,"HiddenHandles"))
2105 {
2106 DFS_HAS_HIDDEN_HANDLES(df->style) = !!set;
2107 }
2108 else if (StrEquals(tok,"NoInset"))
2109 {
2110 DFS_HAS_NO_INSET(df->style) = !!set;
2111 }
2112 else if (StrEquals(tok,"UseBorderStyle"))
2113 {
2114 if (set)
2115 {
2116 DFS_USE_BORDER_STYLE(df->style) = 1;
2117 DFS_USE_TITLE_STYLE(df->style) = 0;
2118 }
2119 else
2120 {
2121 DFS_USE_BORDER_STYLE(df->style) = 0;
2122 }
2123 }
2124 else if (verbose)
2125 {
2126 fvwm_debug(__func__,
2127 "unknown button face flag '%s'"
2128 " -- line: %s", tok, action);
2129 }
2130 if (set)
2131 {
2132 free(tok);
2133 }
2134 else
2135 {
2136 free(old_tok);
2137 }
2138 s = GetNextToken(s, &tok);
2139 }
2140 }
2141 if (file)
2142 {
2143 free(file);
2144 }
2145
2146 return True;
2147 }
2148
2149 /*
2150 *
2151 * Diverts a style definition to an FvwmDecor structure (veliaa@rpi.edu)
2152 *
2153 */
AddToDecor(F_CMD_ARGS,FvwmDecor * decor)2154 void AddToDecor(F_CMD_ARGS, FvwmDecor *decor)
2155 {
2156 if (!action)
2157 {
2158 return;
2159 }
2160 while (*action && isspace((unsigned char)*action))
2161 {
2162 ++action;
2163 }
2164 if (!*action)
2165 {
2166 return;
2167 }
2168 Scr.cur_decor = decor;
2169 execute_function(cond_rc, exc, action, 0);
2170 Scr.cur_decor = NULL;
2171
2172 return;
2173 }
2174
2175 /*
2176 *
2177 * InitFvwmDecor -- initializes an FvwmDecor structure to defaults
2178 *
2179 */
InitFvwmDecor(FvwmDecor * decor)2180 void InitFvwmDecor(FvwmDecor *decor)
2181 {
2182 int i;
2183 DecorFace tmpdf;
2184
2185 /* zero out the structures */
2186 memset(decor, 0, sizeof (FvwmDecor));
2187 memset(&tmpdf, 0, sizeof(DecorFace));
2188
2189 /* initialize title-bar button styles */
2190 DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
2191 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
2192 {
2193 int j = 0;
2194 for (; j < BS_MaxButtonState; ++j)
2195 {
2196 TB_STATE(decor->buttons[i])[j] = tmpdf;
2197 }
2198 }
2199 /* reset to default button set */
2200 ResetAllButtons(decor);
2201 /* initialize title-bar styles */
2202 for (i = 0; i < BS_MaxButtonState; ++i)
2203 {
2204 DFS_FACE_TYPE(
2205 TB_STATE(decor->titlebar)[i].style) = SimpleButton;
2206 }
2207
2208 /* initialize border texture styles */
2209 DFS_FACE_TYPE(decor->BorderStyle.active.style) = SimpleButton;
2210 DFS_FACE_TYPE(decor->BorderStyle.inactive.style) = SimpleButton;
2211
2212 return;
2213 }
2214
reset_decor_changes(void)2215 void reset_decor_changes(void)
2216 {
2217 FvwmDecor *decor;
2218 for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
2219 {
2220 decor->flags.has_changed = 0;
2221 decor->flags.has_title_height_changed = 0;
2222 }
2223 /* todo: must reset individual change flags too */
2224
2225 return;
2226 }
2227
update_fvwm_colorset(int cset)2228 void update_fvwm_colorset(int cset)
2229 {
2230 if (cset == Scr.DefaultColorset)
2231 {
2232 Scr.flags.do_need_window_update = 1;
2233 Scr.flags.has_default_color_changed = 1;
2234 }
2235 UpdateMenuColorset(cset);
2236 update_style_colorset(cset);
2237 update_decors_colorset(cset);
2238
2239 return;
2240 }
2241
2242 /* ---------------------------- builtin commands --------------------------- */
2243
CMD_Status(F_CMD_ARGS)2244 void CMD_Status(F_CMD_ARGS)
2245 {
2246 if ((strcasecmp(action, "on") == 0) && status_fp == NULL) {
2247 status_init_pipe();
2248 } else if (strcasecmp(action, "off") == 0) {
2249 if (status_fp != NULL) {
2250 fclose(status_fp);
2251 status_fp = NULL;
2252 fvwm_debug(__func__, "Closed pipe for status");
2253 }
2254 return;
2255 } else {
2256 fvwm_debug(__func__, "unknown option: %s", action);
2257 return;
2258 }
2259
2260 status_send();
2261 }
2262
CMD_Beep(F_CMD_ARGS)2263 void CMD_Beep(F_CMD_ARGS)
2264 {
2265 XBell(dpy, 0);
2266
2267 return;
2268 }
2269
CMD_Nop(F_CMD_ARGS)2270 void CMD_Nop(F_CMD_ARGS)
2271 {
2272 return;
2273 }
2274
CMD_EscapeFunc(F_CMD_ARGS)2275 void CMD_EscapeFunc(F_CMD_ARGS)
2276 {
2277 return;
2278 }
2279
CMD_CursorMove(F_CMD_ARGS)2280 void CMD_CursorMove(F_CMD_ARGS)
2281 {
2282 int x = 0, y = 0;
2283 int val1, val2, val1_unit, val2_unit;
2284 int x_unit, y_unit;
2285 int virtual_x, virtual_y;
2286 int x_pages, y_pages;
2287 struct monitor *m = NULL;
2288
2289 if (GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit) != 2)
2290 {
2291 fvwm_debug(__func__, "CursorMove needs 2 arguments");
2292 return;
2293 }
2294 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,
2295 &x, &y, &JunkX, &JunkY, &JunkMask) == False)
2296 {
2297 /* pointer is on a different screen */
2298 return;
2299 }
2300
2301 m = monitor_get_current();
2302
2303 x_unit = val1 * val1_unit / 100;
2304 y_unit = val2 * val2_unit / 100;
2305
2306 x += x_unit;
2307 y += y_unit;
2308
2309 virtual_x = m->virtual_scr.Vx;
2310 virtual_y = m->virtual_scr.Vy;
2311 if (x >= 0)
2312 {
2313 x_pages = x / monitor_get_all_widths();
2314 }
2315 else
2316 {
2317 x_pages = ((x + 1) / monitor_get_all_widths()) - 1;
2318 }
2319 virtual_x += x_pages * monitor_get_all_widths();
2320 x -= x_pages * monitor_get_all_widths();
2321 if (virtual_x < 0)
2322 {
2323 x += virtual_x;
2324 virtual_x = 0;
2325 }
2326 else if (virtual_x > m->virtual_scr.VxMax)
2327 {
2328 x += virtual_x - m->virtual_scr.VxMax;
2329 virtual_x = m->virtual_scr.VxMax;
2330 }
2331
2332 if (y >= 0)
2333 {
2334 y_pages = y / monitor_get_all_heights();
2335 }
2336 else
2337 {
2338 y_pages = ((y + 1) / monitor_get_all_heights()) - 1;
2339 }
2340 virtual_y += y_pages > monitor_get_all_heights();
2341 y -= y_pages * monitor_get_all_heights();
2342
2343 if (virtual_y < 0)
2344 {
2345 y += virtual_y;
2346 virtual_y = 0;
2347 }
2348 else if (virtual_y > m->virtual_scr.VyMax)
2349 {
2350 y += virtual_y - m->virtual_scr.VyMax;
2351 virtual_y = m->virtual_scr.VyMax;
2352 }
2353
2354 /* TA: (2010/12/19): Only move to the new page if scrolling is
2355 * enabled and the viewport is able to change based on where the
2356 * pointer is.
2357 */
2358 if ((virtual_x != m->virtual_scr.Vx && m->virtual_scr.EdgeScrollX != 0) ||
2359 (virtual_y != m->virtual_scr.Vy && m->virtual_scr.EdgeScrollY != 0))
2360 {
2361 MoveViewport(m, virtual_x, virtual_y, True);
2362 }
2363
2364 /* TA: (2010/12/19): If the cursor is about to enter a pan-window, or
2365 * is one, or the cursor's next step is to go beyond the page
2366 * boundary, stop the cursor from moving in that direction, *if* we've
2367 * disallowed edge scrolling.
2368 *
2369 * Whilst this stops the cursor short of the edge of the screen in a
2370 * given direction, this is the desired behaviour.
2371 */
2372 if (m->virtual_scr.EdgeScrollX == 0 && (x >= monitor_get_all_widths() ||
2373 x + x_unit >= monitor_get_all_widths()))
2374 return;
2375
2376 if (m->virtual_scr.EdgeScrollY == 0 && (y >= monitor_get_all_heights() ||
2377 y + y_unit >= monitor_get_all_heights()))
2378 return;
2379
2380 FWarpPointerUpdateEvpos(
2381 exc->x.elast, dpy, None, Scr.Root, 0, 0,
2382 monitor_get_all_widths(),
2383 monitor_get_all_heights(), x, y);
2384
2385 return;
2386 }
2387
CMD_Delete(F_CMD_ARGS)2388 void CMD_Delete(F_CMD_ARGS)
2389 {
2390 FvwmWindow * const fw = exc->w.fw;
2391
2392 if (!is_function_allowed(F_DELETE, NULL, fw, RQORIG_PROGRAM_US, True))
2393 {
2394 XBell(dpy, 0);
2395
2396 return;
2397 }
2398 if (IS_TEAR_OFF_MENU(fw))
2399 {
2400 /* 'soft' delete tear off menus. Note: we can't send the
2401 * message to the menu window directly because it was created
2402 * using a different display. The client message would never
2403 * be read from there. */
2404 send_clientmessage(
2405 dpy, FW_W_PARENT(fw), _XA_WM_DELETE_WINDOW,
2406 CurrentTime);
2407
2408 return;
2409 }
2410 if (WM_DELETES_WINDOW(fw))
2411 {
2412 send_clientmessage(
2413 dpy, FW_W(fw), _XA_WM_DELETE_WINDOW, CurrentTime);
2414
2415 return;
2416 }
2417 else
2418 {
2419 XBell(dpy, 0);
2420 }
2421 XFlush(dpy);
2422
2423 return;
2424 }
2425
CMD_Destroy(F_CMD_ARGS)2426 void CMD_Destroy(F_CMD_ARGS)
2427 {
2428 FvwmWindow * const fw = exc->w.fw;
2429
2430 if (IS_TEAR_OFF_MENU(fw))
2431 {
2432 CMD_Delete(F_PASS_ARGS);
2433 return;
2434 }
2435 if (!is_function_allowed(F_DESTROY, NULL, fw, True, True))
2436 {
2437 XBell(dpy, 0);
2438 return;
2439 }
2440 if (
2441 XGetGeometry(
2442 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
2443 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
2444 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth)
2445 != 0)
2446 {
2447 XKillClient(dpy, FW_W(fw));
2448 }
2449 destroy_window(fw);
2450 XFlush(dpy);
2451
2452 return;
2453 }
2454
CMD_Close(F_CMD_ARGS)2455 void CMD_Close(F_CMD_ARGS)
2456 {
2457 FvwmWindow * const fw = exc->w.fw;
2458
2459 if (IS_TEAR_OFF_MENU(fw))
2460 {
2461 CMD_Delete(F_PASS_ARGS);
2462 return;
2463 }
2464 if (!is_function_allowed(F_CLOSE, NULL, fw, True, True))
2465 {
2466 XBell(dpy, 0);
2467 return;
2468 }
2469 if (WM_DELETES_WINDOW(fw))
2470 {
2471 send_clientmessage(
2472 dpy, FW_W(fw), _XA_WM_DELETE_WINDOW, CurrentTime);
2473 return;
2474 }
2475 if (
2476 XGetGeometry(
2477 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
2478 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
2479 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth)
2480 != 0)
2481 {
2482 XKillClient(dpy, FW_W(fw));
2483 }
2484
2485 destroy_window(fw);
2486 XFlush(dpy);
2487
2488 return;
2489 }
2490
CMD_Restart(F_CMD_ARGS)2491 void CMD_Restart(F_CMD_ARGS)
2492 {
2493 Done(1, action);
2494
2495 return;
2496 }
2497
CMD_ExecUseShell(F_CMD_ARGS)2498 void CMD_ExecUseShell(F_CMD_ARGS)
2499 {
2500 char *arg=NULL;
2501 static char shell_set = 0;
2502
2503 if (shell_set)
2504 {
2505 free(exec_shell_name);
2506 }
2507 shell_set = 1;
2508 action = GetNextToken(action,&arg);
2509 if (arg) /* specific shell was specified */
2510 {
2511 exec_shell_name = arg;
2512 }
2513 else /* no arg, so use $SHELL -- not working??? */
2514 {
2515 if (getenv("SHELL"))
2516 {
2517 exec_shell_name = fxstrdup(getenv("SHELL"));
2518 }
2519 else
2520 {
2521 /* if $SHELL not set, use default */
2522 exec_shell_name = fxstrdup("/bin/sh");
2523 }
2524 }
2525 }
2526
CMD_Exec(F_CMD_ARGS)2527 void CMD_Exec(F_CMD_ARGS)
2528 {
2529 char *cmd=NULL;
2530
2531 /* if it doesn't already have an 'exec' as the first word, add that
2532 * to keep down number of procs started */
2533 /* need to parse string better to do this right though, so not doing
2534 * this for now... */
2535 #if 0
2536 if (strncasecmp(action,"exec",4)!=0)
2537 {
2538 cmd = fxmalloc(strlen(action)+6);
2539 strcpy(cmd,"exec ");
2540 strcat(cmd,action);
2541 }
2542 else
2543 #endif
2544 {
2545 cmd = fxstrdup(action);
2546 }
2547 if (!cmd)
2548 {
2549 return;
2550 }
2551 /* Use to grab the pointer here, but the fork guarantees that
2552 * we wont be held up waiting for the function to finish,
2553 * so the pointer-gram just caused needless delay and flashing
2554 * on the screen */
2555 /* Thought I'd try vfork and _exit() instead of regular fork().
2556 * The man page says that its better. */
2557 /* Not everyone has vfork! */
2558 /* According to the man page, vfork should never be used at all.
2559 */
2560
2561 if (!(fork())) /* child process */
2562 {
2563 /* This is for fixing a problem with rox filer */
2564 int fd;
2565
2566 fvmm_deinstall_signals();
2567 fd = open("/dev/null", O_RDONLY, 0);
2568 dup2(fd,STDIN_FILENO);
2569
2570 if (fd != STDIN_FILENO)
2571 close(fd);
2572
2573 if (fvwm_setpgrp() == -1)
2574 {
2575 fvwm_debug(__func__, "setpgrp failed (%s)",
2576 strerror(errno));
2577 exit(100);
2578 }
2579 if (execl(exec_shell_name, exec_shell_name, "-c", cmd, NULL) ==
2580 -1)
2581 {
2582 fvwm_debug(__func__, "execl failed (%s)",
2583 strerror(errno));
2584 exit(100);
2585 }
2586 }
2587 free(cmd);
2588
2589 return;
2590 }
2591
CMD_Refresh(F_CMD_ARGS)2592 void CMD_Refresh(F_CMD_ARGS)
2593 {
2594 refresh_window(Scr.Root, True);
2595
2596 return;
2597 }
2598
CMD_RefreshWindow(F_CMD_ARGS)2599 void CMD_RefreshWindow(F_CMD_ARGS)
2600 {
2601 FvwmWindow * const fw = exc->w.fw;
2602
2603 refresh_window(
2604 (exc->w.wcontext == C_ICON) ?
2605 FW_W_ICON_TITLE(fw) : FW_W_FRAME(fw), True);
2606
2607 return;
2608 }
2609
CMD_Wait(F_CMD_ARGS)2610 void CMD_Wait(F_CMD_ARGS)
2611 {
2612 Bool done = False;
2613 Bool redefine_cursor = False;
2614 Bool is_ungrabbed;
2615 char *escape;
2616 Window nonewin = None;
2617 char *wait_string, *rest;
2618 FvwmWindow *t;
2619
2620 /* try to get a single token */
2621 rest = GetNextToken(action, &wait_string);
2622 if (wait_string)
2623 {
2624 while (*rest && isspace((unsigned char)*rest))
2625 {
2626 rest++;
2627 }
2628 if (*rest)
2629 {
2630 int i;
2631 char *temp;
2632
2633 /* nope, multiple tokens - try old syntax */
2634
2635 /* strip leading and trailing whitespace */
2636 temp = action;
2637 while (*temp && isspace((unsigned char)*temp))
2638 {
2639 temp++;
2640 }
2641 free(wait_string);
2642 wait_string = fxstrdup(temp);
2643 for (i = strlen(wait_string) - 1; i >= 0 &&
2644 isspace(wait_string[i]); i--)
2645 {
2646 wait_string[i] = 0;
2647 }
2648 }
2649 }
2650 else
2651 {
2652 wait_string = fxstrdup("");
2653 }
2654
2655 is_ungrabbed = UngrabEm(GRAB_NORMAL);
2656 while (!done && !isTerminated)
2657 {
2658 XEvent e;
2659 if (BUSY_WAIT & Scr.BusyCursor)
2660 {
2661 XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_WAIT]);
2662 redefine_cursor = True;
2663 }
2664 if (My_XNextEvent(dpy, &e))
2665 {
2666 dispatch_event(&e);
2667 if (XFindContext(
2668 dpy, e.xmap.window, FvwmContext,
2669 (caddr_t *)&t) == XCNOENT)
2670 {
2671 t = NULL;
2672 }
2673
2674 if (e.type == MapNotify && e.xmap.event == Scr.Root)
2675 {
2676 if (!*wait_string)
2677 {
2678 done = True;
2679 }
2680 if (t && matchWildcards(
2681 wait_string, t->name.name) == True)
2682 {
2683 done = True;
2684 }
2685 else if (t && t->class.res_class &&
2686 matchWildcards(
2687 wait_string,
2688 t->class.res_class) == True)
2689 {
2690 done = True;
2691 }
2692 else if (t && t->class.res_name &&
2693 matchWildcards(
2694 wait_string,
2695 t->class.res_name) == True)
2696 {
2697 done = True;
2698 }
2699 }
2700 else if (e.type == KeyPress)
2701 {
2702 /* should I be using <t> or <exc->w.fw>?
2703 * DV: t
2704 */
2705 int context;
2706 XClassHint *class;
2707 char *name;
2708
2709 context = GetContext(&t, t, &e, &nonewin);
2710 if (t != NULL)
2711 {
2712 class = &(t->class);
2713 name = t->name.name;
2714 }
2715 else
2716 {
2717 class = NULL;
2718 name = NULL;
2719 }
2720 escape = CheckBinding(
2721 Scr.AllBindings,
2722 e.xkey.keycode, e.xkey.state,
2723 GetUnusedModifiers(), context,
2724 BIND_KEYPRESS, class, name);
2725 if (escape != NULL)
2726 {
2727 if (!strcasecmp(escape,"escapefunc"))
2728 {
2729 done = True;
2730 }
2731 }
2732 }
2733 }
2734 }
2735 if (redefine_cursor)
2736 {
2737 XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_ROOT]);
2738 }
2739 if (is_ungrabbed)
2740 {
2741 GrabEm(CRS_NONE, GRAB_NORMAL);
2742 }
2743 free(wait_string);
2744
2745 return;
2746 }
2747
CMD_Quit(F_CMD_ARGS)2748 void CMD_Quit(F_CMD_ARGS)
2749 {
2750 if (master_pid != getpid())
2751 {
2752 kill(master_pid, SIGTERM);
2753 }
2754 Done(0,NULL);
2755
2756 return;
2757 }
2758
CMD_QuitScreen(F_CMD_ARGS)2759 void CMD_QuitScreen(F_CMD_ARGS)
2760 {
2761 Done(0,NULL);
2762
2763 return;
2764 }
2765
CMD_Echo(F_CMD_ARGS)2766 void CMD_Echo(F_CMD_ARGS)
2767 {
2768 int len;
2769
2770 if (!action)
2771 {
2772 action = "";
2773 }
2774 len = strlen(action);
2775 if (len != 0)
2776 {
2777 if (action[len-1]=='\n')
2778 {
2779 action[len-1]='\0';
2780 }
2781 }
2782 BroadcastName(MX_ECHO, -1, -1, -1, action);
2783 fvwm_debug(__func__, "%s\n", action);
2784
2785 return;
2786 }
2787
CMD_PrintInfo(F_CMD_ARGS)2788 void CMD_PrintInfo(F_CMD_ARGS)
2789 {
2790 int verbose;
2791 char *rest, *subject = NULL;
2792
2793 rest = GetNextToken(action, &subject);
2794 if (!rest || GetIntegerArguments(rest, NULL, &verbose, 1) != 1)
2795 {
2796 verbose = 0;
2797 }
2798 if (StrEquals(subject, "Colors"))
2799 {
2800 PicturePrintColorInfo(verbose);
2801 }
2802 else if (StrEquals(subject, "Locale"))
2803 {
2804 FlocalePrintLocaleInfo(dpy, verbose);
2805 }
2806 else if (StrEquals(subject, "NLS"))
2807 {
2808 FGettextPrintLocalePath(verbose);
2809 }
2810 else if (StrEquals(subject, "style"))
2811 {
2812 print_styles(verbose);
2813 }
2814 else if (StrEquals(subject, "ImageCache"))
2815 {
2816 PicturePrintImageCache(verbose);
2817 }
2818 else if (StrEquals(subject, "Bindings"))
2819 {
2820 print_bindings();
2821 }
2822 else if (StrEquals(subject, "InfoStore"))
2823 {
2824 print_infostore();
2825 }
2826 else
2827 {
2828 fvwm_debug(__func__,
2829 "Unknown subject '%s'", action);
2830 }
2831 if (subject)
2832 {
2833 free(subject);
2834 }
2835 return;
2836 }
2837
CMD_ColormapFocus(F_CMD_ARGS)2838 void CMD_ColormapFocus(F_CMD_ARGS)
2839 {
2840 if (MatchToken(action,"FollowsFocus"))
2841 {
2842 Scr.ColormapFocus = COLORMAP_FOLLOWS_FOCUS;
2843 }
2844 else if (MatchToken(action,"FollowsMouse"))
2845 {
2846 Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;
2847 }
2848 else
2849 {
2850 fvwm_debug(__func__,
2851 "ColormapFocus requires 1 arg: FollowsFocus or"
2852 " FollowsMouse");
2853 return;
2854 }
2855
2856 return;
2857 }
2858
CMD_ClickTime(F_CMD_ARGS)2859 void CMD_ClickTime(F_CMD_ARGS)
2860 {
2861 int val;
2862
2863 if (GetIntegerArguments(action, NULL, &val, 1) != 1)
2864 {
2865 Scr.ClickTime = DEFAULT_CLICKTIME;
2866 }
2867 else
2868 {
2869 Scr.ClickTime = (val < 0)? 0 : val;
2870 }
2871
2872 /* Use a negative value during startup and change sign afterwards. This
2873 * speeds things up quite a bit. */
2874 if (fFvwmInStartup)
2875 {
2876 Scr.ClickTime = -Scr.ClickTime;
2877 }
2878
2879 return;
2880 }
2881
2882
CMD_ImagePath(F_CMD_ARGS)2883 void CMD_ImagePath(F_CMD_ARGS)
2884 {
2885 PictureSetImagePath( action );
2886
2887 return;
2888 }
2889
CMD_IconPath(F_CMD_ARGS)2890 void CMD_IconPath(F_CMD_ARGS)
2891 {
2892 fvwm_debug(__func__,
2893 "IconPath is deprecated since 2.3.0; use ImagePath instead.");
2894 obsolete_imagepaths( action );
2895
2896 return;
2897 }
2898
CMD_PixmapPath(F_CMD_ARGS)2899 void CMD_PixmapPath(F_CMD_ARGS)
2900 {
2901 fvwm_debug(__func__,
2902 "PixmapPath is deprecated since 2.3.0; use ImagePath"
2903 " instead." );
2904 obsolete_imagepaths( action );
2905
2906 return;
2907 }
2908
CMD_LocalePath(F_CMD_ARGS)2909 void CMD_LocalePath(F_CMD_ARGS)
2910 {
2911 FGettextSetLocalePath( action );
2912
2913 return;
2914 }
2915
CMD_ModulePath(F_CMD_ARGS)2916 void CMD_ModulePath(F_CMD_ARGS)
2917 {
2918 static int need_to_free = 0;
2919
2920 setPath( &ModulePath, action, need_to_free );
2921 need_to_free = 1;
2922
2923 return;
2924 }
2925
CMD_ModuleTimeout(F_CMD_ARGS)2926 void CMD_ModuleTimeout(F_CMD_ARGS)
2927 {
2928 int timeout;
2929
2930 moduleTimeout = DEFAULT_MODULE_TIMEOUT;
2931 if (GetIntegerArguments(action, NULL, &timeout, 1) == 1 && timeout > 0)
2932 {
2933 moduleTimeout = timeout;
2934 }
2935
2936 return;
2937 }
2938
CMD_HilightColor(F_CMD_ARGS)2939 void CMD_HilightColor(F_CMD_ARGS)
2940 {
2941 char *fore;
2942 char *back;
2943
2944 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
2945 {
2946 fvwm_debug(__func__,
2947 "Decors do not support the HilightColor command"
2948 " anymore. Please use"
2949 " 'Style <stylename> HilightFore <forecolor>' and"
2950 " 'Style <stylename> HilightBack <backcolor>' instead."
2951 " Sorry for the inconvenience.");
2952 return;
2953 }
2954 action = GetNextToken(action, &fore);
2955 GetNextToken(action, &back);
2956 if (fore && back)
2957 {
2958 /* TA: FIXME: xasprintf() */
2959 action = fxmalloc(strlen(fore) + strlen(back) + 29);
2960 sprintf(action, "* HilightFore %s, HilightBack %s", fore, back);
2961 CMD_Style(F_PASS_ARGS);
2962 }
2963 if (fore)
2964 {
2965 free(fore);
2966 }
2967 if (back)
2968 {
2969 free(back);
2970 }
2971
2972 return;
2973 }
2974
CMD_HilightColorset(F_CMD_ARGS)2975 void CMD_HilightColorset(F_CMD_ARGS)
2976 {
2977 char *newaction;
2978
2979 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
2980 {
2981 fvwm_debug(__func__,
2982 "Decors do not support the HilightColorset command "
2983 "anymore. Please use "
2984 "'Style <stylename> HilightColorset <colorset>'"
2985 " instead. Sorry for the inconvenience.");
2986 return;
2987 }
2988
2989 if (action)
2990 {
2991 /* TA: FIXME! xasprintf() */
2992 newaction = fxmalloc(strlen(action) + 32);
2993 sprintf(newaction, "* HilightColorset %s", action);
2994 action = newaction;
2995 CMD_Style(F_PASS_ARGS);
2996 free(newaction);
2997 }
2998
2999 return;
3000 }
3001
CMD_TitleStyle(F_CMD_ARGS)3002 void CMD_TitleStyle(F_CMD_ARGS)
3003 {
3004 do_title_style(F_PASS_ARGS, False);
3005
3006 return;
3007 } /* SetTitleStyle */
3008
3009 /*
3010 *
3011 * Appends a titlestyle (veliaa@rpi.edu)
3012 *
3013 */
CMD_AddTitleStyle(F_CMD_ARGS)3014 void CMD_AddTitleStyle(F_CMD_ARGS)
3015 {
3016 do_title_style(F_PASS_ARGS, True);
3017
3018 return;
3019 }
3020
CMD_PropertyChange(F_CMD_ARGS)3021 void CMD_PropertyChange(F_CMD_ARGS)
3022 {
3023 char string[256];
3024 char *token;
3025 char *rest;
3026 int ret;
3027 unsigned long argument;
3028 unsigned long data1;
3029 unsigned long data2;
3030
3031 /* argument */
3032 token = PeekToken(action, &rest);
3033 if (token == NULL)
3034 {
3035 return;
3036 }
3037 ret = sscanf(token, "%lu", &argument);
3038 if (ret < 1)
3039 {
3040 return;
3041 }
3042 /* data1 */
3043 data1 = 0;
3044 token = PeekToken(rest, &rest);
3045 if (token != NULL)
3046 {
3047 ret = sscanf(token, "%lu", &data1);
3048 if (ret < 1)
3049 {
3050 rest = NULL;
3051 }
3052 }
3053 /* data2 */
3054 data2 = 0;
3055 token = PeekToken(rest, &rest);
3056 if (token != NULL)
3057 {
3058 ret = sscanf(token, "%lu", &data2);
3059 if (ret < 1)
3060 {
3061 rest = NULL;
3062 }
3063 }
3064 /* string */
3065 memset(string, 0, 256);
3066 if (rest != NULL)
3067 {
3068 ret = sscanf(rest, "%255c", &(string[0]));
3069 }
3070 BroadcastPropertyChange(argument, data1, data2, string);
3071
3072 return;
3073 }
3074
CMD_DefaultIcon(F_CMD_ARGS)3075 void CMD_DefaultIcon(F_CMD_ARGS)
3076 {
3077 if (Scr.DefaultIcon)
3078 {
3079 free(Scr.DefaultIcon);
3080 }
3081 GetNextToken(action, &Scr.DefaultIcon);
3082
3083 return;
3084 }
3085
CMD_DefaultColorset(F_CMD_ARGS)3086 void CMD_DefaultColorset(F_CMD_ARGS)
3087 {
3088 int cset;
3089
3090 if (GetIntegerArguments(action, NULL, &cset, 1) != 1)
3091 {
3092 return;
3093 }
3094 Scr.DefaultColorset = cset;
3095 if (Scr.DefaultColorset < 0)
3096 {
3097 Scr.DefaultColorset = -1;
3098 }
3099 alloc_colorset(Scr.DefaultColorset);
3100 Scr.flags.do_need_window_update = 1;
3101 Scr.flags.has_default_color_changed = 1;
3102
3103 return;
3104 }
3105
CMD_DefaultColors(F_CMD_ARGS)3106 void CMD_DefaultColors(F_CMD_ARGS)
3107 {
3108 char *fore = NULL;
3109 char *back = NULL;
3110
3111 action = GetNextToken(action, &fore);
3112 if (action)
3113 {
3114 action = GetNextToken(action, &back);
3115 }
3116 if (!back)
3117 {
3118 back = fxstrdup(DEFAULT_BACK_COLOR);
3119 }
3120 if (!fore)
3121 {
3122 fore = fxstrdup(DEFAULT_FORE_COLOR);
3123 }
3124 if (!StrEquals(fore, "-"))
3125 {
3126 PictureFreeColors(dpy, Pcmap, &Scr.StdFore, 1, 0, True);
3127 Scr.StdFore = GetColor(fore);
3128 }
3129 if (!StrEquals(back, "-"))
3130 {
3131 PictureFreeColors(dpy, Pcmap, &Scr.StdBack, 3, 0, True);
3132 Scr.StdBack = GetColor(back);
3133 Scr.StdHilite = GetHilite(Scr.StdBack);
3134 Scr.StdShadow = GetShadow(Scr.StdBack);
3135 }
3136 free(fore);
3137 free(back);
3138
3139 Scr.DefaultColorset = -1;
3140 Scr.flags.do_need_window_update = 1;
3141 Scr.flags.has_default_color_changed = 1;
3142
3143 return;
3144 }
3145
CMD_DefaultFont(F_CMD_ARGS)3146 void CMD_DefaultFont(F_CMD_ARGS)
3147 {
3148 char *font;
3149 FlocaleFont *new_font;
3150 FvwmWindow *t;
3151
3152 font = PeekToken(action, &action);
3153 if (!font)
3154 {
3155 /* Try 'fixed', pass NULL font name */
3156 }
3157 if (!(new_font = FlocaleLoadFont(dpy, font, "fvwm")))
3158 {
3159 if (Scr.DefaultFont == NULL)
3160 {
3161 exit(1);
3162 }
3163 else
3164 {
3165 return;
3166 }
3167 }
3168 FlocaleUnloadFont(dpy, Scr.DefaultFont);
3169 Scr.DefaultFont = new_font;
3170 /* we should do that here because a redraw can happen before
3171 flush_window_updates is called ... */
3172 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3173 {
3174 if (USING_DEFAULT_ICON_FONT(t))
3175 {
3176 t->icon_font = Scr.DefaultFont;
3177 }
3178 if (USING_DEFAULT_WINDOW_FONT(t))
3179 {
3180 t->title_font = Scr.DefaultFont;
3181 }
3182 }
3183 /* set flags to indicate that the font has changed */
3184 Scr.flags.do_need_window_update = 1;
3185 Scr.flags.has_default_font_changed = 1;
3186
3187 return;
3188 }
3189
CMD_IconFont(F_CMD_ARGS)3190 void CMD_IconFont(F_CMD_ARGS)
3191 {
3192 char *newaction;
3193
3194 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
3195 {
3196 fvwm_debug(__func__,
3197 "Decors do not support the IconFont command anymore."
3198 " Please use 'Style <stylename> IconFont <fontname>'"
3199 " instead. Sorry for the inconvenience.");
3200 return;
3201 }
3202
3203 if (action)
3204 {
3205 /* TA: FIXME! xasprintf() */
3206 newaction = fxmalloc(strlen(action) + 16);
3207 sprintf(newaction, "* IconFont %s", action);
3208 action = newaction;
3209 CMD_Style(F_PASS_ARGS);
3210 free(newaction);
3211 }
3212
3213 return;
3214 }
3215
CMD_WindowFont(F_CMD_ARGS)3216 void CMD_WindowFont(F_CMD_ARGS)
3217 {
3218 char *newaction;
3219
3220 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
3221 {
3222 fvwm_debug(__func__,
3223 "Decors do not support the WindowFont command anymore."
3224 " Please use 'Style <stylename> Font <fontname>'"
3225 " instead. Sorry for the inconvenience.");
3226 return;
3227 }
3228
3229 if (action)
3230 {
3231 /* TA; FIXME! xasprintf() */
3232 newaction = fxmalloc(strlen(action) + 16);
3233 sprintf(newaction, "* Font %s", action);
3234 action = newaction;
3235 CMD_Style(F_PASS_ARGS);
3236 free(newaction);
3237 }
3238
3239 return;
3240 }
3241
3242 /*
3243 *
3244 * Changes the window's FvwmDecor pointer (veliaa@rpi.edu)
3245 *
3246 */
CMD_ChangeDecor(F_CMD_ARGS)3247 void CMD_ChangeDecor(F_CMD_ARGS)
3248 {
3249 char *item;
3250 FvwmDecor *decor = &Scr.DefaultDecor;
3251 FvwmDecor *found = NULL;
3252 FvwmWindow * const fw = exc->w.fw;
3253
3254 item = PeekToken(action, &action);
3255 if (!action || !item)
3256 {
3257 return;
3258 }
3259 /* search for tag */
3260 for (; decor; decor = decor->next)
3261 {
3262 if (decor->tag && StrEquals(item, decor->tag))
3263 {
3264 found = decor;
3265 break;
3266 }
3267 }
3268 if (!found)
3269 {
3270 XBell(dpy, 0);
3271 return;
3272 }
3273 SET_DECOR_CHANGED(fw, 1);
3274 fw->decor = found;
3275 apply_decor_change(fw);
3276
3277 return;
3278 }
3279
3280 /*
3281 *
3282 * Destroys an FvwmDecor (veliaa@rpi.edu)
3283 *
3284 */
CMD_DestroyDecor(F_CMD_ARGS)3285 void CMD_DestroyDecor(F_CMD_ARGS)
3286 {
3287 char *item;
3288 FvwmDecor *decor = Scr.DefaultDecor.next;
3289 FvwmDecor *prev = &Scr.DefaultDecor, *found = NULL;
3290 Bool do_recreate = False;
3291
3292 item = PeekToken(action, &action);
3293 if (!item)
3294 {
3295 return;
3296 }
3297 if (StrEquals(item, "recreate"))
3298 {
3299 do_recreate = True;
3300 item = PeekToken(action, NULL);
3301 }
3302 if (!item)
3303 {
3304 return;
3305 }
3306
3307 /* search for tag */
3308 for (; decor; decor = decor->next)
3309 {
3310 if (decor->tag && StrEquals(item, decor->tag))
3311 {
3312 found = decor;
3313 break;
3314 }
3315 prev = decor;
3316 }
3317
3318 if (found && (found != &Scr.DefaultDecor || do_recreate))
3319 {
3320 if (!do_recreate)
3321 {
3322 __remove_window_decors(F_PASS_ARGS, found);
3323 }
3324 DestroyFvwmDecor(found);
3325 if (do_recreate)
3326 {
3327 int i;
3328
3329 InitFvwmDecor(found);
3330 found->tag = fxstrdup(item);
3331 Scr.flags.do_need_window_update = 1;
3332 found->flags.has_changed = 1;
3333 found->flags.has_title_height_changed = 0;
3334 found->titlebar.flags.has_changed = 1;
3335 for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
3336 {
3337 TB_FLAGS(found->buttons[i]).has_changed = 1;
3338 }
3339 }
3340 else
3341 {
3342 prev->next = found->next;
3343 free(found);
3344 }
3345 }
3346
3347 return;
3348 }
3349
3350 /*
3351 *
3352 * Initiates an AddToDecor (veliaa@rpi.edu)
3353 *
3354 */
CMD_AddToDecor(F_CMD_ARGS)3355 void CMD_AddToDecor(F_CMD_ARGS)
3356 {
3357 FvwmDecor *decor;
3358 FvwmDecor *found = NULL;
3359 char *item = NULL;
3360
3361 action = GetNextToken(action, &item);
3362 if (!item)
3363 {
3364 return;
3365 }
3366 if (!action)
3367 {
3368 free(item);
3369 return;
3370 }
3371 /* search for tag */
3372 for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
3373 {
3374 if (decor->tag && StrEquals(item, decor->tag))
3375 {
3376 found = decor;
3377 break;
3378 }
3379 }
3380 if (!found)
3381 {
3382 /* then make a new one */
3383 found = fxmalloc(sizeof *found);
3384 InitFvwmDecor(found);
3385 found->tag = item; /* tag it */
3386 /* add it to list */
3387 for (decor = &Scr.DefaultDecor; decor->next;
3388 decor = decor->next)
3389 {
3390 /* nop */
3391 }
3392 decor->next = found;
3393 }
3394 else
3395 {
3396 free(item);
3397 }
3398
3399 if (found)
3400 {
3401 AddToDecor(F_PASS_ARGS, found);
3402 /* Set + state to last decor */
3403 set_last_added_item(ADDED_DECOR, found);
3404 }
3405
3406 return;
3407 }
3408
3409
3410 /*
3411 *
3412 * Updates window decoration styles (veliaa@rpi.edu)
3413 *
3414 */
CMD_UpdateDecor(F_CMD_ARGS)3415 void CMD_UpdateDecor(F_CMD_ARGS)
3416 {
3417 FvwmWindow *fw2;
3418
3419 FvwmDecor *decor, *found = NULL;
3420 FvwmWindow *hilight = Scr.Hilite;
3421 char *item = NULL;
3422
3423 action = GetNextToken(action, &item);
3424 if (item)
3425 {
3426 /* search for tag */
3427 for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
3428 {
3429 if (decor->tag && StrEquals(item, decor->tag))
3430 {
3431 found = decor;
3432 break;
3433 }
3434 }
3435 free(item);
3436 }
3437
3438 for (fw2 = Scr.FvwmRoot.next; fw2; fw2 = fw2->next)
3439 {
3440 /* update specific decor, or all */
3441 if (found)
3442 {
3443 if (fw2->decor == found)
3444 {
3445 border_draw_decorations(
3446 fw2, PART_ALL, True, True, CLEAR_ALL,
3447 NULL, NULL);
3448 border_draw_decorations(
3449 fw2, PART_ALL, False, True, CLEAR_ALL,
3450 NULL, NULL);
3451 }
3452 }
3453 else
3454 {
3455 border_draw_decorations(
3456 fw2, PART_ALL, True, True, CLEAR_ALL, NULL,
3457 NULL);
3458 border_draw_decorations(
3459 fw2, PART_ALL, False, True, CLEAR_ALL, NULL,
3460 NULL);
3461 }
3462 }
3463 border_draw_decorations(
3464 hilight, PART_ALL, True, True, CLEAR_ALL, NULL, NULL);
3465 }
3466
CMD_ButtonStyle(F_CMD_ARGS)3467 void CMD_ButtonStyle(F_CMD_ARGS)
3468 {
3469 do_button_style(F_PASS_ARGS, False);
3470
3471 return;
3472 }
3473
3474 /*
3475 *
3476 * Appends a button decoration style (veliaa@rpi.edu)
3477 *
3478 */
CMD_AddButtonStyle(F_CMD_ARGS)3479 void CMD_AddButtonStyle(F_CMD_ARGS)
3480 {
3481 do_button_style(F_PASS_ARGS, True);
3482
3483 return;
3484 }
3485
CMD_SetEnv(F_CMD_ARGS)3486 void CMD_SetEnv(F_CMD_ARGS)
3487 {
3488 char *szVar = NULL;
3489 char *szValue = NULL;
3490 char *szPutenv = NULL;
3491
3492 action = GetNextToken(action, &szVar);
3493 if (!szVar)
3494 {
3495 return;
3496 }
3497 action = GetNextToken(action, &szValue);
3498 if (!szValue)
3499 {
3500 szValue = fxstrdup("");
3501 }
3502 szPutenv = fxmalloc(strlen(szVar) + strlen(szValue) + 2);
3503 sprintf(szPutenv,"%s=%s", szVar, szValue);
3504 flib_putenv(szVar, szPutenv);
3505 free(szVar);
3506 free(szPutenv);
3507 free(szValue);
3508
3509 return;
3510 }
3511
CMD_UnsetEnv(F_CMD_ARGS)3512 void CMD_UnsetEnv(F_CMD_ARGS)
3513 {
3514 char *szVar = NULL;
3515
3516 szVar = PeekToken(action, &action);
3517 if (!szVar)
3518 {
3519 return;
3520 }
3521 flib_unsetenv(szVar);
3522
3523 return;
3524 }
3525
CMD_GlobalOpts(F_CMD_ARGS)3526 void CMD_GlobalOpts(F_CMD_ARGS)
3527 {
3528 char *opt;
3529 char *replace;
3530 char buf[64];
3531 int i;
3532 Bool is_bugopt;
3533 char *optlist[] = {
3534 "WindowShadeShrinks",
3535 "WindowShadeScrolls",
3536 "SmartPlacementIsReallySmart",
3537 "SmartPlacementIsNormal",
3538 "ClickToFocusDoesntPassClick",
3539 "ClickToFocusPassesClick",
3540 "ClickToFocusDoesntRaise",
3541 "ClickToFocusRaises",
3542 "MouseFocusClickDoesntRaise",
3543 "MouseFocusClickRaises",
3544 "NoStipledTitles",
3545 "StipledTitles",
3546 "CaptureHonorsStartsOnPage",
3547 "CaptureIgnoresStartsOnPage",
3548 "RecaptureHonorsStartsOnPage",
3549 "RecaptureIgnoresStartsOnPage",
3550 "ActivePlacementHonorsStartsOnPage",
3551 "ActivePlacementIgnoresStartsOnPage",
3552 "RaiseOverNativeWindows",
3553 "IgnoreNativeWindows",
3554 NULL
3555 };
3556 char *replacelist[] = {
3557 /* These options are mapped to the Style * command */
3558 NULL, /* NULL means to use "Style * <optionname>" */
3559 NULL,
3560 "* MinOverlapPlacement",
3561 "* TileCascadePlacement",
3562 "* ClickToFocusPassesClickOff",
3563 "* ClickToFocusPassesClick",
3564 "* ClickToFocusRaisesOff",
3565 "* ClickToFocusRaises",
3566 "* MouseFocusClickRaisesOff",
3567 "* MouseFocusClickRaises",
3568 "* StippledTitleOff",
3569 "* StippledTitle",
3570 NULL,
3571 NULL,
3572 NULL,
3573 NULL,
3574 "* ManualPlacementHonorsStartsOnPage",
3575 "* ManualPlacementIgnoresStartsOnPage",
3576 /* These options are mapped to the BugOpts command */
3577 "RaiseOverNativeWindows on",
3578 "RaiseOverNativeWindows off"
3579 };
3580
3581 fvwm_debug(__func__,
3582 "The GlobalOpts command is obsolete.");
3583 for (action = GetNextSimpleOption(action, &opt); opt;
3584 action = GetNextSimpleOption(action, &opt))
3585 {
3586 replace = NULL;
3587 is_bugopt = False;
3588
3589 i = GetTokenIndex(opt, optlist, 0, NULL);
3590 if (i > -1)
3591 {
3592 char *cmd;
3593 char *tmp;
3594
3595 replace = replacelist[i];
3596 if (replace == NULL)
3597 {
3598 replace = &(buf[0]);
3599 sprintf(buf, "* %s", opt);
3600 }
3601 else if (*replace != '*')
3602 {
3603 is_bugopt = True;
3604 }
3605 tmp = action;
3606 action = replace;
3607 if (!is_bugopt)
3608 {
3609 CMD_Style(F_PASS_ARGS);
3610 cmd = "Style";
3611 }
3612 else
3613 {
3614 CMD_BugOpts(F_PASS_ARGS);
3615 cmd = "BugOpts";
3616 }
3617 action = tmp;
3618 fvwm_debug(__func__,
3619 "Please replace 'GlobalOpts %s' with '%s %s'.",
3620 opt, cmd, replace);
3621 }
3622 else
3623 {
3624 fvwm_debug(__func__,
3625 "Unknown Global Option '%s'", opt);
3626 }
3627 /* should never be null, but checking anyways... */
3628 if (opt)
3629 {
3630 free(opt);
3631 }
3632 }
3633 if (opt)
3634 {
3635 free(opt);
3636 }
3637
3638 return;
3639 }
3640
CMD_BugOpts(F_CMD_ARGS)3641 void CMD_BugOpts(F_CMD_ARGS)
3642 {
3643 char *opt;
3644 int toggle;
3645 char *optstring;
3646
3647 /* fvwm_msg(DBG,"SetGlobalOptions","init action == '%s'\n",action); */
3648 while (action && *action && *action != '\n')
3649 {
3650 action = GetNextFullOption(action, &optstring);
3651 if (!optstring)
3652 {
3653 /* no more options */
3654 return;
3655 }
3656 toggle = ParseToggleArgument(
3657 SkipNTokens(optstring,1), NULL, 2, False);
3658 opt = PeekToken(optstring, NULL);
3659 free(optstring);
3660
3661 if (!opt)
3662 {
3663 return;
3664 }
3665 /* toggle = ParseToggleArgument(rest, &rest, 2, False);*/
3666
3667 if (StrEquals(opt, "FlickeringMoveWorkaround"))
3668 {
3669 switch (toggle)
3670 {
3671 case -1:
3672 Scr.bo.do_disable_configure_notify ^= 1;
3673 break;
3674 case 0:
3675 case 1:
3676 Scr.bo.do_disable_configure_notify = toggle;
3677 break;
3678 default:
3679 Scr.bo.do_disable_configure_notify = 0;
3680 break;
3681 }
3682 }
3683 else if (StrEquals(opt, "MixedVisualWorkaround"))
3684 {
3685 switch (toggle)
3686 {
3687 case -1:
3688 Scr.bo.do_install_root_cmap ^= 1;
3689 break;
3690 case 0:
3691 case 1:
3692 Scr.bo.do_install_root_cmap = toggle;
3693 break;
3694 default:
3695 Scr.bo.do_install_root_cmap = 0;
3696 break;
3697 }
3698 }
3699 else if (StrEquals(opt, "ModalityIsEvil"))
3700 {
3701 switch (toggle)
3702 {
3703 case -1:
3704 Scr.bo.is_modality_evil ^= 1;
3705 break;
3706 case 0:
3707 case 1:
3708 Scr.bo.is_modality_evil = toggle;
3709 break;
3710 default:
3711 Scr.bo.is_modality_evil = 0;
3712 break;
3713 }
3714 if (Scr.bo.is_modality_evil)
3715 {
3716 SetMWM_INFO(Scr.NoFocusWin);
3717 }
3718 }
3719 else if (StrEquals(opt, "RaiseOverNativeWindows"))
3720 {
3721 switch (toggle)
3722 {
3723 case -1:
3724 Scr.bo.is_raise_hack_needed ^= 1;
3725 break;
3726 case 0:
3727 case 1:
3728 Scr.bo.is_raise_hack_needed = toggle;
3729 break;
3730 default:
3731 Scr.bo.is_raise_hack_needed = 0;
3732 break;
3733 }
3734 }
3735 else if (StrEquals(opt, "RaiseOverUnmanaged"))
3736 {
3737 switch (toggle)
3738 {
3739 case -1:
3740 Scr.bo.do_raise_over_unmanaged ^= 1;
3741 break;
3742 case 0:
3743 case 1:
3744 Scr.bo.do_raise_over_unmanaged = toggle;
3745 break;
3746 default:
3747 Scr.bo.do_raise_over_unmanaged = 0;
3748 break;
3749 }
3750 }
3751 else if (StrEquals(opt, "FlickeringQtDialogsWorkaround"))
3752 {
3753 switch (toggle)
3754 {
3755 case -1:
3756 Scr.bo.do_enable_flickering_qt_dialogs_workaround ^= 1;
3757 break;
3758 case 0:
3759 case 1:
3760 Scr.bo.do_enable_flickering_qt_dialogs_workaround = toggle;
3761 break;
3762 default:
3763 Scr.bo.do_enable_flickering_qt_dialogs_workaround = 0;
3764 break;
3765 }
3766 }
3767 else if (StrEquals(opt, "QtDragnDropWorkaround") )
3768 {
3769 switch (toggle)
3770 {
3771 case -1:
3772 Scr.bo.do_enable_qt_drag_n_drop_workaround ^= 1;
3773 break;
3774 case 0:
3775 case 1:
3776 Scr.bo.do_enable_qt_drag_n_drop_workaround = toggle;
3777 break;
3778 default:
3779 Scr.bo.do_enable_qt_drag_n_drop_workaround = 0;
3780 break;
3781 }
3782 }
3783 else if (EWMH_BugOpts(opt, toggle))
3784 {
3785 /* work is done in EWMH_BugOpts */
3786 }
3787 else if (StrEquals(opt, "DisplayNewWindowNames"))
3788 {
3789 switch (toggle)
3790 {
3791 case -1:
3792 Scr.bo.do_display_new_window_names ^= 1;
3793 break;
3794 case 0:
3795 case 1:
3796 Scr.bo.do_display_new_window_names = toggle;
3797 break;
3798 default:
3799 Scr.bo.do_display_new_window_names = 0;
3800 break;
3801 }
3802 }
3803 else if (StrEquals(opt, "ExplainWindowPlacement"))
3804 {
3805 switch (toggle)
3806 {
3807 case -1:
3808 Scr.bo.do_explain_window_placement ^= 1;
3809 break;
3810 case 0:
3811 case 1:
3812 Scr.bo.do_explain_window_placement = toggle;
3813 break;
3814 default:
3815 Scr.bo.do_explain_window_placement = 0;
3816 break;
3817 }
3818 }
3819 else if (StrEquals(opt, "DebugCRMotionMethod"))
3820 {
3821 switch (toggle)
3822 {
3823 case -1:
3824 Scr.bo.do_debug_cr_motion_method ^= 1;
3825 break;
3826 case 0:
3827 case 1:
3828 Scr.bo.do_debug_cr_motion_method = toggle;
3829 break;
3830 default:
3831 Scr.bo.do_debug_cr_motion_method = 0;
3832 break;
3833 }
3834 }
3835 else if (StrEquals(opt, "DebugRandR"))
3836 {
3837 switch (toggle)
3838 {
3839 case -1:
3840 Scr.bo.do_debug_randr ^= 1;
3841 break;
3842 case 0:
3843 case 1:
3844 Scr.bo.do_debug_randr = toggle;
3845 break;
3846 default:
3847 Scr.bo.do_debug_randr = 0;
3848 break;
3849 }
3850 monitor_dump_state(NULL);
3851 }
3852 else if (StrEquals(opt, "TransliterateUtf8"))
3853 {
3854 FiconvSetTransliterateUtf8(toggle);
3855 }
3856 else
3857 {
3858 fvwm_debug(__func__,
3859 "Unknown Bug Option '%s'", opt);
3860 }
3861 }
3862
3863 return;
3864 }
3865
CMD_Emulate(F_CMD_ARGS)3866 void CMD_Emulate(F_CMD_ARGS)
3867 {
3868 char *style;
3869
3870 style = PeekToken(action, NULL);
3871 if (!style || StrEquals(style, "fvwm"))
3872 {
3873 Scr.gs.do_emulate_mwm = False;
3874 Scr.gs.do_emulate_win = False;
3875 }
3876 else if (StrEquals(style, "mwm"))
3877 {
3878 Scr.gs.do_emulate_mwm = True;
3879 Scr.gs.do_emulate_win = False;
3880 }
3881 else if (StrEquals(style, "win"))
3882 {
3883 Scr.gs.do_emulate_mwm = False;
3884 Scr.gs.do_emulate_win = True;
3885 }
3886 else
3887 {
3888 fvwm_debug(__func__, "Unknown style '%s'", style);
3889 return;
3890 }
3891 /* This command might have been issued after any GeometryWindow
3892 * commands, in which case set those values to their defaults.
3893 */
3894 Scr.SizeWindow.is_configured = false;
3895 Scr.SizeWindow.m = NULL;
3896
3897 Scr.flags.do_need_window_update = 1;
3898 Scr.flags.has_default_font_changed = 1;
3899 Scr.flags.has_default_color_changed = 1;
3900
3901 return;
3902 }
3903
CMD_ColorLimit(F_CMD_ARGS)3904 void CMD_ColorLimit(F_CMD_ARGS)
3905 {
3906 fvwm_debug(__func__, "ColorLimit is obsolete,\n\tuse the "
3907 "fvwm -color-limit option");
3908
3909 return;
3910 }
3911
3912
3913 /* set animation parameters */
CMD_SetAnimation(F_CMD_ARGS)3914 void CMD_SetAnimation(F_CMD_ARGS)
3915 {
3916 char *opt;
3917 int delay;
3918 float pct;
3919 int i = 0;
3920
3921 opt = PeekToken(action, &action);
3922 if (!opt || sscanf(opt,"%d",&delay) != 1)
3923 {
3924 fvwm_debug(__func__,
3925 "Improper milli-second delay as first argument");
3926 return;
3927 }
3928 if (delay > 500)
3929 {
3930 fvwm_debug(__func__,
3931 "Using longer than .5 seconds as between frame"
3932 " animation delay");
3933 }
3934 cmsDelayDefault = delay;
3935 for (opt = PeekToken(action, &action); opt;
3936 opt = PeekToken(action, &action))
3937 {
3938 if (sscanf(opt,"%f",&pct) != 1)
3939 {
3940 fvwm_debug(__func__,
3941 "Use fractional values ending in 1.0 as args"
3942 " 2 and on");
3943 return;
3944 }
3945 rgpctMovementDefault[i++] = pct;
3946 }
3947 /* No pct entries means don't change them at all */
3948 if (i > 0 && rgpctMovementDefault[i-1] != 1.0)
3949 {
3950 rgpctMovementDefault[i++] = 1.0;
3951 }
3952
3953 return;
3954 }
3955
3956 /* Determine which modifiers are required with a keycode to make <keysym>. */
FKeysymToKeycode(Display * dpy,KeySym keysym,unsigned int * keycode,unsigned int * modifiers)3957 static Bool FKeysymToKeycode (Display *dpy, KeySym keysym,
3958 unsigned int *keycode, unsigned int *modifiers)
3959 {
3960 int m;
3961
3962 *keycode = XKeysymToKeycode(dpy, keysym);
3963 *modifiers = 0;
3964
3965 for (m = 0; m <= 8; ++m)
3966 {
3967 KeySym ks = fvwm_KeycodeToKeysym(dpy, *keycode, m, 0);
3968 if (ks == keysym)
3969 {
3970 switch (m)
3971 {
3972 case 0: /* No modifiers */
3973 break;
3974 case 1: /* Shift modifier */
3975 *modifiers |= ShiftMask;
3976 break;
3977 default:
3978 fvwm_debug(__func__,
3979 "Unhandled modifier %d", m);
3980 break;
3981 }
3982 return True;
3983 }
3984 }
3985 return False;
3986 }
3987
__fake_event(F_CMD_ARGS,FakeEventType type)3988 static void __fake_event(F_CMD_ARGS, FakeEventType type)
3989 {
3990 char *token;
3991 char *optlist[] = {
3992 "press", "p",
3993 "release", "r",
3994 "wait", "w",
3995 "modifiers", "m",
3996 "depth", "d",
3997 NULL
3998 };
3999 unsigned int mask = 0;
4000 Window root = Scr.Root;
4001 int maxdepth = 0;
4002 static char args[128];
4003 strncpy(args, action, sizeof(args) - 1);
4004
4005 /* get the mask of pressed/released buttons/keys */
4006 FQueryPointer(
4007 dpy, Scr.Root, &root, &JunkRoot, &JunkX, &JunkY, &JunkX,
4008 &JunkY, &mask);
4009
4010 token = PeekToken(action, &action);
4011 while (token && action)
4012 {
4013 int index = GetTokenIndex(token, optlist, 0, NULL);
4014 int val, depth;
4015 XEvent e;
4016 Window w;
4017 Window child_w;
4018 int x = 0;
4019 int y = 0;
4020 int rx = 0;
4021 int ry = 0;
4022 Bool do_unset;
4023 long add_mask = 0;
4024 KeySym keysym = NoSymbol;
4025
4026 XFlush(dpy);
4027 do_unset = True;
4028 switch (index)
4029 {
4030 case 0:
4031 case 1:
4032 do_unset = False;
4033 /* fall through */
4034 case 2:
4035 case 3:
4036 /* key/button press or release */
4037 if (type == FakeMouseEvent)
4038 {
4039 if ((GetIntegerArguments(
4040 action, &action, &val, 1) != 1) ||
4041 val < 1 ||
4042 val > NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
4043 {
4044 fvwm_debug(__func__,
4045 "Invalid button specifier in"
4046 " \"%s\" for FakeClick.",
4047 args);
4048 return; /* error */
4049 }
4050 }
4051 else /* type == FakeKeyEvent */
4052 {
4053 char *key = PeekToken(action, &action);
4054 if (key == NULL)
4055 {
4056 fvwm_debug(__func__,
4057 "No keysym specifier in \"%s\""
4058 " for FakeKeypress.", args);
4059 return;
4060 }
4061
4062 /* Do *NOT* use FvwmStringToKeysym() as it is
4063 * case insensitive. */
4064 keysym = XStringToKeysym(key);
4065 if (keysym == NoSymbol)
4066 {
4067 fvwm_debug(__func__,
4068 "Invalid keysym specifier (%s)"
4069 " in \"%s\" for FakeKeypress.",
4070 key, args);
4071 return;
4072 }
4073 }
4074
4075 w = None;
4076 child_w = root;
4077 for (depth = 1;
4078 depth != maxdepth &&
4079 w != child_w && child_w != None;
4080 depth++)
4081 {
4082 w = child_w;
4083 if (FQueryPointer(
4084 dpy, w, &root, &child_w,
4085 &rx, &ry, &x, &y,
4086 &JunkMask) == False)
4087 {
4088 /* pointer is on a different
4089 * screen - that's okay here */
4090 }
4091 }
4092
4093 if (type == FakeMouseEvent)
4094 {
4095 e.type = (do_unset) ?
4096 ButtonRelease : ButtonPress;
4097 e.xbutton.display = dpy;
4098 e.xbutton.window = w;
4099 e.xbutton.subwindow = None;
4100 e.xbutton.root = root;
4101 e.xbutton.time = fev_get_evtime();
4102 e.xbutton.x = x;
4103 e.xbutton.y = y;
4104 e.xbutton.x_root = rx;
4105 e.xbutton.y_root = ry;
4106 e.xbutton.button = val;
4107 e.xbutton.state = mask;
4108 e.xbutton.same_screen = (Scr.Root == root);
4109 /* SS: I think this mask handling code is
4110 * buggy.
4111 * The value of <mask> is overridden during a
4112 * "wait" operation. Also why are we only using
4113 * Button1Mask? What if the user has requested
4114 * a FakeClick using some other button? */
4115 /* DV: Button1Mask is actually a bit. Shifting
4116 * it by (val -1) bits to the left gives
4117 * Button2Mask, Button3Mask etc. */
4118 if (do_unset)
4119 {
4120 mask &= ~(Button1Mask << (val - 1));
4121 }
4122 else
4123 {
4124 mask |= (Button1Mask << (val - 1));
4125 }
4126 add_mask = (do_unset) ?
4127 ButtonPressMask : ButtonReleaseMask;
4128 }
4129 else
4130 {
4131 /* type == FakeKeyEvent */
4132 e.type = (do_unset ? KeyRelease : KeyPress);
4133 e.xkey.display = dpy;
4134 e.xkey.subwindow = None;
4135 e.xkey.root = root;
4136 e.xkey.time = fev_get_evtime();
4137 e.xkey.x = x;
4138 e.xkey.y = y;
4139 e.xkey.x_root = rx;
4140 e.xkey.y_root = ry;
4141 e.xkey.same_screen = (Scr.Root == root);
4142
4143 w = e.xkey.window = exc->w.w;
4144
4145 if (FKeysymToKeycode(
4146 dpy, keysym, &(e.xkey.keycode),
4147 &(e.xkey.state)) != True)
4148 {
4149 fvwm_debug(__func__,
4150 "FKeysymToKeycode failed");
4151 return;
4152 }
4153 e.xkey.state |= mask;
4154 add_mask = (do_unset) ?
4155 KeyReleaseMask : KeyPressMask;
4156 }
4157 FSendEvent(dpy, w, True,
4158 SubstructureNotifyMask | add_mask, &e);
4159 XFlush(dpy);
4160 break;
4161 case 4:
4162 case 5:
4163 /* wait */
4164 if ((GetIntegerArguments(
4165 action, &action, &val, 1) != 1) ||
4166 val <= 0 || val > 1000000)
4167 {
4168 fvwm_debug(__func__,
4169 "Invalid wait value in \"%s\"",
4170 args);
4171 return;
4172 }
4173
4174 usleep(1000 * val);
4175 if (FQueryPointer(
4176 dpy, Scr.Root, &root, &JunkRoot,
4177 &JunkX, &JunkY, &JunkX, &JunkY,
4178 &mask) == False)
4179 {
4180 /* pointer is on a different screen -
4181 * that's okay here */
4182 }
4183 break;
4184 case 6:
4185 case 7:
4186 /* set modifier */
4187 if (GetIntegerArguments(action, &action, &val, 1) != 1)
4188 {
4189 fvwm_debug(__func__,
4190 "Invalid modifier value in \"%s\"",
4191 args);
4192 return;
4193 }
4194 do_unset = False;
4195 if (val < 0)
4196 {
4197 do_unset = True;
4198 val = -val;
4199 }
4200 if (val == 6)
4201 {
4202 val = ShiftMask;
4203 }
4204 else if (val == 7)
4205 {
4206 val = LockMask;
4207 }
4208 else if (val == 8)
4209 {
4210 val = ControlMask;
4211 }
4212 else if (val >=1 && val <= 5)
4213 {
4214 val = (Mod1Mask << (val - 1));
4215 }
4216 else
4217 {
4218 /* error */
4219 return;
4220 }
4221 /* SS: Could be buggy if a "modifier" operation
4222 * preceeds a "wait" operation. */
4223 if (do_unset)
4224 {
4225 mask &= ~val;
4226 }
4227 else
4228 {
4229 mask |= val;
4230 }
4231 break;
4232 case 8:
4233 case 9:
4234 /* new max depth */
4235 if (GetIntegerArguments(action, &action, &val, 1) != 1)
4236 {
4237 fvwm_debug(__func__,
4238 "Invalid depth value in \"%s\"",
4239 args);
4240 return;
4241 }
4242 maxdepth = val;
4243 break;
4244 default:
4245 fvwm_debug(__func__,
4246 "Invalid command (%s) in \"%s\"", token,
4247 args);
4248 return;
4249 }
4250 if (action)
4251 {
4252 token = PeekToken(action, &action);
4253 }
4254 }
4255
4256 return;
4257 }
4258
CMD_FakeClick(F_CMD_ARGS)4259 void CMD_FakeClick(F_CMD_ARGS)
4260 {
4261 __fake_event(F_PASS_ARGS, FakeMouseEvent);
4262
4263 return;
4264 }
4265
CMD_FakeKeypress(F_CMD_ARGS)4266 void CMD_FakeKeypress(F_CMD_ARGS)
4267 {
4268 __fake_event(F_PASS_ARGS, FakeKeyEvent);
4269
4270 return;
4271 }
4272
CMD_State(F_CMD_ARGS)4273 void CMD_State(F_CMD_ARGS)
4274 {
4275 unsigned int state;
4276 int toggle;
4277 int n;
4278 FvwmWindow * const fw = exc->w.fw;
4279
4280 n = GetIntegerArguments(action, &action, (int *)&state, 1);
4281 if (n <= 0)
4282 {
4283 return;
4284 }
4285 if (state > 31)
4286 {
4287 fvwm_debug(__func__, "Illegal state %d\n", state);
4288 return;
4289 }
4290 toggle = ParseToggleArgument(action, NULL, -1, 0);
4291 state = (1 << state);
4292 switch (toggle)
4293 {
4294 case -1:
4295 TOGGLE_USER_STATES(fw, state);
4296 break;
4297 case 0:
4298 CLEAR_USER_STATES(fw, state);
4299 break;
4300 case 1:
4301 default:
4302 SET_USER_STATES(fw, state);
4303 break;
4304 }
4305
4306 return;
4307 }
4308