Copy purify.fvwm2rc to /tmp - add instructions in README.
[fvwm.git] / modules / FvwmBacker / FvwmBacker.c
blobda556496622f12642b67fefefc6981b42c20b214
1 /* -*-c-*- */
2 /* FvwmBacker Module for fvwm.
4 * Copyright 1994, Mike Finger (mfinger@mermaid.micro.umn.edu or
5 * Mike_Finger@atk.com)
7 * The author makes not guarantees or warantees, either express or
8 * implied. Feel free to use any contained here for any purpose, as long
9 * and this and any other applicible copyrights are kept intact.
11 * The functions in this source file that are based on part of the FvwmIdent
12 * module for fvwm are noted by a small copyright atop that function, all others
13 * are copyrighted by Mike Finger. For those functions modified/used, here is
14 * the full, origonal copyright:
16 * Copyright 1994, Robert Nation and Nobutaka Suzuki.
17 * No guarantees or warantees or anything
18 * are provided or implied in any way whatsoever. Use this program at your
19 * own risk. Permission to use this program for any purpose is given,
20 * as long as the copyright is kept intact. */
22 /* Modified to directly manipulate the X server if a solid color
23 * background is requested. To use this, use "-solid <color_name>"
24 * as the command to be executed.
26 * A. Davison
27 * Septmber 1994.
29 /* This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 2 of the License, or
32 * (at your option) any later version.
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
39 * You should have received a copy of the GNU General Public License
40 * along with this program; if not, write to the Free Software
41 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44 #include "config.h"
46 #include <stdio.h>
47 #include <signal.h>
48 #include <unistd.h>
49 #include <sys/wait.h>
51 #if HAVE_SYS_BSDTYPES_H
52 #include <sys/bsdtypes.h> /* Saul */
53 #endif /* Saul */
55 #include <X11/Xlib.h>
56 #include <X11/Intrinsic.h>
57 #include <X11/Xatom.h>
59 #include "libs/FShape.h"
60 #include "libs/Module.h"
61 #include "libs/Colorset.h"
62 #include "libs/Parse.h"
63 #include "libs/PictureBase.h"
64 #include "libs/FRenderInit.h"
65 #include "libs/Graphics.h"
66 #include "libs/XError.h"
67 #include "FvwmBacker.h"
69 /* migo (22-Nov-1999): Temporarily until fvwm_msg is moved to libs */
70 #define ERR
71 #define fvwm_msg(t,l,f) fprintf(stderr, "[fvwm][FvwmBacker]: <<ERROR>> %s\n", f)
73 unsigned long BackerGetColor(char *color);
75 #define EXEC_CHANGED_PAGE 0x01
76 #define EXEC_CHANGED_DESK 0x02
77 #define EXEC_ALWAYS 0x04
79 struct Command
81 int desk;
82 struct
84 unsigned do_ignore_desk : 1;
85 unsigned do_ignore_page : 1;
86 unsigned do_match_any_desk : 1;
87 } flags;
88 int x; /* Page X coordinate; -1 for glob */
89 int y; /* Page Y coordinate; -1 for glob */
90 /* The command type:
91 * -1 = no command
92 * 0 = command to be spawned
93 * 1 = use a solid color background
94 * 2 = use a colorset background
96 int type;
97 char* cmdStr; /* The command string (Type 0) */
98 unsigned long solidColor; /* A solid color after X parsing (Type 1) */
99 int colorset; /* The colorset to be used (Type 2) */
100 struct Command *next;
103 typedef struct Command Command;
105 typedef struct
107 Command *first;
108 Command *last;
109 } CommandChain;
111 CommandChain *commands;
113 int current_desk = 0;
114 int current_x = 0;
115 int current_y = 0;
116 int current_colorset = -1; /* the last matched command colorset or -1 */
118 int fvwm_fd[2];
120 char *Module; /* i.e. "FvwmBacker" */
121 char *configPrefix; /* i.e. "*FvwmBacker" */
124 /* X Display information. */
126 Display* dpy;
127 Window root;
128 int screen;
129 int MyDisplayHeight;
130 int MyDisplayWidth;
131 char* displayName = NULL;
133 Bool RetainPixmap = False;
134 Atom XA_XSETROOT_ID = None;
135 Atom XA_ESETROOT_PMAP_ID = None;
136 Atom XA_XROOTPMAP_ID = None;
138 unsigned char *XsetrootData = NULL;
141 ErrorHandler(Display *d, XErrorEvent *event)
143 /* some errors are OK=ish */
144 if (0 && event->error_code == BadValue)
145 return 0;
147 PrintXErrorAndCoredump(d, event, Module);
148 return 0;
151 int main(int argc, char **argv)
153 char *temp, *s;
155 commands = (CommandChain*)safemalloc(sizeof(CommandChain));
156 commands->first = commands->last = NULL;
158 /* Save the program name for error messages and config parsing */
159 temp = argv[0];
160 s = strrchr(argv[0], '/');
161 if (s != NULL)
163 temp = s + 1;
166 Module = temp;
167 configPrefix = CatString2("*", Module);
169 if ((argc != 6) && (argc != 7))
171 fprintf(stderr,
172 "%s Version %s should only be executed by fvwm!\n",
173 Module, VERSION);
174 exit(1);
177 fvwm_fd[0] = atoi(argv[1]);
178 fvwm_fd[1] = atoi(argv[2]);
180 /* Grab the X display information now. */
182 dpy = XOpenDisplay(displayName);
183 if (!dpy)
185 fprintf(stderr, "%s: unable to open display '%s'\n",
186 Module, XDisplayName (displayName));
187 exit (2);
189 screen = DefaultScreen(dpy);
190 root = RootWindow(dpy, screen);
191 MyDisplayHeight = DisplayHeight(dpy, screen);
192 MyDisplayWidth = DisplayWidth(dpy, screen);
193 XSetErrorHandler(ErrorHandler);
194 flib_init_graphics(dpy);
196 XA_XSETROOT_ID = XInternAtom(dpy, "_XSETROOT_ID", False);
197 XA_ESETROOT_PMAP_ID = XInternAtom(dpy, "ESETROOT_PMAP_ID", False);
198 XA_XROOTPMAP_ID = XInternAtom(dpy, "_XROOTPMAP_ID", False);
200 signal (SIGPIPE, DeadPipe);
202 /* Parse the config file */
203 ParseConfig();
205 SetMessageMask(fvwm_fd,
206 M_NEW_PAGE | M_NEW_DESK | M_CONFIG_INFO |
207 M_END_CONFIG_INFO | M_SENDCONFIG);
210 ** we really only want the current desk/page, and window list sends it
212 SendInfo(fvwm_fd, "Send_WindowList", 0);
214 /* tell fvwm we're running */
215 SendFinishedStartupNotification(fvwm_fd);
217 /* Recieve all messages from fvwm */
218 EndLessLoop();
220 /* Should never get here! */
221 return 1;
225 EndLessLoop - Read until we get killed, blocking when can't read
227 void EndLessLoop(void)
229 while(1)
231 ReadFvwmPipe();
236 ReadFvwmPipe - Read a single message from the pipe from fvwm
238 void ReadFvwmPipe(void)
240 FvwmPacket* packet = ReadFvwmPacket(fvwm_fd[1]);
241 if ( packet == NULL )
243 exit(0);
245 else
247 ProcessMessage( packet->type, packet->body );
251 void DeleteRootAtoms(Display *dpy2, Window root2)
253 Atom type;
254 int format;
255 unsigned long length, after;
256 unsigned char *data;
257 Bool e_deleted = False;
259 XA_XSETROOT_ID = XInternAtom(dpy2, "_XSETROOT_ID", False);
260 XA_ESETROOT_PMAP_ID = XInternAtom(dpy2, "ESETROOT_PMAP_ID", False);
261 XA_XROOTPMAP_ID = XInternAtom(dpy2, "_XROOTPMAP_ID", False);
263 if (XGetWindowProperty(
264 dpy2, root2, XA_XSETROOT_ID, 0L, 1L, True,
265 XA_PIXMAP, &type, &format, &length, &after, &data) ==
266 Success && type == XA_PIXMAP && format == 32 && length == 1 &&
267 after == 0 && (Pixmap)(*(long *)data) != None)
269 XKillClient(dpy2, *((Pixmap *)data));
271 if (XGetWindowProperty(
272 dpy2, root2, XA_ESETROOT_PMAP_ID, 0L, 1L, True,
273 XA_PIXMAP, &type, &format, &length, &after, &data) ==
274 Success && type == XA_PIXMAP && format == 32 && length == 1 &&
275 after == 0 && (Pixmap)(*(Pixmap *)data) != None)
277 e_deleted = True;
278 XKillClient(dpy2, *((Pixmap *)data));
280 if (e_deleted)
282 XDeleteProperty(dpy2, root2, XA_XROOTPMAP_ID);
286 void SetRootAtoms(Display *dpy2, Window root2, Pixmap RootPix)
288 long l_root_pix;
290 l_root_pix = RootPix;
291 if (RetainPixmap && RootPix != None)
293 XChangeProperty(
294 dpy2, root2, XA_ESETROOT_PMAP_ID, XA_PIXMAP, 32,
295 PropModeReplace,
296 (unsigned char *) &l_root_pix, 1);
297 XChangeProperty(
298 dpy2, root2, XA_XROOTPMAP_ID, XA_PIXMAP, 32,
299 PropModeReplace,
300 (unsigned char *) &l_root_pix, 1);
302 else
304 XChangeProperty(
305 dpy2, root2, XA_XSETROOT_ID, XA_PIXMAP, 32,
306 PropModeReplace, (unsigned char *) &l_root_pix, 1);
310 void SetDeskPageBackground(const Command *c)
312 Display *dpy2 = NULL;
313 Window root2 = None;
314 int screen2;
315 Pixmap pix = None;
317 current_colorset = -1;
319 /* FvwmBacker bg preperation */
320 switch (c->type)
322 case 1: /* solid colors */
323 case 2: /* colorset */
324 dpy2 = XOpenDisplay(displayName);
325 if (!dpy2)
327 fvwm_msg(ERR, "FvwmBacker",
328 "Fail to create a forking dpy, Exit!");
329 exit(2);
331 screen2 = DefaultScreen(dpy2);
332 root2 = RootWindow(dpy2, screen2);
333 if (RetainPixmap)
335 XSetCloseDownMode(dpy2, RetainPermanent);
337 XGrabServer(dpy2);
338 DeleteRootAtoms(dpy2, root2);
339 switch (c->type)
341 case 2:
342 current_colorset = c->colorset;
343 /* Process a colorset */
344 if (CSET_IS_TRANSPARENT(c->colorset))
346 fvwm_msg(ERR,"FvwmBacker", "You cannot "
347 "use a transparent colorset as "
348 "background!");
349 XUngrabServer(dpy2);
350 XCloseDisplay(dpy2);
351 return;
353 else if (Pdepth != DefaultDepth(dpy2, screen2))
355 fvwm_msg(ERR,"FvwmBacker", "You cannot "
356 "use a colorset background if\n"
357 "the fvwm depth is not equal "
358 "to the root depth!");
359 XUngrabServer(dpy2);
360 XCloseDisplay(dpy2);
361 return;
363 else if (RetainPixmap)
365 pix = CreateBackgroundPixmap(
366 dpy2, root2, MyDisplayWidth,
367 MyDisplayHeight,
368 &Colorset[c->colorset],
369 DefaultDepth(dpy2, screen2),
370 DefaultGC(dpy2, screen2), False);
371 if (pix != None)
373 XSetWindowBackgroundPixmap(
374 dpy2, root2, pix);
375 XClearWindow(dpy2, root2);
378 else
380 SetWindowBackground(
381 dpy2, root2, MyDisplayWidth,
382 MyDisplayHeight,
383 &Colorset[c->colorset],
384 DefaultDepth(dpy2, screen2),
385 DefaultGC(dpy2, screen2), True);
387 break;
388 case 1: /* Process a solid color request */
389 if (RetainPixmap)
391 GC gc;
392 XGCValues xgcv;
394 xgcv.foreground = c->solidColor;
395 gc = fvwmlib_XCreateGC(
396 dpy2, root2, GCForeground,
397 &xgcv);
398 pix = XCreatePixmap(
399 dpy2, root2, 1, 1,
400 DefaultDepth(dpy2, screen2));
401 XFillRectangle(
402 dpy2, pix, gc, 0, 0, 1, 1);
403 XFreeGC(dpy2, gc);
405 XSetWindowBackground(dpy2, root2, c->solidColor);
406 XClearWindow(dpy2, root2);
407 break;
409 SetRootAtoms(dpy2, root2, pix);
410 XUngrabServer(dpy2);
411 XCloseDisplay(dpy2); /* this XSync, Ungrab, ...etc */
412 break;
413 case -1:
414 case 0:
415 default:
416 if(c->cmdStr != NULL)
418 SendFvwmPipe(fvwm_fd, c->cmdStr, (unsigned long)0);
420 break;
425 * migo (23-Nov-1999): Maybe execute only first (or last?) matching command?
426 * migo (03-Apr-2001): Yes, execute only the last matching command.
428 void ExecuteMatchingCommands(int colorset, int changed)
430 const Command *matching_command = NULL;
431 const Command *command;
433 if (!changed)
435 return;
437 for (command = commands->first; command; command = command->next)
439 if (!(changed & EXEC_ALWAYS) &&
440 (command->flags.do_ignore_desk ||
441 !(changed & EXEC_CHANGED_DESK)) &&
442 (command->flags.do_ignore_page ||
443 !(changed & EXEC_CHANGED_PAGE)))
445 continue;
447 if ((command->flags.do_match_any_desk ||
448 command->desk == current_desk) &&
449 (command->x < 0 || command->x == current_x) &&
450 (command->y < 0 || command->y == current_y) &&
451 (colorset < 0 || (colorset == current_colorset &&
452 colorset == command->colorset)))
454 matching_command = command;
457 if (matching_command)
459 SetDeskPageBackground(matching_command);
464 ProcessMessage - Process the message coming from fvwm
466 void ProcessMessage(unsigned long type, unsigned long *body)
468 char *tline;
469 int change = 0;
470 static Bool is_initial = True;
472 switch (type)
474 case M_CONFIG_INFO:
475 tline = (char*)&(body[3]);
476 ExecuteMatchingCommands(ParseConfigLine(tline), EXEC_ALWAYS);
477 break;
479 case M_NEW_DESK:
480 if (is_initial)
482 change = EXEC_ALWAYS;
483 is_initial = False;
485 else if (current_desk != body[0])
487 current_desk = body[0];
488 change |= EXEC_CHANGED_DESK;
490 ExecuteMatchingCommands(-1, change);
491 break;
493 case M_NEW_PAGE:
494 if (is_initial)
496 change = EXEC_ALWAYS;
497 is_initial = False;
499 if (current_desk != body[2])
501 current_desk = body[2];
502 change |= EXEC_CHANGED_DESK;
504 if (current_x != body[0] / MyDisplayWidth ||
505 current_y != body[1] / MyDisplayHeight)
507 current_x = body[0] / MyDisplayWidth;
508 current_y = body[1] / MyDisplayHeight;
509 change |= EXEC_CHANGED_PAGE;
511 ExecuteMatchingCommands(-1, change);
512 break;
518 Detected a broken pipe - time to exit
520 RETSIGTYPE DeadPipe(int nonsense)
522 exit(1);
523 SIGNAL_RETURN;
527 ParseConfigLine - Parse the configuration line fvwm to us to use
529 int ParseConfigLine(char *line)
531 int cpl = strlen(configPrefix);
533 if (strlen(line) > 1)
535 if (strncasecmp(line, configPrefix, cpl) == 0)
537 if (strncasecmp(
538 line+cpl, "RetainPixmap", 12) == 0)
540 RetainPixmap = True;
542 else if (strncasecmp(
543 line+cpl,
544 "DoNotRetainPixmap", 17) == 0)
546 RetainPixmap = False;
548 else
550 AddCommand(line + cpl);
553 else if (strncasecmp(line, "colorset", 8) == 0)
555 return LoadColorset(line + 8);
558 return -1;
562 ParseConfig - Parse the configuration file fvwm to us to use
564 void ParseConfig(void)
566 char *line_start;
567 char *tline;
569 line_start = safemalloc(strlen(Module) + 2);
570 strcpy(line_start, "*");
571 strcat(line_start, Module);
573 InitGetConfigLine(fvwm_fd, line_start);
574 GetConfigLine(fvwm_fd, &tline);
576 while(tline != (char *)0)
578 ParseConfigLine(tline);
579 GetConfigLine(fvwm_fd, &tline);
581 free(line_start);
584 Bool ParseNewCommand(
585 char *parens, Command *this, int *do_ignore_desk, int *do_ignore_page)
587 char *option;
588 char *parens_run;
590 parens_run = parens;
592 while (*parens_run &&
593 (parens_run = GetNextFullOption( parens_run, &option)) != NULL)
595 char *name, *value;
596 char *option_val;
598 if (!*option)
600 free(option);
601 break;
603 option_val = GetNextToken(option, &name);
605 if (StrEquals(name, "Desk"))
607 if (GetNextToken(option_val, &value) == NULL)
609 fvwm_msg(ERR, "FvwmBacker",
610 "Desk option requires a value");
611 return 1;
613 if (!StrEquals(value, "*"))
615 this->flags.do_match_any_desk = 0;
616 this->desk = atoi(value);
618 *do_ignore_desk = False;
619 free(value);
621 else if (StrEquals(name, "Page"))
623 if ((option_val = GetNextToken(
624 option_val,&value)) == NULL)
626 fvwm_msg(
627 ERR, "FvwmBacker",
628 "Page option requires 2 values");
629 return 1;
631 if (!StrEquals(value, "*"))
632 this->x = atoi(value);
633 free(value);
634 if (GetNextToken(option_val, &value) == NULL)
636 fvwm_msg(
637 ERR, "FvwmBacker",
638 "Desk option requires 2 values");
639 return 1;
641 if (!StrEquals(value, "*"))
642 this->y = atoi(value);
643 *do_ignore_page = False;
644 free(value);
646 free(name);
647 free(option);
649 free(parens);
650 return 0;
654 AddCommand - Add a command to the correct spot on the dynamic array.
656 void AddCommand(char *line)
658 char *token;
659 Command *this;
660 Bool do_ignore_desk = True;
661 Bool do_ignore_page = True;
663 this = (Command*)safemalloc(sizeof(Command));
664 this->desk = 0;
665 memset(&(this->flags), 0, sizeof(this->flags));
666 this->flags.do_match_any_desk = 1;
667 this->x = -1;
668 this->y = -1;
669 this->type = -1;
670 this->cmdStr = NULL;
671 this->next = NULL;
673 if (strncasecmp(line, "Desk", 4) == 0)
675 /* Old command style */
677 line += 4;
678 line = GetNextToken(line, &token);
679 if (strcasecmp(token, "*") != 0)
681 this->flags.do_match_any_desk = 0;
682 this->desk = atoi(token);
684 do_ignore_desk = False;
685 free(token);
687 else if (strncasecmp(line, "Command", 7) == 0)
689 /* New command style */
690 line += 7;
691 while (*line && isspace(*line))
693 line++;
695 if (*line == '(')
697 int error;
698 char *parens;
700 line++;
701 line = GetQuotedString(
702 line, &parens, ")", NULL, NULL, NULL);
703 if (line == NULL)
705 fvwm_msg(
706 ERR, "FvwmBacker",
707 "Syntax error: no closing brace");
708 free(this);
709 return;
711 error = ParseNewCommand(
712 parens, this, &do_ignore_desk,
713 &do_ignore_page);
714 if (error)
716 free(this);
717 return;
719 line++;
722 else
724 fvwm_msg(
725 ERR, "FvwmBacker",
726 CatString2("Unknown directive: ", line));
727 return;
729 this->flags.do_ignore_desk = do_ignore_desk;
730 this->flags.do_ignore_page = do_ignore_page;
732 /* Now check the type of command... */
734 while (*line && isspace(*line))
736 line++;
738 if (strncasecmp(line, "-solid", 6) == 0)
740 /* Solid color command */
742 line += 6;
743 line = GetNextToken(line, &token);
744 this->type = 1;
745 /* use the root default color map */
746 this->solidColor = (!token || !*token) ?
747 BlackPixel(dpy, screen) :
748 BackerGetColor(token);
749 free(token);
751 else if (strncasecmp(line, "colorset", 8) == 0)
753 /* Colorset command */
755 if (sscanf(line + 8, "%d", &this->colorset) < 1)
757 this->colorset = 0;
759 AllocColorset(this->colorset);
760 this->type = 2;
762 else
764 /* Plain fvwm command */
766 this->type = 0;
767 this->cmdStr = (char *)safemalloc(strlen(line) + 1);
768 strcpy(this->cmdStr, line);
771 if (commands->first == NULL)
773 commands->first = this;
775 if (commands->last != NULL)
777 commands->last->next = this;
779 commands->last = this;