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"
49 #include "xmodifier.h"
51 /**** global variables *****/
53 extern WPreferences wPreferences;
55 extern Time LastTimestamp;
58 static void putdef(char *line, char *name, char *value)
61 wwarning(_("could not define value for %s for cpp"), name);
68 static void putidef(char *line, char *name, int value)
71 snprintf(tmp, sizeof(tmp), "%i", value);
76 static char *username()
84 user = getpwuid(getuid());
86 wsyserror(_("could not get password entry for UID %i"), getuid());
98 char *MakeCPPArgs(char *path)
101 char buffer[MAXLINE], *buf, *line;
105 line = wmalloc(MAXLINE);
108 if ((buf = getenv("HOSTNAME")) != NULL) {
110 wwarning(_("your machine is misconfigured. HOSTNAME is set to %s"), buf);
112 putdef(line, " -DHOST=", buf);
113 } else if ((buf = getenv("HOST")) != NULL) {
115 wwarning(_("your machine is misconfigured. HOST is set to %s"), buf);
117 putdef(line, " -DHOST=", buf);
121 putdef(line, " -DUSER=", buf);
122 putidef(line, " -DUID=", getuid());
123 buf = XDisplayName(DisplayString(dpy));
124 putdef(line, " -DDISPLAY=", buf);
125 putdef(line, " -DWM_VERSION=", VERSION);
127 visual = DefaultVisual(dpy, DefaultScreen(dpy));
128 putidef(line, " -DVISUAL=", visual->class);
130 putidef(line, " -DDEPTH=", DefaultDepth(dpy, DefaultScreen(dpy)));
132 putidef(line, " -DSCR_WIDTH=", WidthOfScreen(DefaultScreenOfDisplay(dpy)));
133 putidef(line, " -DSCR_HEIGHT=", HeightOfScreen(DefaultScreenOfDisplay(dpy)));
135 /* put the dir where the menu is being read from to the
139 buf = strchr(tmp + 1, ' ');
143 buf = strrchr(tmp, '/');
145 *buf = 0; /* trunc filename */
146 putdef(line, " -I", tmp);
151 /* this should be done just once, but it works this way */
152 strcpy(buffer, DEF_CONFIG_PATHS);
153 buf = strtok(buffer, ":");
156 char fullpath[MAXLINE];
159 strcpy(fullpath, buf);
162 /* home is statically allocated. Don't free it! */
163 char *home = wgethomedir();
165 strcpy(fullpath, home);
166 strcat(fullpath, &(buf[1]));
169 putdef(line, " -I", fullpath);
171 } while ((buf = strtok(NULL, ":")) != NULL);
184 * Is win2 below win1?
186 static Bool isBelow(WWindow * win1, WWindow * win2)
191 tmp = win1->frame->core->stacking->under;
193 if (tmp == win2->frame->core)
195 tmp = tmp->stacking->under;
198 for (i = win1->frame->core->stacking->window_level - 1; i >= 0; i--) {
199 tmp = win1->screen_ptr->stacking_list[i];
201 if (tmp == win2->frame->core)
203 tmp = tmp->stacking->under;
214 Bool wFetchName(dpy, win, winname)
219 XTextProperty text_prop;
223 if (XGetWMName(dpy, win, &text_prop)) {
224 if (text_prop.value && text_prop.nitems > 0) {
225 if (text_prop.encoding == XA_STRING) {
226 *winname = wstrdup((char *)text_prop.value);
227 XFree(text_prop.value);
229 text_prop.nitems = strlen((char *)text_prop.value);
230 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >=
231 Success && num > 0 && *list) {
232 XFree(text_prop.value);
233 *winname = wstrdup(*list);
234 XFreeStringList(list);
236 *winname = wstrdup((char *)text_prop.value);
237 XFree(text_prop.value);
241 /* the title is set, but it was set to none */
242 *winname = wstrdup("");
246 /* the hint is probably not set */
254 * XGetIconName Wrapper
258 Bool wGetIconName(dpy, win, iconname)
263 XTextProperty text_prop;
267 if (XGetWMIconName(dpy, win, &text_prop) != 0 && text_prop.value && text_prop.nitems > 0) {
268 if (text_prop.encoding == XA_STRING)
269 *iconname = (char *)text_prop.value;
271 text_prop.nitems = strlen((char *)text_prop.value);
272 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success && num > 0 && *list) {
273 XFree(text_prop.value);
274 *iconname = wstrdup(*list);
275 XFreeStringList(list);
277 *iconname = (char *)text_prop.value;
285 static void eatExpose()
289 /* compress all expose events into a single one */
291 if (XCheckMaskEvent(dpy, ExposureMask, &event)) {
292 /* ignore other exposure events for this window */
293 while (XCheckWindowEvent(dpy, event.xexpose.window, ExposureMask, &foo)) ;
294 /* eat exposes for other windows */
297 event.xexpose.count = 0;
298 XPutBackEvent(dpy, &event);
302 void SlideWindow(Window win, int from_x, int from_y, int to_x, int to_y)
304 time_t time0 = time(NULL);
305 float dx, dy, x = from_x, y = from_y, sx, sy, px, py;
306 int dx_is_bigger = 0;
308 /* animation parameters */
315 ICON_SLIDE_DELAY_UF, ICON_SLIDE_STEPS_UF, ICON_SLIDE_SLOWDOWN_UF}, {
316 ICON_SLIDE_DELAY_F, ICON_SLIDE_STEPS_F, ICON_SLIDE_SLOWDOWN_F}, {
317 ICON_SLIDE_DELAY_M, ICON_SLIDE_STEPS_M, ICON_SLIDE_SLOWDOWN_M}, {
318 ICON_SLIDE_DELAY_S, ICON_SLIDE_STEPS_S, ICON_SLIDE_SLOWDOWN_S}, {
319 ICON_SLIDE_DELAY_US, ICON_SLIDE_STEPS_US, ICON_SLIDE_SLOWDOWN_US}};
321 dx = (float)(to_x - from_x);
322 dy = (float)(to_y - from_y);
323 sx = (dx == 0 ? 0 : fabs(dx) / dx);
324 sy = (dy == 0 ? 0 : fabs(dy) / dy);
326 if (fabs(dx) > fabs(dy)) {
331 px = dx / apars[(int)wPreferences.icon_slide_speed].slowdown;
332 if (px < apars[(int)wPreferences.icon_slide_speed].steps && px > 0)
333 px = apars[(int)wPreferences.icon_slide_speed].steps;
334 else if (px > -apars[(int)wPreferences.icon_slide_speed].steps && px < 0)
335 px = -apars[(int)wPreferences.icon_slide_speed].steps;
336 py = (sx == 0 ? 0 : px * dy / dx);
338 py = dy / apars[(int)wPreferences.icon_slide_speed].slowdown;
339 if (py < apars[(int)wPreferences.icon_slide_speed].steps && py > 0)
340 py = apars[(int)wPreferences.icon_slide_speed].steps;
341 else if (py > -apars[(int)wPreferences.icon_slide_speed].steps && py < 0)
342 py = -apars[(int)wPreferences.icon_slide_speed].steps;
343 px = (sy == 0 ? 0 : py * dx / dy);
346 while (x != to_x || y != to_y) {
349 if ((px < 0 && (int)x < to_x) || (px > 0 && (int)x > to_x))
351 if ((py < 0 && (int)y < to_y) || (py > 0 && (int)y > to_y))
355 px = px * (1.0 - 1 / (float)apars[(int)wPreferences.icon_slide_speed].slowdown);
356 if (px < apars[(int)wPreferences.icon_slide_speed].steps && px > 0)
357 px = apars[(int)wPreferences.icon_slide_speed].steps;
358 else if (px > -apars[(int)wPreferences.icon_slide_speed].steps && px < 0)
359 px = -apars[(int)wPreferences.icon_slide_speed].steps;
360 py = (sx == 0 ? 0 : px * dy / dx);
362 py = py * (1.0 - 1 / (float)apars[(int)wPreferences.icon_slide_speed].slowdown);
363 if (py < apars[(int)wPreferences.icon_slide_speed].steps && py > 0)
364 py = apars[(int)wPreferences.icon_slide_speed].steps;
365 else if (py > -apars[(int)wPreferences.icon_slide_speed].steps && py < 0)
366 py = -apars[(int)wPreferences.icon_slide_speed].steps;
367 px = (sy == 0 ? 0 : py * dx / dy);
370 XMoveWindow(dpy, win, (int)x, (int)y);
372 if (apars[(int)wPreferences.icon_slide_speed].delay > 0) {
373 wusleep(apars[(int)wPreferences.icon_slide_speed].delay * 1000L);
377 if (time(NULL) - time0 > MAX_ANIMATION_TIME)
380 XMoveWindow(dpy, win, to_x, to_y);
383 /* compress expose events */
387 char *ShrinkString(WMFont * font, char *string, int width)
396 w = WMWidthOfString(font, string, p);
397 text = wmalloc(strlen(string) + 8);
398 strcpy(text, string);
402 pos = strchr(text, ' ');
404 pos = strchr(text, ':');
409 w1 = WMWidthOfString(font, text, p);
426 width -= WMWidthOfString(font, "...", 3);
431 while (p2 > p1 && p1 != t) {
432 w = WMWidthOfString(font, &string[p - t], t);
435 t = p1 + (p2 - p1) / 2;
436 } else if (w < width) {
438 t = p1 + (p2 - p1) / 2;
442 strcat(text, &string[p - p1]);
447 char *FindImage(char *paths, char *file)
451 tmp = strrchr(file, ':');
454 path = wfindfile(paths, file);
458 path = wfindfile(paths, file);
464 static void timeoutHandler(void *data)
469 static char *getTextSelection(WScreen * screen, Atom selection)
503 data = XFetchBuffer(dpy, &size, buffer);
510 unsigned long len, bytes;
514 static Atom clipboard = 0;
517 clipboard = XInternAtom(dpy, "CLIPBOARD", False);
519 XDeleteProperty(dpy, screen->info_window, clipboard);
521 XConvertSelection(dpy, selection, XA_STRING, clipboard, screen->info_window, CurrentTime);
523 timer = WMAddTimerHandler(1000, timeoutHandler, &timeout);
525 while (!XCheckTypedWindowEvent(dpy, screen->info_window, SelectionNotify, &ev) && !timeout) ;
528 WMDeleteTimerHandler(timer);
530 wwarning("selection retrieval timed out");
534 /* nobody owns the selection or the current owner has
535 * nothing to do with what we need */
536 if (ev.xselection.property == None) {
540 if (XGetWindowProperty(dpy, screen->info_window,
542 False, XA_STRING, &rtype, &bits, &len,
543 &bytes, (unsigned char **)&data) != Success) {
546 if (rtype != XA_STRING || bits != 8) {
547 wwarning("invalid data in text selection");
556 static char *getselection(WScreen * scr)
560 tmp = getTextSelection(scr, XA_PRIMARY);
562 tmp = getTextSelection(scr, XA_CUT_BUFFER0);
566 static char *getuserinput(WScreen * scr, char *line, int *ptr)
574 char tbuffer[BUFSIZE], pbuffer[BUFSIZE];
576 title = _("Program Arguments");
577 prompt = _("Enter command arguments:");
587 for (; line[*ptr] != 0 && state != _DONE; (*ptr)++) {
590 if (line[*ptr] == '(') {
599 if (j <= 0 && line[*ptr] == ',') {
603 strncpy(tbuffer, &line[begin], WMIN(*ptr - begin, BUFSIZE));
604 tbuffer[WMIN(*ptr - begin, BUFSIZE)] = 0;
605 title = (char *)tbuffer;
610 } else if (j <= 0 && line[*ptr] == ')') {
613 strncpy(tbuffer, &line[begin], WMIN(*ptr - begin, BUFSIZE));
614 tbuffer[WMIN(*ptr - begin, BUFSIZE)] = 0;
615 title = (char *)tbuffer;
619 } else if (line[*ptr] == '(') {
621 } else if (line[*ptr] == ')') {
628 if (line[*ptr] == ')' && j == 0) {
630 if (*ptr - begin > 1) {
631 strncpy(pbuffer, &line[begin], WMIN(*ptr - begin, BUFSIZE));
632 pbuffer[WMIN(*ptr - begin, BUFSIZE)] = 0;
633 prompt = (char *)pbuffer;
636 } else if (line[*ptr] == '(')
638 else if (line[*ptr] == ')')
649 if (!wInputDialog(scr, title, prompt, &ret))
660 * state input new-state output
661 * NORMAL % OPTION <nil>
662 * NORMAL \ ESCAPE <nil>
663 * NORMAL etc. NORMAL <input>
664 * ESCAPE any NORMAL <input>
665 * OPTION s NORMAL <selection buffer>
666 * OPTION w NORMAL <selected window id>
667 * OPTION a NORMAL <input text>
668 * OPTION d NORMAL <OffiX DND selection object>
669 * OPTION W NORMAL <current workspace>
670 * OPTION etc. NORMAL %<input>
672 #define TMPBUFSIZE 64
673 char *ExpandOptions(WScreen * scr, char *cmdline)
675 int ptr, optr, state, len, olen;
677 char *selection = NULL;
678 char *user_input = NULL;
680 char *dropped_thing = NULL;
682 char tmpbuf[TMPBUFSIZE];
685 len = strlen(cmdline);
689 wwarning(_("out of memory during expansion of \"%s\""));
693 ptr = 0; /* input line pointer */
694 optr = 0; /* output line pointer */
699 switch (cmdline[ptr]) {
708 out[optr++] = cmdline[ptr];
713 switch (cmdline[ptr]) {
727 out[optr++] = cmdline[ptr];
733 switch (cmdline[ptr]) {
735 if (scr->focused_window && scr->focused_window->flags.focused) {
736 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x",
737 (unsigned int)scr->focused_window->client_win);
738 slen = strlen(tmpbuf);
740 nout = realloc(out, olen);
742 wwarning(_("out of memory during expansion of \"%w\""));
754 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x", (unsigned int)scr->current_workspace + 1);
755 slen = strlen(tmpbuf);
757 nout = realloc(out, olen);
759 wwarning(_("out of memory during expansion of \"%W\""));
769 user_input = getuserinput(scr, cmdline, &ptr);
771 slen = strlen(user_input);
773 nout = realloc(out, olen);
775 wwarning(_("out of memory during expansion of \"%a\""));
779 strcat(out, user_input);
782 /* Not an error, but user has Canceled the dialog box.
783 * This will make the command to not be performed. */
790 if (scr->xdestring) {
791 dropped_thing = wstrdup(scr->xdestring);
793 if (!dropped_thing) {
794 dropped_thing = get_dnd_selection(scr);
796 if (!dropped_thing) {
797 scr->flags.dnd_data_convertion_status = 1;
800 slen = strlen(dropped_thing);
802 nout = realloc(out, olen);
804 wwarning(_("out of memory during expansion of \"%d\""));
808 strcat(out, dropped_thing);
815 selection = getselection(scr);
818 wwarning(_("selection not available"));
821 slen = strlen(selection);
823 nout = realloc(out, olen);
825 wwarning(_("out of memory during expansion of \"%s\""));
829 strcat(out, selection);
835 out[optr++] = cmdline[ptr];
853 void ParseWindowName(WMPropList * value, char **winstance, char **wclass, char *where)
857 *winstance = *wclass = NULL;
859 if (!WMIsPLString(value)) {
860 wwarning(_("bad window name value in %s state info"), where);
864 name = WMGetFromPLString(value);
865 if (!name || strlen(name) == 0) {
866 wwarning(_("bad window name value in %s state info"), where);
870 UnescapeWM_CLASS(name, winstance, wclass);
874 static char *keysymToString(KeySym keysym, unsigned int state)
877 char *buf = wmalloc(20);
882 kev.send_event = False;
883 kev.window = DefaultRootWindow(dpy);
884 kev.root = DefaultRootWindow(dpy);
885 kev.same_screen = True;
886 kev.subwindow = kev.root;
887 kev.serial = 0x12344321;
888 kev.time = CurrentTime;
890 kev.keycode = XKeysymToKeycode(dpy, keysym);
891 count = XLookupString(&kev, buf, 19, NULL, NULL);
898 char *GetShortcutString(char *text)
907 tmp = text = wstrdup(text);
910 while ((k = strchr(text, '+')) != NULL) {
914 mod = wXModifierFromKey(text);
916 return wstrdup("bug");
921 if (strcasecmp(text, "Meta") == 0) {
922 buffer = wstrappend(buffer, "M+");
923 } else if (strcasecmp(text, "Alt") == 0) {
924 buffer = wstrappend(buffer, "A+");
925 } else if (strcasecmp(text, "Shift") == 0) {
926 buffer = wstrappend(buffer, "Sh+");
927 } else if (strcasecmp(text, "Mod1") == 0) {
928 buffer = wstrappend(buffer, "M1+");
929 } else if (strcasecmp(text, "Mod2") == 0) {
930 buffer = wstrappend(buffer, "M2+");
931 } else if (strcasecmp(text, "Mod3") == 0) {
932 buffer = wstrappend(buffer, "M3+");
933 } else if (strcasecmp(text, "Mod4") == 0) {
934 buffer = wstrappend(buffer, "M4+");
935 } else if (strcasecmp(text, "Mod5") == 0) {
936 buffer = wstrappend(buffer, "M5+");
937 } else if (strcasecmp(text, "Control") == 0) {
940 buffer = wstrappend(buffer, text);
946 buffer = wstrappend(buffer, "^");
948 buffer = wstrappend(buffer, text);
951 /* ksym = XStringToKeysym(text);
952 tmp = keysymToString(ksym, modmask);
954 buffer = wstrappend(buffer, tmp);
961 char *EscapeWM_CLASS(char *name, char *class)
964 char *ename = NULL, *eclass = NULL;
972 ename = wmalloc(l * 2 + 1);
974 for (i = 0; i < l; i++) {
975 if (name[i] == '\\') {
977 } else if (name[i] == '.') {
980 ename[j++] = name[i];
986 eclass = wmalloc(l * 2 + 1);
988 for (i = 0; i < l; i++) {
989 if (class[i] == '\\') {
991 } else if (class[i] == '.') {
994 eclass[j++] = class[i];
999 if (ename && eclass) {
1000 int len = strlen(ename) + strlen(eclass) + 4;
1002 snprintf(ret, len, "%s.%s", ename, eclass);
1006 ret = wstrdup(ename);
1009 ret = wstrdup(eclass);
1016 void UnescapeWM_CLASS(char *str, char **name, char **class)
1023 *class = wmalloc(j);
1026 /* separate string in 2 parts */
1028 for (i = 0; i < j; i++) {
1029 if (str[i] == '\\') {
1032 } else if (str[i] == '.') {
1038 /* unescape strings */
1039 for (i = 0, k = 0; i < dot; i++) {
1040 if (str[i] == '\\') {
1043 (*name)[k++] = str[i];
1048 for (i = dot + 1, k = 0; i < j; i++) {
1049 if (str[i] == '\\') {
1052 (*class)[k++] = str[i];
1067 void SendHelperMessage(WScreen * scr, char type, int workspace, char *msg)
1074 if (!scr->flags.backimage_helper_launched) {
1078 len = (msg ? strlen(msg) : 0) + (workspace >= 0 ? 4 : 0) + 1;
1079 buffer = wmalloc(len + 5);
1080 snprintf(buf, sizeof(buf), "%4i", len);
1081 memcpy(buffer, buf, 4);
1084 if (workspace >= 0) {
1085 snprintf(buf, sizeof(buf), "%4i", workspace);
1086 memcpy(&buffer[i], buf, 4);
1091 strcpy(&buffer[i], msg);
1093 if (write(scr->helper_fd, buffer, len + 4) < 0) {
1094 wsyserror(_("could not send message to background image helper"));
1099 Bool UpdateDomainFile(WDDomain * domain)
1102 char path[PATH_MAX];
1103 WMPropList *shared_dict, *dict;
1104 Bool result, freeDict = False;
1106 dict = domain->dictionary;
1107 if (WMIsPLDictionary(domain->dictionary)) {
1108 /* retrieve global system dictionary */
1109 snprintf(path, sizeof(path), "%s/WindowMaker/%s", SYSCONFDIR, domain->domain_name);
1110 if (stat(path, &stbuf) >= 0) {
1111 shared_dict = WMReadPropListFromFile(path);
1113 if (WMIsPLDictionary(shared_dict)) {
1115 dict = WMDeepCopyPropList(domain->dictionary);
1116 WMSubtractPLDictionaries(dict, shared_dict, True);
1118 WMReleasePropList(shared_dict);
1123 result = WMWritePropListToFile(dict, domain->path, True);
1126 WMReleasePropList(dict);
1132 char *StrConcatDot(char *a, char *b)
1142 len = strlen(a) + strlen(b) + 4;
1145 snprintf(str, len, "%s.%s", a, b);
1150 #define MAX_CMD_SIZE 4096
1152 Bool GetCommandForPid(int pid, char ***argv, int *argc)
1154 static char buf[MAX_CMD_SIZE];
1159 sprintf(buf, "/proc/%d/cmdline", pid);
1160 fPtr = fopen(buf, "r");
1162 count = read(fileno(fPtr), buf, MAX_CMD_SIZE);
1165 for (i = 0, *argc = 0; i < count; i++) {
1174 *argv = (char **)wmalloc(sizeof(char *) * (*argc));
1176 for (i = 0, j = 1; i < count; i++) {
1179 if (i < count - 1) {
1180 (*argv)[j++] = &buf[i + 1];
1195 static char *getCommandForWindow(Window win, int elements)
1197 char **argv, *command = NULL;
1200 if (XGetCommand(dpy, win, &argv, &argc)) {
1201 if (argc > 0 && argv != NULL) {
1204 command = wtokenjoin(argv, WMIN(argc, elements));
1205 if (command[0] == 0) {
1211 XFreeStringList(argv);
1218 /* Free result when done */
1219 char *GetCommandForWindow(Window win)
1221 return getCommandForWindow(win, 0);
1224 /* Free result when done */
1225 char *GetProgramNameForWindow(Window win)
1227 return getCommandForWindow(win, 1);