2 * WindowMaker window manager
4 * Copyright (c) 1997, 1998 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 "WindowMaker.h"
46 #include "xmodifier.h"
50 /**** global variables *****/
52 extern char *DisplayName
;
54 extern WPreferences wPreferences
;
56 extern Time LastTimestamp
;
61 extern Atom _XA_DND_SELECTION
;
67 putdef(char *line
, char *name
, char *value
)
70 wwarning(_("could not define value for %s for cpp"), name
);
80 putidef(char *line
, char *name
, int value
)
83 sprintf(tmp
, "%i", value
);
98 user
= getpwuid(getuid());
100 wsyserror(_("could not get password entry for UID %i"), getuid());
103 if (!user
->pw_name
) {
106 return user
->pw_name
;
113 MakeCPPArgs(char *path
)
116 char buffer
[MAXLINE
], *buf
, *line
;
119 line
= wmalloc(MAXLINE
);
122 if ((buf
=getenv("HOSTNAME"))!=NULL
) {
124 wwarning(_("your machine is misconfigured. HOSTNAME is set to %s"),
127 putdef(line
, " -DHOST=", buf
);
128 } else if ((buf
=getenv("HOST"))!=NULL
) {
130 wwarning(_("your machine is misconfigured. HOST is set to %s"),
133 putdef(line
, " -DHOST=", buf
);
137 putdef(line
, " -DUSER=", buf
);
138 putidef(line
, " -DUID=", getuid());
139 buf
= XDisplayName(DisplayString(dpy
));
140 putdef(line
, " -DDISPLAY=", buf
);
141 putdef(line
, " -DWM_VERSION=", VERSION
);
143 visual
= DefaultVisual(dpy
, DefaultScreen(dpy
));
144 putidef(line
, " -DVISUAL=", visual
->class);
146 putidef(line
, " -DDEPTH=", DefaultDepth(dpy
, DefaultScreen(dpy
)));
148 putidef(line
, " -DSCR_WIDTH=", WidthOfScreen(DefaultScreenOfDisplay(dpy
)));
149 putidef(line
, " -DSCR_HEIGHT=",
150 HeightOfScreen(DefaultScreenOfDisplay(dpy
)));
153 strcpy(buffer
, path
);
154 buf
= strrchr(buffer
, '/');
155 if (buf
) *buf
= 0; /* trunc filename */
156 putdef(line
, " -I", buffer
);
161 /* this should be done just once, but it works this way */
162 strcpy(buffer
, DEF_CONFIG_PATHS
);
163 buf
= strtok(buffer
, ":");
166 char fullpath
[MAXLINE
];
169 strcpy(fullpath
, buf
);
171 char * wgethomedir();
172 /* home is statically allocated. Don't free it! */
173 char *home
= wgethomedir();
175 strcpy(fullpath
, home
);
176 strcat(fullpath
, &(buf
[1]));
179 putdef(line
, " -I", fullpath
);
181 } while ((buf
= strtok(NULL
, ":"))!=NULL
);
195 NextFocusWindow(WScreen
*scr
)
197 WWindow
*tmp
, *wwin
, *closest
, *min
;
200 if (!(wwin
= scr
->focused_window
))
207 if (wWindowCanReceiveFocus(tmp
)
208 && (!tmp
->window_flags
.skip_window_list
209 || tmp
->flags
.internal_window
)) {
210 if (min
->client_win
> tmp
->client_win
)
212 if (tmp
->client_win
> wwin
->client_win
214 || (tmp
->client_win
- wwin
->client_win
) < d
)) {
216 d
= tmp
->client_win
- wwin
->client_win
;
221 if (!closest
||closest
==wwin
)
228 PrevFocusWindow(WScreen
*scr
)
230 WWindow
*tmp
, *wwin
, *closest
, *max
;
233 if (!(wwin
= scr
->focused_window
))
240 if (wWindowCanReceiveFocus(tmp
) &&
241 (!tmp
->window_flags
.skip_window_list
242 || tmp
->flags
.internal_window
)) {
243 if (max
->client_win
< tmp
->client_win
)
245 if (tmp
->client_win
< wwin
->client_win
247 || (wwin
->client_win
- tmp
->client_win
) < d
)) {
249 d
= wwin
->client_win
- tmp
->client_win
;
254 if (!closest
||closest
==wwin
)
263 * Is win2 below win1?
266 isBelow(WWindow
*win1
, WWindow
*win2
)
271 tmp
= win1
->frame
->core
->stacking
->under
;
273 if (tmp
== win2
->frame
->core
)
275 tmp
= tmp
->stacking
->under
;
278 for (i
=win1
->frame
->window_level
-1; i
>=0; i
--) {
279 tmp
= win1
->screen_ptr
->stacking_list
[i
];
281 if (tmp
== win2
->frame
->core
)
283 tmp
= tmp
->stacking
->under
;
296 Bool
wFetchName(dpy
, win
, winname
)
301 XTextProperty text_prop
;
305 if (XGetWMName(dpy
, win
, &text_prop
)) {
306 if (text_prop
.value
&& text_prop
.nitems
> 0) {
307 if (text_prop
.encoding
== XA_STRING
) {
308 *winname
= wstrdup((char *)text_prop
.value
);
309 XFree(text_prop
.value
);
311 text_prop
.nitems
= strlen((char *)text_prop
.value
);
312 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >=
313 Success
&& num
> 0 && *list
) {
314 XFree(text_prop
.value
);
315 *winname
= wstrdup(*list
);
316 XFreeStringList(list
);
318 *winname
= wstrdup((char *)text_prop
.value
);
319 XFree(text_prop
.value
);
323 /* the title is set, but it was set to none */
324 *winname
= wstrdup("");
328 /* the hint is probably not set */
336 * XGetIconName Wrapper
340 Bool
wGetIconName(dpy
, win
, iconname
)
345 XTextProperty text_prop
;
349 if (XGetWMIconName(dpy
, win
, &text_prop
) != 0 && text_prop
.value
350 && text_prop
.nitems
> 0) {
351 if (text_prop
.encoding
== XA_STRING
)
352 *iconname
= (char *)text_prop
.value
;
354 text_prop
.nitems
= strlen((char *)text_prop
.value
);
355 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >=
356 Success
&& num
> 0 && *list
) {
357 XFree(text_prop
.value
);
358 *iconname
= wstrdup(*list
);
359 XFreeStringList(list
);
361 *iconname
= (char *)text_prop
.value
;
373 wTextWidth(XFontSet font
, char *text
, int length
)
378 XmbTextExtents(font
, text
, length
, &AIXsucks
, &rec
);
384 wTextWidth(XFontStruct
*font
, char *text
, int length
)
386 return XTextWidth(font
, text
, length
);
396 /* compress all expose events into a single one */
398 if (XCheckMaskEvent(dpy
, ExposureMask
, &event
)) {
399 /* ignore other exposure events for this window */
400 while (XCheckWindowEvent(dpy
, event
.xexpose
.window
, ExposureMask
,
402 /* eat exposes for other windows */
405 event
.xexpose
.count
= 0;
406 XPutBackEvent(dpy
, &event
);
412 SlideWindow(Window win
, int from_x
, int from_y
, int to_x
, int to_y
)
414 float dx
, dy
, x
=from_x
, y
=from_y
, sx
, sy
, px
, py
;
416 /* animation parameters */
422 {ICON_SLIDE_DELAY_UF
, ICON_SLIDE_STEPS_UF
, ICON_SLIDE_SLOWDOWN_UF
},
423 {ICON_SLIDE_DELAY_F
, ICON_SLIDE_STEPS_F
, ICON_SLIDE_SLOWDOWN_F
},
424 {ICON_SLIDE_DELAY_M
, ICON_SLIDE_STEPS_M
, ICON_SLIDE_SLOWDOWN_M
},
425 {ICON_SLIDE_DELAY_S
, ICON_SLIDE_STEPS_S
, ICON_SLIDE_SLOWDOWN_S
},
426 {ICON_SLIDE_DELAY_U
, ICON_SLIDE_STEPS_U
, ICON_SLIDE_SLOWDOWN_U
}};
430 dx
= (float)(to_x
-from_x
);
431 dy
= (float)(to_y
-from_y
);
432 sx
= (dx
== 0 ? 0 : fabs(dx
)/dx
);
433 sy
= (dy
== 0 ? 0 : fabs(dy
)/dy
);
435 if (fabs(dx
) > fabs(dy
)) {
440 px
= dx
/ apars
[wPreferences
.icon_slide_speed
].slowdown
;
441 if (px
< apars
[wPreferences
.icon_slide_speed
].steps
&& px
> 0)
442 px
= apars
[wPreferences
.icon_slide_speed
].steps
;
443 else if (px
> -apars
[wPreferences
.icon_slide_speed
].steps
&& px
< 0)
444 px
= -apars
[wPreferences
.icon_slide_speed
].steps
;
445 py
= (sx
== 0 ? 0 : px
*dy
/dx
);
447 py
= dy
/ apars
[wPreferences
.icon_slide_speed
].slowdown
;
448 if (py
< apars
[wPreferences
.icon_slide_speed
].steps
&& py
> 0)
449 py
= apars
[wPreferences
.icon_slide_speed
].steps
;
450 else if (py
> -apars
[wPreferences
.icon_slide_speed
].steps
&& py
< 0)
451 py
= -apars
[wPreferences
.icon_slide_speed
].steps
;
452 px
= (sy
== 0 ? 0 : py
*dx
/dy
);
455 while (x
!= to_x
|| y
!= to_y
) {
458 if ((px
<0 && (int)x
< to_x
) || (px
>0 && (int)x
> to_x
))
460 if ((py
<0 && (int)y
< to_y
) || (py
>0 && (int)y
> to_y
))
464 px
= px
* (1.0 - 1/(float)apars
[wPreferences
.icon_slide_speed
].slowdown
);
465 if (px
< apars
[wPreferences
.icon_slide_speed
].steps
&& px
> 0)
466 px
= apars
[wPreferences
.icon_slide_speed
].steps
;
467 else if (px
> -apars
[wPreferences
.icon_slide_speed
].steps
&& px
< 0)
468 px
= -apars
[wPreferences
.icon_slide_speed
].steps
;
469 py
= (sx
== 0 ? 0 : px
*dy
/dx
);
471 py
= py
* (1.0 - 1/(float)apars
[wPreferences
.icon_slide_speed
].slowdown
);
472 if (py
< apars
[wPreferences
.icon_slide_speed
].steps
&& py
> 0)
473 py
= apars
[wPreferences
.icon_slide_speed
].steps
;
474 else if (py
> -apars
[wPreferences
.icon_slide_speed
].steps
&& py
< 0)
475 py
= -apars
[wPreferences
.icon_slide_speed
].steps
;
476 px
= (sy
== 0 ? 0 : py
*dx
/dy
);
479 XMoveWindow(dpy
, win
, (int)x
, (int)y
);
481 if (apars
[wPreferences
.icon_slide_speed
].delay
> 0) {
482 wusleep(apars
[wPreferences
.icon_slide_speed
].delay
*1000L);
485 XMoveWindow(dpy
, win
, to_x
, to_y
);
488 /* compress expose events */
494 ShrinkString(WFont
*font
, char *string
, int width
)
505 return wstrdup(string
);
508 w
= wTextWidth(font
->font
, string
, p
);
509 text
= wmalloc(strlen(string
)+8);
510 strcpy(text
, string
);
514 pos
= strchr(text
, ' ');
516 pos
= strchr(text
, ':');
521 w1
=wTextWidth(font
->font
, text
, p
);
538 width
-= wTextWidth(font
->font
, "...", 3);
544 while (p2
>p1
&& p1
!=t
) {
545 w
= wTextWidth(font
->font
, &string
[p
-t
], t
);
549 } else if (w
<width
) {
555 strcat(text
, &string
[p
-p1
]);
562 FindImage(char **paths
, char *file
)
566 tmp
= strrchr(file
, ':');
569 path
= wfindfileinlist(paths
, file
);
573 path
= wfindfileinlist(paths
, file
);
580 FlattenStringList(char **list
, int count
)
583 char *flat_string
, *wspace
;
586 for (i
=0; i
<count
; i
++) {
587 if (list
[i
]!=NULL
&& list
[i
][0]!=0) {
588 j
+= strlen(list
[i
]);
589 if (strpbrk(list
[i
], " \t"))
594 flat_string
= malloc(j
+count
+1);
599 strcpy(flat_string
, list
[0]);
600 for (i
=1; i
<count
; i
++) {
601 if (list
[i
]!=NULL
&& list
[i
][0]!=0) {
602 strcat(flat_string
, " ");
603 wspace
= strpbrk(list
[i
], " \t");
605 strcat(flat_string
, "\"");
606 strcat(flat_string
, list
[i
]);
608 strcat(flat_string
, "\"");
618 *----------------------------------------------------------------------
620 * Divides a command line into a argv/argc pair.
621 *----------------------------------------------------------------------
636 static DFA mtable
[9][6] = {
637 {{3,1},{0,0},{4,0},{1,0},{8,0},{6,0}},
638 {{1,1},{1,1},{2,0},{3,0},{5,0},{1,1}},
639 {{1,1},{1,1},{1,1},{1,1},{5,0},{1,1}},
640 {{3,1},{5,0},{4,0},{1,0},{5,0},{6,0}},
641 {{3,1},{3,1},{3,1},{3,1},{5,0},{3,1}},
642 {{-1,-1},{0,0},{0,0},{0,0},{0,0},{0,0}}, /* final state */
643 {{6,1},{6,1},{7,0},{6,1},{5,0},{3,0}},
644 {{6,1},{6,1},{6,1},{6,1},{5,0},{6,1}},
645 {{-1,-1},{0,0},{0,0},{0,0},{0,0},{0,0}}, /* final state */
649 next_token(char *word
, char **next
)
655 t
= ret
= wmalloc(strlen(word
)+1);
669 else if (*ptr
==' ' || *ptr
=='\t')
674 if (mtable
[state
][ctype
].output
) {
678 state
= mtable
[state
][ctype
].nstate
;
680 if (mtable
[state
][0].output
<0) {
702 ParseCommand(char *command
, char ***argv
, int *argc
)
704 LinkedList
*list
= NULL
;
710 token
= next_token(line
, &line
);
712 list
= list_cons(token
, list
);
714 } while (token
!=NULL
&& line
!=NULL
);
716 count
= list_length(list
);
717 *argv
= wmalloc(sizeof(char*)*count
);
720 (*argv
)[--i
] = list
->head
;
721 list_remove_head(&list
);
734 getselection(WScreen
*scr
)
741 puts("getting selection");
743 RequestSelection(dpy
, scr
->no_focus_win
, LastTimestamp
);
744 /* timeout on 1 sec. */
745 id
= WMAddTimerHandler(1000, timeup
, &timeover
);
747 WMNextEvent(dpy
, &event
);
748 if (event
.type
== SelectionNotify
749 && event
.xany
.window
==scr
->no_focus_win
) {
750 WMDeleteTimerHandler(id
);
752 puts("selection ok");
754 return GetSelection(dpy
, scr
->no_focus_win
);
756 WMHandleEvent(&event
);
759 wwarning(_("selection timed-out"));
765 getuserinput(WScreen
*scr
, char *line
, int *ptr
)
772 if (line
[*ptr
]!='(') {
773 tmp
= _("Program Arguments");
776 while (line
[*ptr
]!=0 && line
[*ptr
]!=')') {
778 if (line
[*ptr
]!='\\') {
779 buffer
[i
++] = line
[*ptr
];
792 if (wInputDialog(scr
, tmp
, _("Enter command arguments:"), &ret
)!= WDB_OK
)
801 get_dnd_selection(WScreen
*scr
)
803 XTextProperty text_ret
;
809 result
=XGetTextProperty(dpy
, scr
->root_win
, &text_ret
, _XA_DND_SELECTION
);
811 if (result
==0 || text_ret
.value
==NULL
|| text_ret
.encoding
==None
812 || text_ret
.format
==0 || text_ret
.nitems
== 0) {
813 wwarning(_("unable to get dropped data from DND drop"));
817 XTextPropertyToStringList(&text_ret
, &list
, &count
);
819 if (!list
|| count
<1) {
820 XFree(text_ret
.value
);
821 wwarning(_("error getting dropped data from DND drop"));
825 flat_string
= FlattenStringList(list
, count
);
827 wwarning(_("out of memory while getting data from DND drop"));
830 XFreeStringList(list
);
831 XFree(text_ret
.value
);
834 #endif /* OFFIX_DND */
842 * state input new-state output
843 * NORMAL % OPTION <nil>
844 * NORMAL \ ESCAPE <nil>
845 * NORMAL etc. NORMAL <input>
846 * ESCAPE any NORMAL <input>
847 * OPTION s NORMAL <selection buffer>
848 * OPTION w NORMAL <selected window id>
849 * OPTION a NORMAL <input text>
850 * OPTION d NORMAL <OffiX DND selection object>
851 * OPTION etc. NORMAL %<input>
853 #define TMPBUFSIZE 64
855 ExpandOptions(WScreen
*scr
, char *cmdline
)
857 int ptr
, optr
, state
, len
, olen
;
859 char *selection
=NULL
;
860 char *user_input
=NULL
;
862 char *dropped_thing
=NULL
;
864 char tmpbuf
[TMPBUFSIZE
];
867 len
= strlen(cmdline
);
871 wwarning(_("out of memory during expansion of \"%s\""));
875 ptr
= 0; /* input line pointer */
876 optr
= 0; /* output line pointer */
881 switch (cmdline
[ptr
]) {
890 out
[optr
++]=cmdline
[ptr
];
895 switch (cmdline
[ptr
]) {
909 out
[optr
++]=cmdline
[ptr
];
915 switch (cmdline
[ptr
]) {
917 if (scr
->focused_window
918 && scr
->focused_window
->flags
.focused
) {
919 sprintf(tmpbuf
, "0x%x",
920 (unsigned int)scr
->focused_window
->client_win
);
921 slen
= strlen(tmpbuf
);
923 nout
= realloc(out
,olen
);
925 wwarning(_("out of memory during expansion of \"%w\""));
938 user_input
= getuserinput(scr
, cmdline
, &ptr
);
940 slen
= strlen(user_input
);
942 nout
= realloc(out
,olen
);
944 wwarning(_("out of memory during expansion of \"%a\""));
948 strcat(out
,user_input
);
955 if (!dropped_thing
) {
956 dropped_thing
= get_dnd_selection(scr
);
958 if (!dropped_thing
) {
959 scr
->flags
.dnd_data_convertion_status
= 1;
962 slen
= strlen(dropped_thing
);
964 nout
= realloc(out
,olen
);
966 wwarning(_("out of memory during expansion of \"%d\""));
970 strcat(out
,dropped_thing
);
973 #endif /* OFFIX_DND */
977 if (!XGetSelectionOwner(dpy
, XA_PRIMARY
)) {
978 wwarning(_("selection not available"));
981 selection
= getselection(scr
);
986 slen
= strlen(selection
);
988 nout
= realloc(out
,olen
);
990 wwarning(_("out of memory during expansion of \"%s\""));
994 strcat(out
,selection
);
999 out
[optr
++]=cmdline
[ptr
];
1018 /* We don't care for upper/lower case in comparing the keys; so we
1019 have to define our own comparison function here */
1021 StringCompareHook(proplist_t pl1
, proplist_t pl2
)
1025 str1
= PLGetString(pl1
);
1026 str2
= PLGetString(pl2
);
1028 if (strcasecmp(str1
, str2
)==0)
1035 /* feof doesn't seem to work on pipes */
1037 IsEof(FILE * stream
)
1039 static struct stat stinfo
;
1041 fstat(fileno(stream
), &stinfo
);
1042 return ((S_ISFIFO(stinfo
.st_dev
) && stinfo
.st_size
== 0) ||
1048 ParseWindowName(proplist_t value
, char **winstance
, char **wclass
, char *where
)
1053 *winstance
= *wclass
= NULL
;
1055 if (!PLIsString(value
)) {
1056 wwarning(_("bad window name value in %s state info"), where
);
1060 name
= PLGetString(value
);
1061 if (!name
|| strlen(name
)==0) {
1062 wwarning(_("bad window name value in %s state info"), where
);
1066 dot
= strchr(name
, '.');
1068 *wclass
= wstrdup(&name
[1]);
1071 *winstance
= wstrdup(name
);
1075 *winstance
= wstrdup(name
);
1076 *dot
= '.'; /* restore old string */
1077 *wclass
= wstrdup(&dot
[1]);
1084 keysymToString(KeySym keysym
, unsigned int state
)
1087 char *buf
= wmalloc(20);
1091 kev
.type
= KeyPress
;
1092 kev
.send_event
= False
;
1093 kev
.window
= DefaultRootWindow(dpy
);
1094 kev
.root
= DefaultRootWindow(dpy
);
1095 kev
.same_screen
= True
;
1096 kev
.subwindow
= kev
.root
;
1097 kev
.serial
= 0x12344321;
1098 kev
.time
= CurrentTime
;
1100 kev
.keycode
= XKeysymToKeycode(dpy
, keysym
);
1101 count
= XLookupString(&kev
, buf
, 19, NULL
, NULL
);
1109 GetShortcutString(char *text
)
1111 char *buffer
= NULL
;
1118 tmp
= text
= wstrdup(text
);
1121 while ((k
= strchr(text
, '+'))!=NULL
) {
1125 mod
= wXModifierFromKey(text
);
1127 return wstrdup("bug");
1132 if (strcasecmp(text
, "Meta")==0) {
1133 buffer
= wstrappend(buffer
, "M+");
1134 } else if (strcasecmp(text
, "Alt")==0) {
1135 buffer
= wstrappend(buffer
, "A+");
1136 } else if (strcasecmp(text
, "Shift")==0) {
1137 buffer
= wstrappend(buffer
, "Sh+");
1138 } else if (strcasecmp(text
, "Mod1")==0) {
1139 buffer
= wstrappend(buffer
, "M1+");
1140 } else if (strcasecmp(text
, "Mod2")==0) {
1141 buffer
= wstrappend(buffer
, "M2+");
1142 } else if (strcasecmp(text
, "Mod3")==0) {
1143 buffer
= wstrappend(buffer
, "M3+");
1144 } else if (strcasecmp(text
, "Mod4")==0) {
1145 buffer
= wstrappend(buffer
, "M4+");
1146 } else if (strcasecmp(text
, "Mod5")==0) {
1147 buffer
= wstrappend(buffer
, "M5+");
1148 } else if (strcasecmp(text
, "Control")==0) {
1151 buffer
= wstrappend(buffer
, text
);
1157 buffer
= wstrappend(buffer
, "^");
1159 buffer
= wstrappend(buffer
, text
);
1162 /* ksym = XStringToKeysym(text);
1163 tmp = keysymToString(ksym, modmask);
1165 buffer = wstrappend(buffer, tmp);