2 * Window Maker window manager
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
24 #include <X11/Xutil.h>
25 #include <X11/Xatom.h>
36 #include <WINGs/WUtil.h>
40 #include "WindowMaker.h"
50 #include "xmodifier.h"
53 /**** global variables *****/
55 extern WPreferences wPreferences
;
57 extern Time LastTimestamp
;
62 putdef(char *line
, char *name
, char *value
)
65 wwarning(_("could not define value for %s for cpp"), name
);
75 putidef(char *line
, char *name
, int value
)
78 snprintf(tmp
, sizeof(tmp
), "%i", value
);
93 user
= getpwuid(getuid());
95 wsyserror(_("could not get password entry for UID %i"), getuid());
101 return user
->pw_name
;
108 MakeCPPArgs(char *path
)
111 char buffer
[MAXLINE
], *buf
, *line
;
115 line
= wmalloc(MAXLINE
);
118 if ((buf
=getenv("HOSTNAME"))!=NULL
) {
120 wwarning(_("your machine is misconfigured. HOSTNAME is set to %s"),
123 putdef(line
, " -DHOST=", buf
);
124 } else if ((buf
=getenv("HOST"))!=NULL
) {
126 wwarning(_("your machine is misconfigured. HOST is set to %s"),
129 putdef(line
, " -DHOST=", buf
);
133 putdef(line
, " -DUSER=", buf
);
134 putidef(line
, " -DUID=", getuid());
135 buf
= XDisplayName(DisplayString(dpy
));
136 putdef(line
, " -DDISPLAY=", buf
);
137 putdef(line
, " -DWM_VERSION=", VERSION
);
139 visual
= DefaultVisual(dpy
, DefaultScreen(dpy
));
140 putidef(line
, " -DVISUAL=", visual
->class);
142 putidef(line
, " -DDEPTH=", DefaultDepth(dpy
, DefaultScreen(dpy
)));
144 putidef(line
, " -DSCR_WIDTH=", WidthOfScreen(DefaultScreenOfDisplay(dpy
)));
145 putidef(line
, " -DSCR_HEIGHT=",
146 HeightOfScreen(DefaultScreenOfDisplay(dpy
)));
148 /* put the dir where the menu is being read from to the
152 buf
= strchr(tmp
+1, ' ');
156 buf
= strrchr(tmp
, '/');
158 *buf
= 0; /* trunc filename */
159 putdef(line
, " -I", tmp
);
165 /* this should be done just once, but it works this way */
166 strcpy(buffer
, DEF_CONFIG_PATHS
);
167 buf
= strtok(buffer
, ":");
170 char fullpath
[MAXLINE
];
173 strcpy(fullpath
, buf
);
175 char * wgethomedir();
176 /* home is statically allocated. Don't free it! */
177 char *home
= wgethomedir();
179 strcpy(fullpath
, home
);
180 strcat(fullpath
, &(buf
[1]));
183 putdef(line
, " -I", fullpath
);
185 } while ((buf
= strtok(NULL
, ":"))!=NULL
);
201 * Is win2 below win1?
204 isBelow(WWindow
*win1
, WWindow
*win2
)
209 tmp
= win1
->frame
->core
->stacking
->under
;
211 if (tmp
== win2
->frame
->core
)
213 tmp
= tmp
->stacking
->under
;
216 for (i
=win1
->frame
->core
->stacking
->window_level
-1; i
>=0; i
--) {
217 tmp
= win1
->screen_ptr
->stacking_list
[i
];
219 if (tmp
== win2
->frame
->core
)
221 tmp
= tmp
->stacking
->under
;
235 wFetchName(dpy
, win
, winname
)
240 XTextProperty text_prop
;
244 if (XGetWMName(dpy
, win
, &text_prop
)) {
245 if (text_prop
.value
&& text_prop
.nitems
> 0) {
246 if (text_prop
.encoding
== XA_STRING
) {
247 *winname
= wstrdup((char *)text_prop
.value
);
248 XFree(text_prop
.value
);
250 text_prop
.nitems
= strlen((char *)text_prop
.value
);
251 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >=
252 Success
&& num
> 0 && *list
) {
253 XFree(text_prop
.value
);
254 *winname
= wstrdup(*list
);
255 XFreeStringList(list
);
257 *winname
= wstrdup((char *)text_prop
.value
);
258 XFree(text_prop
.value
);
262 /* the title is set, but it was set to none */
263 *winname
= wstrdup("");
267 /* the hint is probably not set */
275 * XGetIconName Wrapper
280 wGetIconName(dpy
, win
, iconname
)
285 XTextProperty text_prop
;
289 if (XGetWMIconName(dpy
, win
, &text_prop
) != 0 && text_prop
.value
290 && text_prop
.nitems
> 0) {
291 if (text_prop
.encoding
== XA_STRING
)
292 *iconname
= (char *)text_prop
.value
;
294 text_prop
.nitems
= strlen((char *)text_prop
.value
);
295 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >=
296 Success
&& num
> 0 && *list
) {
297 XFree(text_prop
.value
);
298 *iconname
= wstrdup(*list
);
299 XFreeStringList(list
);
301 *iconname
= (char *)text_prop
.value
;
315 /* compress all expose events into a single one */
317 if (XCheckMaskEvent(dpy
, ExposureMask
, &event
)) {
318 /* ignore other exposure events for this window */
319 while (XCheckWindowEvent(dpy
, event
.xexpose
.window
, ExposureMask
,
321 /* eat exposes for other windows */
324 event
.xexpose
.count
= 0;
325 XPutBackEvent(dpy
, &event
);
331 SlideWindow(Window win
, int from_x
, int from_y
, int to_x
, int to_y
)
333 time_t time0
= time(NULL
);
334 float dx
, dy
, x
=from_x
, y
=from_y
, sx
, sy
, px
, py
;
337 /* animation parameters */
343 {ICON_SLIDE_DELAY_UF
, ICON_SLIDE_STEPS_UF
, ICON_SLIDE_SLOWDOWN_UF
},
344 {ICON_SLIDE_DELAY_F
, ICON_SLIDE_STEPS_F
, ICON_SLIDE_SLOWDOWN_F
},
345 {ICON_SLIDE_DELAY_M
, ICON_SLIDE_STEPS_M
, ICON_SLIDE_SLOWDOWN_M
},
346 {ICON_SLIDE_DELAY_S
, ICON_SLIDE_STEPS_S
, ICON_SLIDE_SLOWDOWN_S
},
347 {ICON_SLIDE_DELAY_US
, ICON_SLIDE_STEPS_US
, ICON_SLIDE_SLOWDOWN_US
}};
351 dx
= (float)(to_x
-from_x
);
352 dy
= (float)(to_y
-from_y
);
353 sx
= (dx
== 0 ? 0 : fabs(dx
)/dx
);
354 sy
= (dy
== 0 ? 0 : fabs(dy
)/dy
);
356 if (fabs(dx
) > fabs(dy
)) {
361 px
= dx
/ apars
[(int)wPreferences
.icon_slide_speed
].slowdown
;
362 if (px
< apars
[(int)wPreferences
.icon_slide_speed
].steps
&& px
> 0)
363 px
= apars
[(int)wPreferences
.icon_slide_speed
].steps
;
364 else if (px
> -apars
[(int)wPreferences
.icon_slide_speed
].steps
&& px
< 0)
365 px
= -apars
[(int)wPreferences
.icon_slide_speed
].steps
;
366 py
= (sx
== 0 ? 0 : px
*dy
/dx
);
368 py
= dy
/ apars
[(int)wPreferences
.icon_slide_speed
].slowdown
;
369 if (py
< apars
[(int)wPreferences
.icon_slide_speed
].steps
&& py
> 0)
370 py
= apars
[(int)wPreferences
.icon_slide_speed
].steps
;
371 else if (py
> -apars
[(int)wPreferences
.icon_slide_speed
].steps
&& py
< 0)
372 py
= -apars
[(int)wPreferences
.icon_slide_speed
].steps
;
373 px
= (sy
== 0 ? 0 : py
*dx
/dy
);
376 while (x
!= to_x
|| y
!= to_y
) {
379 if ((px
<0 && (int)x
< to_x
) || (px
>0 && (int)x
> to_x
))
381 if ((py
<0 && (int)y
< to_y
) || (py
>0 && (int)y
> to_y
))
385 px
= px
* (1.0 - 1/(float)apars
[(int)wPreferences
.icon_slide_speed
].slowdown
);
386 if (px
< apars
[(int)wPreferences
.icon_slide_speed
].steps
&& px
> 0)
387 px
= apars
[(int)wPreferences
.icon_slide_speed
].steps
;
388 else if (px
> -apars
[(int)wPreferences
.icon_slide_speed
].steps
&& px
< 0)
389 px
= -apars
[(int)wPreferences
.icon_slide_speed
].steps
;
390 py
= (sx
== 0 ? 0 : px
*dy
/dx
);
392 py
= py
* (1.0 - 1/(float)apars
[(int)wPreferences
.icon_slide_speed
].slowdown
);
393 if (py
< apars
[(int)wPreferences
.icon_slide_speed
].steps
&& py
> 0)
394 py
= apars
[(int)wPreferences
.icon_slide_speed
].steps
;
395 else if (py
> -apars
[(int)wPreferences
.icon_slide_speed
].steps
&& py
< 0)
396 py
= -apars
[(int)wPreferences
.icon_slide_speed
].steps
;
397 px
= (sy
== 0 ? 0 : py
*dx
/dy
);
400 XMoveWindow(dpy
, win
, (int)x
, (int)y
);
402 if (apars
[(int)wPreferences
.icon_slide_speed
].delay
> 0) {
403 wusleep(apars
[(int)wPreferences
.icon_slide_speed
].delay
*1000L);
407 if (time(NULL
) - time0
> MAX_ANIMATION_TIME
)
410 XMoveWindow(dpy
, win
, to_x
, to_y
);
413 /* compress expose events */
419 ShrinkString(WMFont
*font
, char *string
, int width
)
428 w
= WMWidthOfString(font
, string
, p
);
429 text
= wmalloc(strlen(string
)+8);
430 strcpy(text
, string
);
434 pos
= strchr(text
, ' ');
436 pos
= strchr(text
, ':');
441 w1
= WMWidthOfString(font
, text
, p
);
458 width
-= WMWidthOfString(font
, "...", 3);
463 while (p2
>p1
&& p1
!=t
) {
464 w
= WMWidthOfString(font
, &string
[p
-t
], t
);
468 } else if (w
<width
) {
474 strcat(text
, &string
[p
-p1
]);
481 FindImage(char *paths
, char *file
)
485 tmp
= strrchr(file
, ':');
488 path
= wfindfile(paths
, file
);
492 path
= wfindfile(paths
, file
);
500 timeoutHandler(void *data
)
507 getTextSelection(WScreen
*screen
, Atom selection
)
541 data
= XFetchBuffer(dpy
, &size
, buffer
);
548 unsigned long len
, bytes
;
552 static Atom clipboard
= 0;
555 clipboard
= XInternAtom(dpy
, "CLIPBOARD", False
);
557 XDeleteProperty(dpy
, screen
->info_window
, clipboard
);
559 XConvertSelection(dpy
, selection
, XA_STRING
,
560 clipboard
, screen
->info_window
,
563 timer
= WMAddTimerHandler(1000, timeoutHandler
, &timeout
);
565 while (!XCheckTypedWindowEvent(dpy
, screen
->info_window
,
566 SelectionNotify
, &ev
) && !timeout
);
569 WMDeleteTimerHandler(timer
);
571 wwarning("selection retrieval timed out");
575 /* nobody owns the selection or the current owner has
576 * nothing to do with what we need */
577 if (ev
.xselection
.property
== None
) {
581 if (XGetWindowProperty(dpy
, screen
->info_window
,
583 False
, XA_STRING
, &rtype
, &bits
, &len
,
584 &bytes
, (unsigned char**)&data
)!=Success
) {
587 if (rtype
!=XA_STRING
|| bits
!=8) {
588 wwarning("invalid data in text selection");
598 getselection(WScreen
*scr
)
602 tmp
= getTextSelection(scr
, XA_PRIMARY
);
604 tmp
= getTextSelection(scr
, XA_CUT_BUFFER0
);
610 getuserinput(WScreen
*scr
, char *line
, int *ptr
)
618 char tbuffer
[BUFSIZE
], pbuffer
[BUFSIZE
];
621 title
= _("Program Arguments");
622 prompt
= _("Enter command arguments:");
632 for (; line
[*ptr
]!=0 && state
!=_DONE
; (*ptr
)++) {
635 if (line
[*ptr
]=='(') {
644 if (j
<= 0 && line
[*ptr
]==',') {
648 strncpy(tbuffer
, &line
[begin
], WMIN(*ptr
-begin
, BUFSIZE
));
649 tbuffer
[WMIN(*ptr
-begin
, BUFSIZE
)] = 0;
650 title
= (char*)tbuffer
;
655 } else if (j
<= 0 && line
[*ptr
]==')') {
658 strncpy(tbuffer
, &line
[begin
], WMIN(*ptr
-begin
, BUFSIZE
));
659 tbuffer
[WMIN(*ptr
-begin
, BUFSIZE
)] = 0;
660 title
= (char*)tbuffer
;
664 } else if (line
[*ptr
]=='(') {
666 } else if (line
[*ptr
]==')') {
673 if (line
[*ptr
]==')' && j
==0) {
675 if (*ptr
-begin
> 1) {
676 strncpy(pbuffer
, &line
[begin
], WMIN(*ptr
-begin
, BUFSIZE
));
677 pbuffer
[WMIN(*ptr
-begin
, BUFSIZE
)] = 0;
678 prompt
= (char*)pbuffer
;
681 } else if (line
[*ptr
]=='(')
683 else if (line
[*ptr
]==')')
694 if (!wInputDialog(scr
, title
, prompt
, &ret
))
706 * state input new-state output
707 * NORMAL % OPTION <nil>
708 * NORMAL \ ESCAPE <nil>
709 * NORMAL etc. NORMAL <input>
710 * ESCAPE any NORMAL <input>
711 * OPTION s NORMAL <selection buffer>
712 * OPTION w NORMAL <selected window id>
713 * OPTION a NORMAL <input text>
714 * OPTION d NORMAL <OffiX DND selection object>
715 * OPTION W NORMAL <current workspace>
716 * OPTION etc. NORMAL %<input>
718 #define TMPBUFSIZE 64
720 ExpandOptions(WScreen
*scr
, char *cmdline
)
722 int ptr
, optr
, state
, len
, olen
;
724 char *selection
=NULL
;
725 char *user_input
=NULL
;
727 char *dropped_thing
=NULL
;
729 char tmpbuf
[TMPBUFSIZE
];
732 len
= strlen(cmdline
);
736 wwarning(_("out of memory during expansion of \"%s\""));
740 ptr
= 0; /* input line pointer */
741 optr
= 0; /* output line pointer */
746 switch (cmdline
[ptr
]) {
755 out
[optr
++]=cmdline
[ptr
];
760 switch (cmdline
[ptr
]) {
774 out
[optr
++]=cmdline
[ptr
];
780 switch (cmdline
[ptr
]) {
782 if (scr
->focused_window
783 && scr
->focused_window
->flags
.focused
) {
784 snprintf(tmpbuf
, sizeof(tmpbuf
), "0x%x",
785 (unsigned int)scr
->focused_window
->client_win
);
786 slen
= strlen(tmpbuf
);
788 nout
= realloc(out
,olen
);
790 wwarning(_("out of memory during expansion of \"%w\""));
802 snprintf(tmpbuf
, sizeof(tmpbuf
), "0x%x",
803 (unsigned int)scr
->current_workspace
+ 1);
804 slen
= strlen(tmpbuf
);
806 nout
= realloc(out
,olen
);
808 wwarning(_("out of memory during expansion of \"%W\""));
818 user_input
= getuserinput(scr
, cmdline
, &ptr
);
820 slen
= strlen(user_input
);
822 nout
= realloc(out
,olen
);
824 wwarning(_("out of memory during expansion of \"%a\""));
828 strcat(out
,user_input
);
831 /* Not an error, but user has Canceled the dialog box.
832 * This will make the command to not be performed. */
840 dropped_thing
= wstrdup(scr
->xdestring
);
842 if (!dropped_thing
) {
843 dropped_thing
= get_dnd_selection(scr
);
845 if (!dropped_thing
) {
846 scr
->flags
.dnd_data_convertion_status
= 1;
849 slen
= strlen(dropped_thing
);
851 nout
= realloc(out
,olen
);
853 wwarning(_("out of memory during expansion of \"%d\""));
857 strcat(out
,dropped_thing
);
864 selection
= getselection(scr
);
867 wwarning(_("selection not available"));
870 slen
= strlen(selection
);
872 nout
= realloc(out
,olen
);
874 wwarning(_("out of memory during expansion of \"%s\""));
878 strcat(out
,selection
);
884 out
[optr
++]=cmdline
[ptr
];
904 ParseWindowName(WMPropList
*value
, char **winstance
, char **wclass
, char *where
)
908 *winstance
= *wclass
= NULL
;
910 if (!WMIsPLString(value
)) {
911 wwarning(_("bad window name value in %s state info"), where
);
915 name
= WMGetFromPLString(value
);
916 if (!name
|| strlen(name
)==0) {
917 wwarning(_("bad window name value in %s state info"), where
);
921 UnescapeWM_CLASS(name
, winstance
, wclass
);
927 keysymToString(KeySym keysym
, unsigned int state
)
930 char *buf
= wmalloc(20);
935 kev
.send_event
= False
;
936 kev
.window
= DefaultRootWindow(dpy
);
937 kev
.root
= DefaultRootWindow(dpy
);
938 kev
.same_screen
= True
;
939 kev
.subwindow
= kev
.root
;
940 kev
.serial
= 0x12344321;
941 kev
.time
= CurrentTime
;
943 kev
.keycode
= XKeysymToKeycode(dpy
, keysym
);
944 count
= XLookupString(&kev
, buf
, 19, NULL
, NULL
);
953 GetShortcutString(char *text
)
962 tmp
= text
= wstrdup(text
);
965 while ((k
= strchr(text
, '+'))!=NULL
) {
969 mod
= wXModifierFromKey(text
);
971 return wstrdup("bug");
976 if (strcasecmp(text
, "Meta")==0) {
977 buffer
= wstrappend(buffer
, "M+");
978 } else if (strcasecmp(text
, "Alt")==0) {
979 buffer
= wstrappend(buffer
, "A+");
980 } else if (strcasecmp(text
, "Shift")==0) {
981 buffer
= wstrappend(buffer
, "Sh+");
982 } else if (strcasecmp(text
, "Mod1")==0) {
983 buffer
= wstrappend(buffer
, "M1+");
984 } else if (strcasecmp(text
, "Mod2")==0) {
985 buffer
= wstrappend(buffer
, "M2+");
986 } else if (strcasecmp(text
, "Mod3")==0) {
987 buffer
= wstrappend(buffer
, "M3+");
988 } else if (strcasecmp(text
, "Mod4")==0) {
989 buffer
= wstrappend(buffer
, "M4+");
990 } else if (strcasecmp(text
, "Mod5")==0) {
991 buffer
= wstrappend(buffer
, "M5+");
992 } else if (strcasecmp(text
, "Control")==0) {
995 buffer
= wstrappend(buffer
, text
);
1001 buffer
= wstrappend(buffer
, "^");
1003 buffer
= wstrappend(buffer
, text
);
1006 /* ksym = XStringToKeysym(text);
1007 tmp = keysymToString(ksym, modmask);
1009 buffer = wstrappend(buffer, tmp);
1018 EscapeWM_CLASS(char *name
, char *class)
1021 char *ename
= NULL
, *eclass
= NULL
;
1024 if (!name
&& !class)
1029 ename
= wmalloc(l
*2+1);
1031 for (i
=0; i
<l
; i
++) {
1032 if (name
[i
]=='\\') {
1034 } else if (name
[i
]=='.') {
1037 ename
[j
++] = name
[i
];
1043 eclass
= wmalloc(l
*2+1);
1045 for (i
=0; i
<l
; i
++) {
1046 if (class[i
]=='\\') {
1048 } else if (class[i
]=='.') {
1051 eclass
[j
++] = class[i
];
1056 if (ename
&& eclass
) {
1057 int len
= strlen(ename
)+strlen(eclass
)+4;
1059 snprintf(ret
, len
, "%s.%s", ename
, eclass
);
1063 ret
= wstrdup(ename
);
1066 ret
= wstrdup(eclass
);
1075 UnescapeWM_CLASS(char *str
, char **name
, char **class)
1082 *class = wmalloc(j
);
1085 /* separate string in 2 parts */
1087 for (i
= 0; i
< j
; i
++) {
1091 } else if (str
[i
]=='.') {
1097 /* unescape strings */
1098 for (i
=0, k
=0; i
< dot
; i
++) {
1102 (*name
)[k
++] = str
[i
];
1107 for (i
=dot
+1, k
=0; i
<j
; i
++) {
1111 (*class)[k
++] = str
[i
];
1129 SendHelperMessage(WScreen
*scr
, char type
, int workspace
, char *msg
)
1136 if (!scr
->flags
.backimage_helper_launched
) {
1140 len
= (msg
? strlen(msg
) : 0) + (workspace
>=0 ? 4 : 0) + 1 ;
1141 buffer
= wmalloc(len
+5);
1142 snprintf(buf
, len
, "%4i", len
);
1143 memcpy(buffer
, buf
, 4);
1146 if (workspace
>= 0) {
1147 snprintf(buf
, sizeof(buf
), "%4i", workspace
);
1148 memcpy(&buffer
[i
], buf
, 4);
1153 strcpy(&buffer
[i
], msg
);
1155 if (write(scr
->helper_fd
, buffer
, len
+4) < 0) {
1156 wsyserror(_("could not send message to background image helper"));
1163 UpdateDomainFile(WDDomain
*domain
)
1166 char path
[PATH_MAX
];
1167 WMPropList
*shared_dict
, *dict
;
1168 Bool result
, freeDict
= False
;
1170 dict
= domain
->dictionary
;
1171 if (WMIsPLDictionary(domain
->dictionary
)) {
1172 /* retrieve global system dictionary */
1173 snprintf(path
, sizeof(path
), "%s/WindowMaker/%s",
1174 SYSCONFDIR
, domain
->domain_name
);
1175 if (stat(path
, &stbuf
) >= 0) {
1176 shared_dict
= WMReadPropListFromFile(path
);
1178 if (WMIsPLDictionary(shared_dict
)) {
1180 dict
= WMDeepCopyPropList(domain
->dictionary
);
1181 WMSubtractPLDictionaries(dict
, shared_dict
, True
);
1183 WMReleasePropList(shared_dict
);
1188 result
= WMWritePropListToFile(dict
, domain
->path
, True
);
1191 WMReleasePropList(dict
);
1199 StrConcatDot(char *a
, char *b
)
1209 len
= strlen(a
)+strlen(b
)+4;
1212 snprintf(str
, len
, "%s.%s", a
, b
);
1218 #define MAX_CMD_SIZE 4096
1221 GetCommandForPid(int pid
, char ***argv
, int *argc
)
1223 static char buf
[MAX_CMD_SIZE
];
1228 sprintf(buf
, "/proc/%d/cmdline", pid
);
1229 fPtr
= fopen(buf
, "r");
1231 count
= read(fileno(fPtr
), buf
, MAX_CMD_SIZE
);
1234 for (i
=0, *argc
=0; i
<count
; i
++) {
1243 *argv
= (char**) wmalloc(sizeof(char*) * (*argc
));
1245 for (i
=0, j
=1; i
<count
; i
++) {
1249 (*argv
)[j
++] = &buf
[i
+1];
1266 getCommandForWindow(Window win
, int elements
)
1268 char **argv
, *command
= NULL
;
1271 if (XGetCommand(dpy
, win
, &argv
, &argc
)) {
1272 if (argc
> 0 && argv
!= NULL
) {
1275 command
= wtokenjoin(argv
, WMIN(argc
, elements
));
1276 if (command
[0] == 0) {
1282 XFreeStringList(argv
);
1290 /* Free result when done */
1292 GetCommandForWindow(Window win
)
1294 return getCommandForWindow(win
, 0);
1298 /* Free result when done */
1300 GetProgramNameForWindow(Window win
)
1302 return getCommandForWindow(win
, 1);