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>
39 #include "WindowMaker.h"
48 #include "xmodifier.h"
50 /**** global variables *****/
51 extern WPreferences wPreferences
;
54 static void putdef(char *line
, char *name
, char *value
)
57 wwarning(_("could not define value for %s for cpp"), name
);
64 static void putidef(char *line
, char *name
, int value
)
67 snprintf(tmp
, sizeof(tmp
), "%i", value
);
72 static char *username(void)
80 user
= getpwuid(getuid());
82 werror(_("could not get password entry for UID %i"), getuid());
94 char *MakeCPPArgs(char *path
)
97 char buffer
[MAXLINE
], *buf
, *line
;
101 line
= wmalloc(MAXLINE
);
104 if ((buf
= getenv("HOSTNAME")) != NULL
) {
106 wwarning(_("your machine is misconfigured. HOSTNAME is set to %s"), buf
);
108 putdef(line
, " -DHOST=", buf
);
109 } else if ((buf
= getenv("HOST")) != NULL
) {
111 wwarning(_("your machine is misconfigured. HOST is set to %s"), buf
);
113 putdef(line
, " -DHOST=", buf
);
117 putdef(line
, " -DUSER=", buf
);
118 putidef(line
, " -DUID=", getuid());
119 buf
= XDisplayName(DisplayString(dpy
));
120 putdef(line
, " -DDISPLAY=", buf
);
121 putdef(line
, " -DWM_VERSION=", VERSION
);
123 visual
= DefaultVisual(dpy
, DefaultScreen(dpy
));
124 putidef(line
, " -DVISUAL=", visual
->class);
126 putidef(line
, " -DDEPTH=", DefaultDepth(dpy
, DefaultScreen(dpy
)));
128 putidef(line
, " -DSCR_WIDTH=", WidthOfScreen(DefaultScreenOfDisplay(dpy
)));
129 putidef(line
, " -DSCR_HEIGHT=", HeightOfScreen(DefaultScreenOfDisplay(dpy
)));
131 /* put the dir where the menu is being read from to the
135 buf
= strchr(tmp
+ 1, ' ');
139 buf
= strrchr(tmp
, '/');
141 *buf
= 0; /* trunc filename */
142 putdef(line
, " -I", tmp
);
147 /* this should be done just once, but it works this way */
148 strcpy(buffer
, DEF_CONFIG_PATHS
);
149 buf
= strtok(buffer
, ":");
152 char fullpath
[MAXLINE
];
155 strcpy(fullpath
, buf
);
157 /* home is statically allocated. Don't free it! */
158 char *home
= wgethomedir();
160 strcpy(fullpath
, home
);
161 strcat(fullpath
, &(buf
[1]));
164 putdef(line
, " -I", fullpath
);
166 } while ((buf
= strtok(NULL
, ":")) != NULL
);
173 /* XFetchName Wrapper */
174 Bool
wFetchName(Display
*dpy
, Window win
, char **winname
)
176 XTextProperty text_prop
;
180 if (XGetWMName(dpy
, win
, &text_prop
)) {
181 if (text_prop
.value
&& text_prop
.nitems
> 0) {
182 if (text_prop
.encoding
== XA_STRING
) {
183 *winname
= wstrdup((char *)text_prop
.value
);
184 XFree(text_prop
.value
);
186 text_prop
.nitems
= strlen((char *)text_prop
.value
);
187 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >=
188 Success
&& num
> 0 && *list
) {
189 XFree(text_prop
.value
);
190 *winname
= wstrdup(*list
);
191 XFreeStringList(list
);
193 *winname
= wstrdup((char *)text_prop
.value
);
194 XFree(text_prop
.value
);
198 /* the title is set, but it was set to none */
199 *winname
= wstrdup("");
203 /* the hint is probably not set */
210 /* XGetIconName Wrapper */
211 Bool
wGetIconName(Display
*dpy
, Window win
, char **iconname
)
213 XTextProperty text_prop
;
217 if (XGetWMIconName(dpy
, win
, &text_prop
) != 0 && text_prop
.value
&& text_prop
.nitems
> 0) {
218 if (text_prop
.encoding
== XA_STRING
)
219 *iconname
= (char *)text_prop
.value
;
221 text_prop
.nitems
= strlen((char *)text_prop
.value
);
222 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >= Success
&& num
> 0 && *list
) {
223 XFree(text_prop
.value
);
224 *iconname
= wstrdup(*list
);
225 XFreeStringList(list
);
227 *iconname
= (char *)text_prop
.value
;
235 static void eatExpose(void)
239 /* compress all expose events into a single one */
241 if (XCheckMaskEvent(dpy
, ExposureMask
, &event
)) {
242 /* ignore other exposure events for this window */
243 while (XCheckWindowEvent(dpy
, event
.xexpose
.window
, ExposureMask
, &foo
)) ;
244 /* eat exposes for other windows */
247 event
.xexpose
.count
= 0;
248 XPutBackEvent(dpy
, &event
);
252 void SlideWindow(Window win
, int from_x
, int from_y
, int to_x
, int to_y
)
254 time_t time0
= time(NULL
);
255 float dx
, dy
, x
= from_x
, y
= from_y
, sx
, sy
, px
, py
;
256 int dx_is_bigger
= 0;
257 int slide_delay
, slide_steps
, slide_slowdown
;
259 /* animation parameters */
265 {ICON_SLIDE_DELAY_UF
, ICON_SLIDE_STEPS_UF
, ICON_SLIDE_SLOWDOWN_UF
},
266 {ICON_SLIDE_DELAY_F
, ICON_SLIDE_STEPS_F
, ICON_SLIDE_SLOWDOWN_F
},
267 {ICON_SLIDE_DELAY_M
, ICON_SLIDE_STEPS_M
, ICON_SLIDE_SLOWDOWN_M
},
268 {ICON_SLIDE_DELAY_S
, ICON_SLIDE_STEPS_S
, ICON_SLIDE_SLOWDOWN_S
},
269 {ICON_SLIDE_DELAY_US
, ICON_SLIDE_STEPS_US
, ICON_SLIDE_SLOWDOWN_US
}
272 slide_slowdown
= apars
[(int)wPreferences
.icon_slide_speed
].slowdown
;
273 slide_steps
= apars
[(int)wPreferences
.icon_slide_speed
].steps
;
274 slide_delay
= apars
[(int)wPreferences
.icon_slide_speed
].delay
;
276 dx
= (float)(to_x
- from_x
);
277 dy
= (float)(to_y
- from_y
);
278 sx
= (dx
== 0 ? 0 : fabs(dx
) / dx
);
279 sy
= (dy
== 0 ? 0 : fabs(dy
) / dy
);
281 if (fabs(dx
) > fabs(dy
)) {
286 px
= dx
/ slide_slowdown
;
287 if (px
< slide_steps
&& px
> 0)
289 else if (px
> -slide_steps
&& px
< 0)
291 py
= (sx
== 0 ? 0 : px
* dy
/ dx
);
293 py
= dy
/ slide_slowdown
;
294 if (py
< slide_steps
&& py
> 0)
296 else if (py
> -slide_steps
&& py
< 0)
298 px
= (sy
== 0 ? 0 : py
* dx
/ dy
);
301 while (x
!= to_x
|| y
!= to_y
) {
304 if ((px
< 0 && (int)x
< to_x
) || (px
> 0 && (int)x
> to_x
))
306 if ((py
< 0 && (int)y
< to_y
) || (py
> 0 && (int)y
> to_y
))
310 px
= px
* (1.0 - 1 / (float)slide_slowdown
);
311 if (px
< slide_steps
&& px
> 0)
313 else if (px
> -slide_steps
&& px
< 0)
315 py
= (sx
== 0 ? 0 : px
* dy
/ dx
);
317 py
= py
* (1.0 - 1 / (float)slide_slowdown
);
318 if (py
< slide_steps
&& py
> 0)
320 else if (py
> -slide_steps
&& py
< 0)
322 px
= (sy
== 0 ? 0 : py
* dx
/ dy
);
325 XMoveWindow(dpy
, win
, (int)x
, (int)y
);
327 if (slide_delay
> 0) {
328 wusleep(slide_delay
* 1000L);
332 if (time(NULL
) - time0
> MAX_ANIMATION_TIME
)
335 XMoveWindow(dpy
, win
, to_x
, to_y
);
338 /* compress expose events */
342 char *ShrinkString(WMFont
* font
, char *string
, int width
)
351 w
= WMWidthOfString(font
, string
, p
);
352 text
= wmalloc(strlen(string
) + 8);
353 strcpy(text
, string
);
357 pos
= strchr(text
, ' ');
359 pos
= strchr(text
, ':');
364 w1
= WMWidthOfString(font
, text
, p
);
381 width
-= WMWidthOfString(font
, "...", 3);
386 while (p2
> p1
&& p1
!= t
) {
387 w
= WMWidthOfString(font
, &string
[p
- t
], t
);
390 t
= p1
+ (p2
- p1
) / 2;
391 } else if (w
< width
) {
393 t
= p1
+ (p2
- p1
) / 2;
397 strcat(text
, &string
[p
- p1
]);
402 char *FindImage(char *paths
, char *file
)
404 char *tmp
, *path
= NULL
;
406 tmp
= strrchr(file
, ':');
409 path
= wfindfile(paths
, file
);
413 path
= wfindfile(paths
, file
);
419 static void timeoutHandler(void *data
)
424 static char *getTextSelection(WScreen
* screen
, Atom selection
)
458 data
= XFetchBuffer(dpy
, &size
, buffer
);
465 unsigned long len
, bytes
;
469 static Atom clipboard
= 0;
472 clipboard
= XInternAtom(dpy
, "CLIPBOARD", False
);
474 XDeleteProperty(dpy
, screen
->info_window
, clipboard
);
476 XConvertSelection(dpy
, selection
, XA_STRING
, clipboard
, screen
->info_window
, CurrentTime
);
478 timer
= WMAddTimerHandler(1000, timeoutHandler
, &timeout
);
480 while (!XCheckTypedWindowEvent(dpy
, screen
->info_window
, SelectionNotify
, &ev
) && !timeout
) ;
483 WMDeleteTimerHandler(timer
);
485 wwarning("selection retrieval timed out");
489 /* nobody owns the selection or the current owner has
490 * nothing to do with what we need */
491 if (ev
.xselection
.property
== None
) {
495 if (XGetWindowProperty(dpy
, screen
->info_window
,
497 False
, XA_STRING
, &rtype
, &bits
, &len
,
498 &bytes
, (unsigned char **)&data
) != Success
) {
501 if (rtype
!= XA_STRING
|| bits
!= 8) {
502 wwarning("invalid data in text selection");
511 static char *getselection(WScreen
* scr
)
515 tmp
= getTextSelection(scr
, XA_PRIMARY
);
517 tmp
= getTextSelection(scr
, XA_CUT_BUFFER0
);
522 parseuserinputpart(char *line
, int *ptr
, char *endchars
)
524 int depth
= 0, begin
;
528 while(line
[*ptr
] != '\0') {
529 if(line
[*ptr
] == '(') {
531 } else if(depth
> 0 && line
[*ptr
] == ')') {
533 } else if(depth
== 0 && strchr(endchars
, line
[*ptr
]) != NULL
) {
534 value
= wmalloc(*ptr
- begin
+ 1);
535 strncpy(value
, line
+ begin
, *ptr
- begin
);
536 value
[*ptr
- begin
] = '\0';
546 getuserinput(WScreen
*scr
, char *line
, int *ptr
, Bool advanced
)
548 char *ret
= NULL
, *title
= NULL
, *prompt
= NULL
, *name
= NULL
;
551 if(line
[*ptr
] == '(')
552 title
= parseuserinputpart(line
, ptr
, ",)");
553 if(title
!= NULL
&& line
[*ptr
] == ',')
554 prompt
= parseuserinputpart(line
, ptr
, ",)");
555 if(prompt
!= NULL
&& line
[*ptr
] == ',')
556 name
= parseuserinputpart(line
, ptr
, ")");
559 rv
= wAdvancedInputDialog(scr
,
560 title
? _(title
):_("Program Arguments"),
561 prompt
? _(prompt
):_("Enter command arguments:"),
564 rv
= wInputDialog(scr
,
565 title
? _(title
):_("Program Arguments"),
566 prompt
? _(prompt
):_("Enter command arguments:"),
569 if(title
) wfree(title
);
570 if(prompt
) wfree(prompt
);
571 if(name
) wfree(name
);
573 return rv
? ret
: NULL
;
581 * state input new-state output
582 * NORMAL % OPTION <nil>
583 * NORMAL \ ESCAPE <nil>
584 * NORMAL etc. NORMAL <input>
585 * ESCAPE any NORMAL <input>
586 * OPTION s NORMAL <selection buffer>
587 * OPTION w NORMAL <selected window id>
588 * OPTION a NORMAL <input text>
589 * OPTION d NORMAL <OffiX DND selection object>
590 * OPTION W NORMAL <current workspace>
591 * OPTION etc. NORMAL %<input>
593 #define TMPBUFSIZE 64
594 char *ExpandOptions(WScreen
* scr
, char *cmdline
)
596 int ptr
, optr
, state
, len
, olen
;
598 char *selection
= NULL
;
599 char *user_input
= NULL
;
601 char *dropped_thing
= NULL
;
603 char tmpbuf
[TMPBUFSIZE
];
606 len
= strlen(cmdline
);
610 wwarning(_("out of memory during expansion of \"%s\""), cmdline
);
614 ptr
= 0; /* input line pointer */
615 optr
= 0; /* output line pointer */
620 switch (cmdline
[ptr
]) {
629 out
[optr
++] = cmdline
[ptr
];
634 switch (cmdline
[ptr
]) {
648 out
[optr
++] = cmdline
[ptr
];
654 switch (cmdline
[ptr
]) {
656 if (scr
->focused_window
&& scr
->focused_window
->flags
.focused
) {
657 snprintf(tmpbuf
, sizeof(tmpbuf
), "0x%x",
658 (unsigned int)scr
->focused_window
->client_win
);
659 slen
= strlen(tmpbuf
);
661 nout
= realloc(out
, olen
);
663 wwarning(_("out of memory during expansion of \"%%w\""));
675 snprintf(tmpbuf
, sizeof(tmpbuf
), "0x%x", (unsigned int)scr
->current_workspace
+ 1);
676 slen
= strlen(tmpbuf
);
678 nout
= realloc(out
, olen
);
680 wwarning(_("out of memory during expansion of \"%%W\""));
691 user_input
= getuserinput(scr
, cmdline
, &ptr
, cmdline
[ptr
-1] == 'A');
693 slen
= strlen(user_input
);
695 nout
= realloc(out
, olen
);
697 wwarning(_("out of memory during expansion of \"%%a\""));
701 strcat(out
, user_input
);
704 /* Not an error, but user has Canceled the dialog box.
705 * This will make the command to not be performed. */
712 if (scr
->xdestring
) {
713 dropped_thing
= wstrdup(scr
->xdestring
);
715 if (!dropped_thing
) {
716 dropped_thing
= get_dnd_selection(scr
);
718 if (!dropped_thing
) {
719 scr
->flags
.dnd_data_convertion_status
= 1;
722 slen
= strlen(dropped_thing
);
724 nout
= realloc(out
, olen
);
726 wwarning(_("out of memory during expansion of \"%%d\""));
730 strcat(out
, dropped_thing
);
737 selection
= getselection(scr
);
740 wwarning(_("selection not available"));
743 slen
= strlen(selection
);
745 nout
= realloc(out
, olen
);
747 wwarning(_("out of memory during expansion of \"%%s\""));
751 strcat(out
, selection
);
757 out
[optr
++] = cmdline
[ptr
];
775 void ParseWindowName(WMPropList
* value
, char **winstance
, char **wclass
, char *where
)
779 *winstance
= *wclass
= NULL
;
781 if (!WMIsPLString(value
)) {
782 wwarning(_("bad window name value in %s state info"), where
);
786 name
= WMGetFromPLString(value
);
787 if (!name
|| strlen(name
) == 0) {
788 wwarning(_("bad window name value in %s state info"), where
);
792 UnescapeWM_CLASS(name
, winstance
, wclass
);
796 static char *keysymToString(KeySym keysym
, unsigned int state
)
799 char *buf
= wmalloc(20);
804 kev
.send_event
= False
;
805 kev
.window
= DefaultRootWindow(dpy
);
806 kev
.root
= DefaultRootWindow(dpy
);
807 kev
.same_screen
= True
;
808 kev
.subwindow
= kev
.root
;
809 kev
.serial
= 0x12344321;
810 kev
.time
= CurrentTime
;
812 kev
.keycode
= XKeysymToKeycode(dpy
, keysym
);
813 count
= XLookupString(&kev
, buf
, 19, NULL
, NULL
);
820 char *GetShortcutString(char *text
)
829 tmp
= text
= wstrdup(text
);
832 while ((k
= strchr(text
, '+')) != NULL
) {
836 mod
= wXModifierFromKey(text
);
838 return wstrdup("bug");
843 if (strcasecmp(text
, "Meta") == 0) {
844 buffer
= wstrappend(buffer
, "M+");
845 } else if (strcasecmp(text
, "Alt") == 0) {
846 buffer
= wstrappend(buffer
, "A+");
847 } else if (strcasecmp(text
, "Shift") == 0) {
848 buffer
= wstrappend(buffer
, "Sh+");
849 } else if (strcasecmp(text
, "Mod1") == 0) {
850 buffer
= wstrappend(buffer
, "M1+");
851 } else if (strcasecmp(text
, "Mod2") == 0) {
852 buffer
= wstrappend(buffer
, "M2+");
853 } else if (strcasecmp(text
, "Mod3") == 0) {
854 buffer
= wstrappend(buffer
, "M3+");
855 } else if (strcasecmp(text
, "Mod4") == 0) {
856 buffer
= wstrappend(buffer
, "M4+");
857 } else if (strcasecmp(text
, "Mod5") == 0) {
858 buffer
= wstrappend(buffer
, "M5+");
859 } else if (strcasecmp(text
, "Control") == 0) {
862 buffer
= wstrappend(buffer
, text
);
868 buffer
= wstrappend(buffer
, "^");
870 buffer
= wstrappend(buffer
, text
);
873 /* ksym = XStringToKeysym(text);
874 tmp = keysymToString(ksym, modmask);
876 buffer = wstrappend(buffer, tmp);
883 char *EscapeWM_CLASS(char *name
, char *class)
886 char *ename
= NULL
, *eclass
= NULL
;
894 ename
= wmalloc(l
* 2 + 1);
896 for (i
= 0; i
< l
; i
++) {
897 if (name
[i
] == '\\') {
899 } else if (name
[i
] == '.') {
902 ename
[j
++] = name
[i
];
908 eclass
= wmalloc(l
* 2 + 1);
910 for (i
= 0; i
< l
; i
++) {
911 if (class[i
] == '\\') {
913 } else if (class[i
] == '.') {
916 eclass
[j
++] = class[i
];
921 if (ename
&& eclass
) {
922 int len
= strlen(ename
) + strlen(eclass
) + 4;
924 snprintf(ret
, len
, "%s.%s", ename
, eclass
);
928 ret
= wstrdup(ename
);
931 ret
= wstrdup(eclass
);
938 void UnescapeWM_CLASS(char *str
, char **name
, char **class)
948 /* separate string in 2 parts */
950 for (i
= 0; i
< j
; i
++) {
951 if (str
[i
] == '\\') {
954 } else if (str
[i
] == '.') {
960 /* unescape strings */
961 for (i
= 0, k
= 0; i
< dot
; i
++) {
962 if (str
[i
] == '\\') {
965 (*name
)[k
++] = str
[i
];
970 for (i
= dot
+ 1, k
= 0; i
< j
; i
++) {
971 if (str
[i
] == '\\') {
974 (*class)[k
++] = str
[i
];
989 void SendHelperMessage(WScreen
* scr
, char type
, int workspace
, char *msg
)
996 if (!scr
->flags
.backimage_helper_launched
) {
1000 len
= (msg
? strlen(msg
) : 0) + (workspace
>= 0 ? 4 : 0) + 1;
1001 buffer
= wmalloc(len
+ 5);
1002 snprintf(buf
, sizeof(buf
), "%4i", len
);
1003 memcpy(buffer
, buf
, 4);
1006 if (workspace
>= 0) {
1007 snprintf(buf
, sizeof(buf
), "%4i", workspace
);
1008 memcpy(&buffer
[i
], buf
, 4);
1013 strcpy(&buffer
[i
], msg
);
1015 if (write(scr
->helper_fd
, buffer
, len
+ 4) < 0) {
1016 werror(_("could not send message to background image helper"));
1021 Bool
UpdateDomainFile(WDDomain
* domain
)
1024 char path
[PATH_MAX
];
1025 WMPropList
*shared_dict
, *dict
;
1026 Bool result
, freeDict
= False
;
1028 dict
= domain
->dictionary
;
1029 if (WMIsPLDictionary(domain
->dictionary
)) {
1030 /* retrieve global system dictionary */
1031 snprintf(path
, sizeof(path
), "%s/WindowMaker/%s", SYSCONFDIR
, domain
->domain_name
);
1032 if (stat(path
, &stbuf
) >= 0) {
1033 shared_dict
= WMReadPropListFromFile(path
);
1035 if (WMIsPLDictionary(shared_dict
)) {
1037 dict
= WMDeepCopyPropList(domain
->dictionary
);
1038 WMSubtractPLDictionaries(dict
, shared_dict
, True
);
1040 WMReleasePropList(shared_dict
);
1045 result
= WMWritePropListToFile(dict
, domain
->path
);
1048 WMReleasePropList(dict
);
1054 char *StrConcatDot(char *a
, char *b
)
1064 len
= strlen(a
) + strlen(b
) + 4;
1067 snprintf(str
, len
, "%s.%s", a
, b
);
1072 static char *getCommandForWindow(Window win
, int elements
)
1074 char **argv
, *command
= NULL
;
1077 if (XGetCommand(dpy
, win
, &argv
, &argc
)) {
1078 if (argc
> 0 && argv
!= NULL
) {
1081 command
= wtokenjoin(argv
, WMIN(argc
, elements
));
1082 if (command
[0] == 0) {
1088 XFreeStringList(argv
);
1095 /* Free result when done */
1096 char *GetCommandForWindow(Window win
)
1098 return getCommandForWindow(win
, 0);
1101 /* Free result when done */
1102 char *GetProgramNameForWindow(Window win
)
1104 return getCommandForWindow(win
, 1);