4 /* Sometimes i want to get quick access to this flag :-)*/
6 /* Time-stamp: <00/05/15 23:13:43 friedel> */
8 /* Copyright 2000 Friedrich Delgado Friedrichs */
10 /* Swallow applications in the Windowmaker dock */
11 /* Originally i started with asbeats 0.2 (by iznogood@bohemians.org) (simply
12 the smallest WindowMaker dockapp i could find, since there was no proper
13 Documentation to find on how to make an app dockable), which i stripped
14 down to have a basic dockable app, and then stole code from fvwm2 to
15 swallow an Application */
16 /* Man, this was easy! Why did nobody implement this before? (That's me,
17 looking astonished at the first working version at Mon Apr 17 02:16:31
18 CEST 2000 after only 4 hours of mostly learning how to program the X-Environment and
19 hacking and guessing a little :-) */
25 #include <X11/Xutil.h>
26 #include <X11/extensions/shape.h>
28 #include <X11/Xatom.h>
33 /* That's all we need from xpm.h */
34 #if ! defined(_XtIntrinsic_h) && ! defined(PIXEL_ALREADY_TYPEDEFED)
35 typedef unsigned long Pixel
; /* Index into colormap */
36 # define PIXEL_ALREADY_TYPEDEFED
38 /* Now we got rid of that stupid libXpm dependency :-) */
42 /* 55x57 seems to be the default size for a WindowMaker dockapp */
43 /* settable by "-geometry" switch */
49 XSizeHints mysizehints
;
51 #define MW_EVENTS (ExposureMask | ButtonPressMask |\
52 StructureNotifyMask |\
54 EnterWindowMask|LeaveWindowMask)
55 #define SW_EVENTS (PropertyChangeMask | StructureNotifyMask |\
56 ResizeRedirectMask | SubstructureNotifyMask)
57 #define FIND_EVENTS (SubstructureNotifyMask | StructureNotifyMask)
58 #define READY_EVENTS FIND_EVENTS
62 Pixel
GetColor(char *name
);
64 void usage (char *progname
);
65 int parseargs(int argc
, char *argv
[]);
66 Window
findnamedwindow (char *class);
67 Window
findnamedwindowacc (char *class, Window window
);
68 int checkwindow (Window window
, char *class);
69 int execstuff(int argc
, char *oldargv
[]);
70 Window
startandfind(int argc
, char *oldargv
[], char* class);
71 int printlist(FILE * stream
, char * string
, char **stringlist
);
72 int flush_expose (Window w
);
73 void stealshape (Window w
);
74 int sendexpose (Window w
);
75 void waitformap (Window win
);
76 /* int softenwindow (Window w); */ /* won't work, kept for historical reasons */
78 /* Parameters that can be customized via commandline switches */
79 char *execstring
=NULL
;
86 char *display_name
=NULL
;
88 int main(int argc
,char *argv
[])
93 Pixel back_pix
,fore_pix
;
96 unsigned int borderwidth
;
97 char *wname
="wmswallow";
99 int remainarg
, remainargc
;
103 XClassHint classHint
;
105 remainarg
=parseargs(argc
, argv
); /* remainarg > 0 afterwards */
106 remainargc
=argc
-remainarg
;
108 fprintf(stderr
, "remainarg: %d, remainargc: %d, argc: %d\n", remainarg
,
113 if (!(dpy
= XOpenDisplay(display_name
))) {
114 fprintf(stderr
,"wmswallow: can't open display %s\n",
115 XDisplayName(display_name
));
118 screen
=DefaultScreen(dpy
);
119 Root
=RootWindow(dpy
, screen
);
121 /* So, now we've got everything we need to get Events from the XServer */
123 winid
=startandfind(remainargc
-1, argv
+remainarg
+1, argv
[remainarg
]);
125 perror("wmswallow: startandfind failed");
126 /* Real error handling in execstuff()*/
131 d_depth
=DefaultDepth(dpy
, screen
);
132 /* XConnectionNumber(dpy); */ /* useless */
133 mysizehints
.flags
=USSize
|USPosition
;
136 back_pix
=GetColor("white");
137 fore_pix
=GetColor("black");
138 XWMGeometry(dpy
, screen
, geometry
, NULL
, (borderwidth
=1),
139 &mysizehints
, &mysizehints
.x
, &mysizehints
.y
,
140 &mysizehints
.width
, &mysizehints
.height
, &i
);
141 mysizehints
.width
=WIDTH
;
142 mysizehints
.height
=HEIGHT
;
143 if (geometry
!=NULL
) {
145 fprintf(stderr
,"Setting geometry to: %s\n",geometry
);
148 XParseGeometry(geometry
, &mysizehints
.x
, &mysizehints
.y
,
149 &mysizehints
.width
, &mysizehints
.height
);
152 win
=XCreateSimpleWindow(dpy
, Root
, mysizehints
.x
, mysizehints
.y
,
153 mysizehints
.width
, mysizehints
.height
, borderwidth
,
155 iconwin
=XCreateSimpleWindow(dpy
, win
, mysizehints
.x
, mysizehints
.y
,
156 mysizehints
.width
, mysizehints
.height
, borderwidth
,
158 XSetWMNormalHints(dpy
, win
, &mysizehints
);
159 classHint
.res_name
="wmswallow";
160 classHint
.res_class
="WMswallow";
161 XSetClassHint(dpy
, win
, &classHint
);
162 XSelectInput(dpy
, win
, MW_EVENTS
);
163 XSelectInput(dpy
, iconwin
, MW_EVENTS
);
164 if(XStringListToTextProperty(&wname
, 1, &name
)==0)
166 fprintf(stderr
, "wmswallow: can't allocate window name\n");
169 XSetWMName(dpy
, win
, &name
);
170 mywmhints
.initial_state
= WithdrawnState
;
171 mywmhints
.icon_window
= iconwin
;
172 mywmhints
.icon_x
= mysizehints
.x
;
173 mywmhints
.icon_y
= mysizehints
.y
;
174 mywmhints
.window_group
= win
;
175 mywmhints
.flags
= StateHint
| IconWindowHint
|
176 IconPositionHint
| WindowGroupHint
;
177 XSetWMHints(dpy
, win
, &mywmhints
);
178 XSetCommand(dpy
, win
, argv
, argc
);
181 swallowed
=findnamedwindow(argv
[remainarg
]); /* Find which window to
184 fprintf(stderr
,"%s has Window-id 0x%lx\n", argv
[remainarg
], swallowed
);
193 XReparentWindow(dpy
, swallowed
, iconwin
, 0, 0);
195 /* softenwindow (swallowed); */ /* Change some attributes */
196 XSelectInput(dpy
, swallowed
, SW_EVENTS
|ButtonPressMask
);
199 XSelectInput(dpy
, swallowed
, SW_EVENTS
); /* Workaround for apps like
200 perfmeter that don't let us
201 get their mouseclicks :-( */
203 XSetWindowBorderWidth(dpy
, swallowed
,0);
204 XMoveResizeWindow(dpy
, swallowed
, 0, 0,
205 mysizehints
.width
, mysizehints
.height
);
207 /* Now we do some special juju for shaped windows: */
209 /* ...tell the window to repaint itself, please! */
211 sendexpose(swallowed
);
213 /* ... ok, window should be repainted and a shaped window should have updated
214 its mask accordingly! (-: End of shape-juju :-) */
216 /* Now steal the shape of the Window we just swallowed! */
217 stealshape(swallowed
); }
219 XMapSubwindows(dpy
,win
);
224 while (XPending(dpy
))
226 XNextEvent(dpy
,&Event
);
231 fprintf (stderr
, "wmswallow: Got ButtonPress Event\n");
238 if(Event
.xexpose
.count
== 0 ) {
240 fprintf (stderr
, "wmswallow: Got Expose Event, count==0\n");
244 stealshape(swallowed
); /* Oclock changes its shape! That's why
245 we have to steal it *again* */
247 XMapRaised(dpy
,swallowed
);
248 /* the following Produces "focus-flicker" */
249 /* XMapSubwindows(dpy,win); */
250 /* XMapWindow(dpy,win); */
255 XSetInputFocus(dpy
, swallowed
, RevertToPointerRoot
,
260 XSetInputFocus(dpy
, PointerRoot
, RevertToPointerRoot
,
269 /* fprintf (stderr, "wmswallow: Got Some Other Event\n");
281 /* int softenwindow (Window w) { */
282 /* XSetWindowAttributes attributes; */
284 /* attributes.override_redirect=FALSE; */
285 /* attributes.event_mask=SW_EVENTS|MW_EVENTS; */
286 /* attributes.do_not_propagate_mask=0; */
288 /* XChangeWindowAttributes(dpy, w, */
289 /* CWOverrideRedirect|CWEventMask|CWDontPropagate, */
294 int sendexpose (Window w
) {
301 xexp
.send_event
=TRUE
;
306 xexp
.width
=mysizehints
.width
;
307 xexp
.height
=mysizehints
.height
;
310 retval
=XSendEvent(dpy
, w
, FALSE
, ExposureMask
, &Event
);
311 /* XFlush(dpy); */ /* ... send all queued Events */
312 /* usleep(5000L); */ /* ... take a tiny doze */
313 XSync(dpy
, FALSE
); /* I doubt if this is really better than Flushing and
318 void stealshape(Window w
) {
319 XShapeCombineShape (dpy
, iconwin
, ShapeBounding
, 0, 0, w
,
320 ShapeBounding
, ShapeSet
);
321 /* XShapeCombineShape (dpy, win, ShapeBounding, 0, 0, w, */
322 /* ShapeBounding, ShapeSet); */
324 /* XShapeCombineShape (dpy, win, ShapeClip, 0, 0, w, */
325 /* ShapeClip, ShapeSet); */
326 /* XShapeCombineShape (dpy, iconwin, ShapeClip, 0, 0, w, */
327 /* ShapeClip, ShapeSet); */
330 void nocolor(char *a
, char *b
)
332 fprintf(stderr
,"wmswallow: can't %s %s\n", a
,b
);
336 int flush_expose (Window w
)
340 while(XCheckTypedWindowEvent(dpy
,w
,Expose
,&dummy
))
347 flush_expose(swallowed
);
348 flush_expose (iconwin
);
352 Pixel
GetColor(char *name
)
355 XWindowAttributes attributes
;
356 XGetWindowAttributes(dpy
,Root
,&attributes
);
358 if (!XParseColor(dpy
,attributes
.colormap
,name
,&color
))
359 nocolor("parse",name
);
360 else if(!XAllocColor (dpy
,attributes
.colormap
,&color
))
361 nocolor("alloc",name
);
365 void usage(char *progname
){
367 "wmswallow Version %s\n"
368 " by Friedrich Delgado Friedrichs (c) 2000\n"
371 " %s [<flags>] [windowname [command [args ...]]]\n"
372 " Will swallow the first X-Window it finds with a WM_NAME or\n"
373 " WM_CLASS matching <windowname>\n"
376 " -h: prints this message and exits\n"
377 " -geometry <string>: use specified geometry for swallowed\n"
379 " -display <string>: connect to specified display\n"
380 " -shape: use the shape extension (default)\n"
381 " -noshape: don't use the shape extension\n"
382 " -focus: Window should take focus\n"
383 " -nofocus: Window shouldn't take focus(default)\n"
384 " -managed: Assume window is managed by the\n"
385 " windowmanager (default)\n"
386 " -unmanaged: Assume window is not managed by the\n"
388 " -getclick <string>: on mouseclick, execute <string>\n"
389 " instead of passing the Event to the\n"
390 " swallowed window.\n"
391 " -id [0x]<hexnumber>: swallow window with id <hexnumber>\n"
392 " The command with args will be executed, before swallowing.\n",
396 /* Parse commandline args, returns first non-switch argnumber */
397 int parseargs(int argc
, char *argv
[]){
405 for(argnum
=1; argnum
<argc
&& *argv
[argnum
]=='-'; lastarg
=++argnum
) {
406 if (!strncmp(argv
[argnum
],"-h",2) ||
407 !strncmp(argv
[argnum
],"--",2)) {
410 } else if (!strcmp(argv
[argnum
],"-geometry")||
411 !strcmp(argv
[argnum
],"-geom"))
412 geometry
=argv
[++argnum
];
413 else if (!strcmp(argv
[argnum
],"-display"))
414 display_name
= argv
[++argnum
];
415 else if (!strcmp(argv
[argnum
],"-noshape"))
417 else if (!strcmp(argv
[argnum
],"-shape"))
419 else if (!strcmp(argv
[argnum
],"-unmanaged"))
421 else if (!strcmp(argv
[argnum
],"-managed"))
423 else if (!strcmp(argv
[argnum
],"-nofocus"))
425 else if (!strcmp(argv
[argnum
],"-focus"))
427 else if (!strcmp(argv
[argnum
],"-getclick")) {
428 execstring
=(char *)malloc(strlen(argv
[++argnum
])+1+2);
429 strcpy(execstring
, argv
[argnum
]);
430 strcat(execstring
, " &");
432 } else if (!strcmp(argv
[argnum
],"-id"))
433 winid
=strtol(argv
[++argnum
], NULL
, 16);
439 return lastarg
; /*Return number of first argument, that is neither a switch nor
440 an argument to a switch */
444 /* Print a NULL-terminated list of char* onto file stream */
445 int printlist(FILE * stream
, char * string
, char **stringlist
) {
448 fprintf(stream
, string
);
449 if (stringlist
!=NULL
) {
450 while (stringlist
[i
]!=NULL
) {
451 fprintf(stream
, " §");
452 fprintf(stream
, stringlist
[i
]);
453 fprintf(stream
, "§ ");
462 /* Select SubstructureNotify on the root-window, start the command, then look
463 if we get Create Events for a matching window, set winid */
464 Window
startandfind (int argc
, char *oldargv
[], char* class) {
467 Window winreturn
=(Window
)0;
468 Window wintmp
=(Window
)0;
471 fprintf(stderr
, "Checking for name: %s\n", class);
475 XSelectInput(dpy
, Root
, FIND_EVENTS
);
476 if (!execstuff(argc
, oldargv
))
477 return FALSE
; /* execstuff failed, should not return, but
480 while (!found
&& XPending(dpy
)) {
481 /* FIXME: We hang, when the application starts, but we
482 cannot find the window! */
483 XNextEvent(dpy
, &Event
);
484 switch (Event
.type
) { /* Switch, in case we check for more than one
485 Event-Type one day */
486 /* We're waiting for the wm to reparent the window! */
489 fprintf (stderr
, "wmswallow: Got ReparentNotify Event\n");
492 wintmp
=Event
.xreparent
.window
;
493 if (checkwindow(wintmp
, class)) {
496 } else if ((winreturn
=findnamedwindowacc(class, wintmp
))) {
500 case CreateNotify
: case MapNotify
:
501 wintmp
=Event
.xcreatewindow
.window
;
503 fprintf (stderr
, "wmswallow: Got CreateNotify Event for window "
508 if (checkwindow(wintmp
, class)) {
511 } else if ((winreturn
=findnamedwindowacc(class, wintmp
))) {
521 XSelectInput(dpy
, Root
, None
);
523 fprintf (stderr
, "wmswallow: found it"
528 waitformap(winreturn
);
529 /* Ok, the window has been created, Reparented by WindowMaker and mapped */
530 /* What else can we do to make sure the window was created? */
532 sleep(1); /* doze just a sec, should be more than enough in any case */
537 /* Execute a command */
538 int execstuff (int argc
, char *oldargv
[]) {
540 int i
, success
, forked
;
542 argv
=(char **)malloc((argc
+1)*sizeof(char *));
544 for (i
=0; i
<argc
; i
++) {
551 perror("Could not fork");
556 printlist(stderr
, "Trying to execute:", argv
);
557 fprintf(stderr
, "\n");
559 success
=execvp(argv
[0],argv
);
561 printlist(stderr
, "Could not execute:", argv
);
562 fprintf(stderr
, "\n");
565 } /* Removed the sleep, since it keeps us from getting the Create Event */
570 void waitformap (Window win
) {
574 XSelectInput(dpy
, win
, READY_EVENTS
);
576 while (!found
&& XPending(dpy
)) {
577 if (XCheckMaskEvent(dpy
, READY_EVENTS
, &Event
))
578 if (Event
.type
==MapNotify
)
579 if (Event
.xmap
.window
==win
)
583 fprintf (stderr
, "wmswallow: Got MapNotify Event for window 0x%lx\n", win
);
586 while (XCheckTypedWindowEvent(dpy
, win
, MapNotify
, &Event
))
587 ; /* Flush the map Events */
588 XSelectInput(dpy
, win
, None
);
591 /* Find window which matches WM_NAME or WM_CLASS */
592 Window
findnamedwindow (char *class) {
593 /* Get All Children of the Root-Window */
595 if ((result
=(findnamedwindowacc (class, Root
)))!=0)
598 fprintf(stderr
, "Could not find %s\n", class);
603 /* Recursively walk through all the windows and their children to find the
604 first one that matches WM_NAME or WM_CLASS */
605 /* Only called by findnamedwindow() */
606 Window
findnamedwindowacc (char *class, Window window
) {
608 Window parent_return
;
609 Window
*children_return
;
610 unsigned int nchildren_return
;
615 children_return
=(Window
*)NULL
;
618 if (checkwindow(window
, class))
621 if (XQueryTree (dpy
, window
, &root_return
, &parent_return
,
622 &children_return
, &nchildren_return
)&&nchildren_return
>0) {
624 (i
=0,runner
=*children_return
;i
<nchildren_return
;
625 runner
=children_return
[++i
]) {
626 if ((result
=findnamedwindowacc(class, runner
))!=0)
627 break; /* Leave this loop */ /* It's one of the children */
629 /* end of for loop*/ /* checked all windows to no avail */
630 } /* If the if (XQueryTree...)-part wasn't executed, wmswallow could not get
631 Windows (probably no children) */
633 XFree(children_return
);
637 /* Checks if the window has WM_NAME or WM_CLASS properties fitting *class */
638 int checkwindow (Window window
, char *class) {
639 XClassHint class_hints
;
644 class_hints
.res_name
= class_hints
.res_class
= prop
.value
=(char *) NULL
;
646 /* Check WM_CLASS properties name and class */
647 if (XGetClassHint(dpy
, window
, &class_hints
)) {
648 if (!strcmp(class_hints
.res_name
, class) ||
649 !strcmp(class_hints
.res_class
, class)) {
650 found
= 1; /* It's this window! */
653 fprintf (stderr
, "wmswallow: checkwindow: 0x%lx, class: %s, name: %s\n",
654 win
, class_hints
.res_class
, class_hints
.res_name
);
658 /* Check WM_NAME property */
659 if (!found
&& XGetWMName(dpy
, window
, &prop
))
660 if (prop
.nitems
&& !strcmp(prop
.value
, class)) {
661 found
= 1; /* (-: It's really this window, and we're lucky we guessed its
662 name correctly :-) */
665 fprintf (stderr
, "wmswallow: WM_NAME: %s\n",
673 if (class_hints
.res_class
)
674 XFree(class_hints
.res_class
);
675 if (class_hints
.res_name
)
676 XFree(class_hints
.res_name
);