2 /* FvwmBacker Module for fvwm.
4 * Copyright 1994, Mike Finger (mfinger@mermaid.micro.umn.edu or
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.
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
51 #if HAVE_SYS_BSDTYPES_H
52 #include <sys/bsdtypes.h> /* Saul */
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 */
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
84 unsigned do_ignore_desk
: 1;
85 unsigned do_ignore_page
: 1;
86 unsigned do_match_any_desk
: 1;
88 int x
; /* Page X coordinate; -1 for glob */
89 int y
; /* Page Y coordinate; -1 for glob */
92 * 0 = command to be spawned
93 * 1 = use a solid color background
94 * 2 = use a colorset background
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
;
111 CommandChain
*commands
;
113 int current_desk
= 0;
116 int current_colorset
= -1; /* the last matched command colorset or -1 */
120 char *Module
; /* i.e. "FvwmBacker" */
121 char *configPrefix
; /* i.e. "*FvwmBacker" */
124 /* X Display information. */
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
)
147 PrintXErrorAndCoredump(d
, event
, Module
);
151 int main(int argc
, char **argv
)
155 commands
= (CommandChain
*)safemalloc(sizeof(CommandChain
));
156 commands
->first
= commands
->last
= NULL
;
158 /* Save the program name for error messages and config parsing */
160 s
= strrchr(argv
[0], '/');
167 configPrefix
= CatString2("*", Module
);
169 if ((argc
!= 6) && (argc
!= 7))
172 "%s Version %s should only be executed by fvwm!\n",
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
);
185 fprintf(stderr
, "%s: unable to open display '%s'\n",
186 Module
, XDisplayName (displayName
));
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 */
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 */
220 /* Should never get here! */
225 EndLessLoop - Read until we get killed, blocking when can't read
227 void EndLessLoop(void)
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
)
247 ProcessMessage( packet
->type
, packet
->body
);
251 void DeleteRootAtoms(Display
*dpy2
, Window root2
)
255 unsigned long length
, after
;
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
)
278 XKillClient(dpy2
, *((Pixmap
*)data
));
282 XDeleteProperty(dpy2
, root2
, XA_XROOTPMAP_ID
);
286 void SetRootAtoms(Display
*dpy2
, Window root2
, Pixmap RootPix
)
290 l_root_pix
= RootPix
;
291 if (RetainPixmap
&& RootPix
!= None
)
294 dpy2
, root2
, XA_ESETROOT_PMAP_ID
, XA_PIXMAP
, 32,
296 (unsigned char *) &l_root_pix
, 1);
298 dpy2
, root2
, XA_XROOTPMAP_ID
, XA_PIXMAP
, 32,
300 (unsigned char *) &l_root_pix
, 1);
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
;
317 current_colorset
= -1;
319 /* FvwmBacker bg preperation */
322 case 1: /* solid colors */
323 case 2: /* colorset */
324 dpy2
= XOpenDisplay(displayName
);
327 fvwm_msg(ERR
, "FvwmBacker",
328 "Fail to create a forking dpy, Exit!");
331 screen2
= DefaultScreen(dpy2
);
332 root2
= RootWindow(dpy2
, screen2
);
335 XSetCloseDownMode(dpy2
, RetainPermanent
);
338 DeleteRootAtoms(dpy2
, root2
);
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 "
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!");
363 else if (RetainPixmap
)
365 pix
= CreateBackgroundPixmap(
366 dpy2
, root2
, MyDisplayWidth
,
368 &Colorset
[c
->colorset
],
369 DefaultDepth(dpy2
, screen2
),
370 DefaultGC(dpy2
, screen2
), False
);
373 XSetWindowBackgroundPixmap(
375 XClearWindow(dpy2
, root2
);
381 dpy2
, root2
, MyDisplayWidth
,
383 &Colorset
[c
->colorset
],
384 DefaultDepth(dpy2
, screen2
),
385 DefaultGC(dpy2
, screen2
), True
);
388 case 1: /* Process a solid color request */
394 xgcv
.foreground
= c
->solidColor
;
395 gc
= fvwmlib_XCreateGC(
396 dpy2
, root2
, GCForeground
,
400 DefaultDepth(dpy2
, screen2
));
402 dpy2
, pix
, gc
, 0, 0, 1, 1);
405 XSetWindowBackground(dpy2
, root2
, c
->solidColor
);
406 XClearWindow(dpy2
, root2
);
409 SetRootAtoms(dpy2
, root2
, pix
);
411 XCloseDisplay(dpy2
); /* this XSync, Ungrab, ...etc */
416 if(c
->cmdStr
!= NULL
)
418 SendFvwmPipe(fvwm_fd
, c
->cmdStr
, (unsigned long)0);
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
;
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
)))
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
)
470 static Bool is_initial
= True
;
475 tline
= (char*)&(body
[3]);
476 ExecuteMatchingCommands(ParseConfigLine(tline
), EXEC_ALWAYS
);
482 change
= EXEC_ALWAYS
;
485 else if (current_desk
!= body
[0])
487 current_desk
= body
[0];
488 change
|= EXEC_CHANGED_DESK
;
490 ExecuteMatchingCommands(-1, change
);
496 change
= EXEC_ALWAYS
;
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
);
518 Detected a broken pipe - time to exit
520 RETSIGTYPE
DeadPipe(int nonsense
)
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)
538 line
+cpl
, "RetainPixmap", 12) == 0)
542 else if (strncasecmp(
544 "DoNotRetainPixmap", 17) == 0)
546 RetainPixmap
= False
;
550 AddCommand(line
+ cpl
);
553 else if (strncasecmp(line
, "colorset", 8) == 0)
555 return LoadColorset(line
+ 8);
562 ParseConfig - Parse the configuration file fvwm to us to use
564 void ParseConfig(void)
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
);
584 Bool
ParseNewCommand(
585 char *parens
, Command
*this, int *do_ignore_desk
, int *do_ignore_page
)
592 while (*parens_run
&&
593 (parens_run
= GetNextFullOption( parens_run
, &option
)) != NULL
)
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");
613 if (!StrEquals(value
, "*"))
615 this->flags
.do_match_any_desk
= 0;
616 this->desk
= atoi(value
);
618 *do_ignore_desk
= False
;
621 else if (StrEquals(name
, "Page"))
623 if ((option_val
= GetNextToken(
624 option_val
,&value
)) == NULL
)
628 "Page option requires 2 values");
631 if (!StrEquals(value
, "*"))
632 this->x
= atoi(value
);
634 if (GetNextToken(option_val
, &value
) == NULL
)
638 "Desk option requires 2 values");
641 if (!StrEquals(value
, "*"))
642 this->y
= atoi(value
);
643 *do_ignore_page
= False
;
654 AddCommand - Add a command to the correct spot on the dynamic array.
656 void AddCommand(char *line
)
660 Bool do_ignore_desk
= True
;
661 Bool do_ignore_page
= True
;
663 this = (Command
*)safemalloc(sizeof(Command
));
665 memset(&(this->flags
), 0, sizeof(this->flags
));
666 this->flags
.do_match_any_desk
= 1;
673 if (strncasecmp(line
, "Desk", 4) == 0)
675 /* Old command style */
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
;
687 else if (strncasecmp(line
, "Command", 7) == 0)
689 /* New command style */
691 while (*line
&& isspace(*line
))
701 line
= GetQuotedString(
702 line
, &parens
, ")", NULL
, NULL
, NULL
);
707 "Syntax error: no closing brace");
711 error
= ParseNewCommand(
712 parens
, this, &do_ignore_desk
,
726 CatString2("Unknown directive: ", line
));
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
))
738 if (strncasecmp(line
, "-solid", 6) == 0)
740 /* Solid color command */
743 line
= GetNextToken(line
, &token
);
745 /* use the root default color map */
746 this->solidColor
= (!token
|| !*token
) ?
747 BlackPixel(dpy
, screen
) :
748 BackerGetColor(token
);
751 else if (strncasecmp(line
, "colorset", 8) == 0)
753 /* Colorset command */
755 if (sscanf(line
+ 8, "%d", &this->colorset
) < 1)
759 AllocColorset(this->colorset
);
764 /* Plain fvwm command */
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;