Add history to some dialog boxes
[wmaker-crm.git] / src / misc.c
1 /*
2 * Window Maker window manager
3 *
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
5 *
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.
10 *
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.
15 *
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,
19 * USA.
20 */
21 #include "wconfig.h"
22
23 #include <X11/Xlib.h>
24 #include <X11/Xutil.h>
25 #include <X11/Xatom.h>
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdarg.h>
32 #include <pwd.h>
33 #include <math.h>
34 #include <time.h>
35 #include <libintl.h>
36
37 #include <WINGs/WUtil.h>
38 #include <wraster.h>
39
40 #include "WindowMaker.h"
41 #include "GNUstep.h"
42 #include "screen.h"
43 #include "wcore.h"
44 #include "window.h"
45 #include "framewin.h"
46 #include "funcs.h"
47 #include "defaults.h"
48 #include "dialog.h"
49 #include "xutil.h"
50 #include "xmodifier.h"
51
52 /**** global variables *****/
53
54 extern WPreferences wPreferences;
55
56 extern Time LastTimestamp;
57
58 #ifdef USECPP
59 static void putdef(char *line, char *name, char *value)
60 {
61 if (!value) {
62 wwarning(_("could not define value for %s for cpp"), name);
63 return;
64 }
65 strcat(line, name);
66 strcat(line, value);
67 }
68
69 static void putidef(char *line, char *name, int value)
70 {
71 char tmp[64];
72 snprintf(tmp, sizeof(tmp), "%i", value);
73 strcat(line, name);
74 strcat(line, tmp);
75 }
76
77 static char *username()
78 {
79 char *tmp;
80
81 tmp = getlogin();
82 if (!tmp) {
83 struct passwd *user;
84
85 user = getpwuid(getuid());
86 if (!user) {
87 wsyserror(_("could not get password entry for UID %i"), getuid());
88 return NULL;
89 }
90 if (!user->pw_name) {
91 return NULL;
92 } else {
93 return user->pw_name;
94 }
95 }
96 return tmp;
97 }
98
99 char *MakeCPPArgs(char *path)
100 {
101 int i;
102 char buffer[MAXLINE], *buf, *line;
103 Visual *visual;
104 char *tmp;
105
106 line = wmalloc(MAXLINE);
107 *line = 0;
108 i = 1;
109 if ((buf = getenv("HOSTNAME")) != NULL) {
110 if (buf[0] == '(') {
111 wwarning(_("your machine is misconfigured. HOSTNAME is set to %s"), buf);
112 } else
113 putdef(line, " -DHOST=", buf);
114 } else if ((buf = getenv("HOST")) != NULL) {
115 if (buf[0] == '(') {
116 wwarning(_("your machine is misconfigured. HOST is set to %s"), buf);
117 } else
118 putdef(line, " -DHOST=", buf);
119 }
120 buf = username();
121 if (buf)
122 putdef(line, " -DUSER=", buf);
123 putidef(line, " -DUID=", getuid());
124 buf = XDisplayName(DisplayString(dpy));
125 putdef(line, " -DDISPLAY=", buf);
126 putdef(line, " -DWM_VERSION=", VERSION);
127
128 visual = DefaultVisual(dpy, DefaultScreen(dpy));
129 putidef(line, " -DVISUAL=", visual->class);
130
131 putidef(line, " -DDEPTH=", DefaultDepth(dpy, DefaultScreen(dpy)));
132
133 putidef(line, " -DSCR_WIDTH=", WidthOfScreen(DefaultScreenOfDisplay(dpy)));
134 putidef(line, " -DSCR_HEIGHT=", HeightOfScreen(DefaultScreenOfDisplay(dpy)));
135
136 /* put the dir where the menu is being read from to the
137 * search path */
138 if (path) {
139 tmp = wstrdup(path);
140 buf = strchr(tmp + 1, ' ');
141 if (buf) {
142 *buf = 0;
143 }
144 buf = strrchr(tmp, '/');
145 if (buf) {
146 *buf = 0; /* trunc filename */
147 putdef(line, " -I", tmp);
148 }
149 wfree(tmp);
150 }
151
152 /* this should be done just once, but it works this way */
153 strcpy(buffer, DEF_CONFIG_PATHS);
154 buf = strtok(buffer, ":");
155
156 do {
157 char fullpath[MAXLINE];
158
159 if (buf[0] != '~') {
160 strcpy(fullpath, buf);
161 } else {
162 char *wgethomedir();
163 /* home is statically allocated. Don't free it! */
164 char *home = wgethomedir();
165
166 strcpy(fullpath, home);
167 strcat(fullpath, &(buf[1]));
168 }
169
170 putdef(line, " -I", fullpath);
171
172 } while ((buf = strtok(NULL, ":")) != NULL);
173
174 #undef arg
175 #ifdef DEBUG
176 puts("CPP ARGS");
177 puts(line);
178 #endif
179 return line;
180 }
181 #endif /* USECPP */
182
183 #if 0
184 /*
185 * Is win2 below win1?
186 */
187 static Bool isBelow(WWindow * win1, WWindow * win2)
188 {
189 int i;
190 WCoreWindow *tmp;
191
192 tmp = win1->frame->core->stacking->under;
193 while (tmp) {
194 if (tmp == win2->frame->core)
195 return True;
196 tmp = tmp->stacking->under;
197 }
198
199 for (i = win1->frame->core->stacking->window_level - 1; i >= 0; i--) {
200 tmp = win1->screen_ptr->stacking_list[i];
201 while (tmp) {
202 if (tmp == win2->frame->core)
203 return True;
204 tmp = tmp->stacking->under;
205 }
206 }
207 return True;
208 }
209 #endif
210
211 /*
212 * XFetchName Wrapper
213 *
214 */
215 Bool wFetchName(dpy, win, winname)
216 Display *dpy;
217 Window win;
218 char **winname;
219 {
220 XTextProperty text_prop;
221 char **list;
222 int num;
223
224 if (XGetWMName(dpy, win, &text_prop)) {
225 if (text_prop.value && text_prop.nitems > 0) {
226 if (text_prop.encoding == XA_STRING) {
227 *winname = wstrdup((char *)text_prop.value);
228 XFree(text_prop.value);
229 } else {
230 text_prop.nitems = strlen((char *)text_prop.value);
231 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >=
232 Success && num > 0 && *list) {
233 XFree(text_prop.value);
234 *winname = wstrdup(*list);
235 XFreeStringList(list);
236 } else {
237 *winname = wstrdup((char *)text_prop.value);
238 XFree(text_prop.value);
239 }
240 }
241 } else {
242 /* the title is set, but it was set to none */
243 *winname = wstrdup("");
244 }
245 return True;
246 } else {
247 /* the hint is probably not set */
248 *winname = NULL;
249
250 return False;
251 }
252 }
253
254 /*
255 * XGetIconName Wrapper
256 *
257 */
258
259 Bool wGetIconName(dpy, win, iconname)
260 Display *dpy;
261 Window win;
262 char **iconname;
263 {
264 XTextProperty text_prop;
265 char **list;
266 int num;
267
268 if (XGetWMIconName(dpy, win, &text_prop) != 0 && text_prop.value && text_prop.nitems > 0) {
269 if (text_prop.encoding == XA_STRING)
270 *iconname = (char *)text_prop.value;
271 else {
272 text_prop.nitems = strlen((char *)text_prop.value);
273 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success && num > 0 && *list) {
274 XFree(text_prop.value);
275 *iconname = wstrdup(*list);
276 XFreeStringList(list);
277 } else
278 *iconname = (char *)text_prop.value;
279 }
280 return True;
281 }
282 *iconname = NULL;
283 return False;
284 }
285
286 static void eatExpose()
287 {
288 XEvent event, foo;
289
290 /* compress all expose events into a single one */
291
292 if (XCheckMaskEvent(dpy, ExposureMask, &event)) {
293 /* ignore other exposure events for this window */
294 while (XCheckWindowEvent(dpy, event.xexpose.window, ExposureMask, &foo)) ;
295 /* eat exposes for other windows */
296 eatExpose();
297
298 event.xexpose.count = 0;
299 XPutBackEvent(dpy, &event);
300 }
301 }
302
303 void SlideWindow(Window win, int from_x, int from_y, int to_x, int to_y)
304 {
305 time_t time0 = time(NULL);
306 float dx, dy, x = from_x, y = from_y, sx, sy, px, py;
307 int dx_is_bigger = 0;
308
309 /* animation parameters */
310 static struct {
311 int delay;
312 int steps;
313 int slowdown;
314 } apars[5] = {
315 {
316 ICON_SLIDE_DELAY_UF, ICON_SLIDE_STEPS_UF, ICON_SLIDE_SLOWDOWN_UF}, {
317 ICON_SLIDE_DELAY_F, ICON_SLIDE_STEPS_F, ICON_SLIDE_SLOWDOWN_F}, {
318 ICON_SLIDE_DELAY_M, ICON_SLIDE_STEPS_M, ICON_SLIDE_SLOWDOWN_M}, {
319 ICON_SLIDE_DELAY_S, ICON_SLIDE_STEPS_S, ICON_SLIDE_SLOWDOWN_S}, {
320 ICON_SLIDE_DELAY_US, ICON_SLIDE_STEPS_US, ICON_SLIDE_SLOWDOWN_US}};
321
322 dx = (float)(to_x - from_x);
323 dy = (float)(to_y - from_y);
324 sx = (dx == 0 ? 0 : fabs(dx) / dx);
325 sy = (dy == 0 ? 0 : fabs(dy) / dy);
326
327 if (fabs(dx) > fabs(dy)) {
328 dx_is_bigger = 1;
329 }
330
331 if (dx_is_bigger) {
332 px = dx / apars[(int)wPreferences.icon_slide_speed].slowdown;
333 if (px < apars[(int)wPreferences.icon_slide_speed].steps && px > 0)
334 px = apars[(int)wPreferences.icon_slide_speed].steps;
335 else if (px > -apars[(int)wPreferences.icon_slide_speed].steps && px < 0)
336 px = -apars[(int)wPreferences.icon_slide_speed].steps;
337 py = (sx == 0 ? 0 : px * dy / dx);
338 } else {
339 py = dy / apars[(int)wPreferences.icon_slide_speed].slowdown;
340 if (py < apars[(int)wPreferences.icon_slide_speed].steps && py > 0)
341 py = apars[(int)wPreferences.icon_slide_speed].steps;
342 else if (py > -apars[(int)wPreferences.icon_slide_speed].steps && py < 0)
343 py = -apars[(int)wPreferences.icon_slide_speed].steps;
344 px = (sy == 0 ? 0 : py * dx / dy);
345 }
346
347 while (x != to_x || y != to_y) {
348 x += px;
349 y += py;
350 if ((px < 0 && (int)x < to_x) || (px > 0 && (int)x > to_x))
351 x = (float)to_x;
352 if ((py < 0 && (int)y < to_y) || (py > 0 && (int)y > to_y))
353 y = (float)to_y;
354
355 if (dx_is_bigger) {
356 px = px * (1.0 - 1 / (float)apars[(int)wPreferences.icon_slide_speed].slowdown);
357 if (px < apars[(int)wPreferences.icon_slide_speed].steps && px > 0)
358 px = apars[(int)wPreferences.icon_slide_speed].steps;
359 else if (px > -apars[(int)wPreferences.icon_slide_speed].steps && px < 0)
360 px = -apars[(int)wPreferences.icon_slide_speed].steps;
361 py = (sx == 0 ? 0 : px * dy / dx);
362 } else {
363 py = py * (1.0 - 1 / (float)apars[(int)wPreferences.icon_slide_speed].slowdown);
364 if (py < apars[(int)wPreferences.icon_slide_speed].steps && py > 0)
365 py = apars[(int)wPreferences.icon_slide_speed].steps;
366 else if (py > -apars[(int)wPreferences.icon_slide_speed].steps && py < 0)
367 py = -apars[(int)wPreferences.icon_slide_speed].steps;
368 px = (sy == 0 ? 0 : py * dx / dy);
369 }
370
371 XMoveWindow(dpy, win, (int)x, (int)y);
372 XFlush(dpy);
373 if (apars[(int)wPreferences.icon_slide_speed].delay > 0) {
374 wusleep(apars[(int)wPreferences.icon_slide_speed].delay * 1000L);
375 } else {
376 wusleep(10);
377 }
378 if (time(NULL) - time0 > MAX_ANIMATION_TIME)
379 break;
380 }
381 XMoveWindow(dpy, win, to_x, to_y);
382
383 XSync(dpy, 0);
384 /* compress expose events */
385 eatExpose();
386 }
387
388 char *ShrinkString(WMFont * font, char *string, int width)
389 {
390 int w, w1 = 0;
391 int p;
392 char *pos;
393 char *text;
394 int p1, p2, t;
395
396 p = strlen(string);
397 w = WMWidthOfString(font, string, p);
398 text = wmalloc(strlen(string) + 8);
399 strcpy(text, string);
400 if (w <= width)
401 return text;
402
403 pos = strchr(text, ' ');
404 if (!pos)
405 pos = strchr(text, ':');
406
407 if (pos) {
408 *pos = 0;
409 p = strlen(text);
410 w1 = WMWidthOfString(font, text, p);
411 if (w1 > width) {
412 w1 = 0;
413 p = 0;
414 *pos = ' ';
415 *text = 0;
416 } else {
417 *pos = 0;
418 width -= w1;
419 p++;
420 }
421 string += p;
422 p = strlen(string);
423 } else {
424 *text = 0;
425 }
426 strcat(text, "...");
427 width -= WMWidthOfString(font, "...", 3);
428 pos = string;
429 p1 = 0;
430 p2 = p;
431 t = (p2 - p1) / 2;
432 while (p2 > p1 && p1 != t) {
433 w = WMWidthOfString(font, &string[p - t], t);
434 if (w > width) {
435 p2 = t;
436 t = p1 + (p2 - p1) / 2;
437 } else if (w < width) {
438 p1 = t;
439 t = p1 + (p2 - p1) / 2;
440 } else
441 p2 = p1 = t;
442 }
443 strcat(text, &string[p - p1]);
444
445 return text;
446 }
447
448 char *FindImage(char *paths, char *file)
449 {
450 char *tmp, *path;
451
452 tmp = strrchr(file, ':');
453 if (tmp) {
454 *tmp = 0;
455 path = wfindfile(paths, file);
456 *tmp = ':';
457 }
458 if (!tmp || !path) {
459 path = wfindfile(paths, file);
460 }
461
462 return path;
463 }
464
465 static void timeoutHandler(void *data)
466 {
467 *(int *)data = 1;
468 }
469
470 static char *getTextSelection(WScreen * screen, Atom selection)
471 {
472 int buffer = -1;
473
474 switch (selection) {
475 case XA_CUT_BUFFER0:
476 buffer = 0;
477 break;
478 case XA_CUT_BUFFER1:
479 buffer = 1;
480 break;
481 case XA_CUT_BUFFER2:
482 buffer = 2;
483 break;
484 case XA_CUT_BUFFER3:
485 buffer = 3;
486 break;
487 case XA_CUT_BUFFER4:
488 buffer = 4;
489 break;
490 case XA_CUT_BUFFER5:
491 buffer = 5;
492 break;
493 case XA_CUT_BUFFER6:
494 buffer = 6;
495 break;
496 case XA_CUT_BUFFER7:
497 buffer = 7;
498 break;
499 }
500 if (buffer >= 0) {
501 char *data;
502 int size;
503
504 data = XFetchBuffer(dpy, &size, buffer);
505
506 return data;
507 } else {
508 char *data;
509 int bits;
510 Atom rtype;
511 unsigned long len, bytes;
512 WMHandlerID timer;
513 int timeout = 0;
514 XEvent ev;
515 static Atom clipboard = 0;
516
517 if (!clipboard)
518 clipboard = XInternAtom(dpy, "CLIPBOARD", False);
519
520 XDeleteProperty(dpy, screen->info_window, clipboard);
521
522 XConvertSelection(dpy, selection, XA_STRING, clipboard, screen->info_window, CurrentTime);
523
524 timer = WMAddTimerHandler(1000, timeoutHandler, &timeout);
525
526 while (!XCheckTypedWindowEvent(dpy, screen->info_window, SelectionNotify, &ev) && !timeout) ;
527
528 if (!timeout) {
529 WMDeleteTimerHandler(timer);
530 } else {
531 wwarning("selection retrieval timed out");
532 return NULL;
533 }
534
535 /* nobody owns the selection or the current owner has
536 * nothing to do with what we need */
537 if (ev.xselection.property == None) {
538 return NULL;
539 }
540
541 if (XGetWindowProperty(dpy, screen->info_window,
542 clipboard, 0, 1024,
543 False, XA_STRING, &rtype, &bits, &len,
544 &bytes, (unsigned char **)&data) != Success) {
545 return NULL;
546 }
547 if (rtype != XA_STRING || bits != 8) {
548 wwarning("invalid data in text selection");
549 if (data)
550 XFree(data);
551 return NULL;
552 }
553 return data;
554 }
555 }
556
557 static char *getselection(WScreen * scr)
558 {
559 char *tmp;
560
561 tmp = getTextSelection(scr, XA_PRIMARY);
562 if (!tmp)
563 tmp = getTextSelection(scr, XA_CUT_BUFFER0);
564 return tmp;
565 }
566
567 static char*
568 parseuserinputpart(char *line, int *ptr, char *endchars)
569 {
570 int depth = 0, begin;
571 char *value = NULL;
572 begin = ++*ptr;
573
574 while(line[*ptr] != '\0') {
575 if(line[*ptr] == '(') {
576 ++depth;
577 } else if(depth > 0 && line[*ptr] == ')') {
578 --depth;
579 } else if(depth == 0 && strchr(endchars, line[*ptr]) != NULL) {
580 value = wmalloc(*ptr - begin + 1);
581 strncpy(value, line + begin, *ptr - begin);
582 value[*ptr - begin] = '\0';
583 break;
584 }
585 ++*ptr;
586 }
587
588 return value;
589 }
590
591 static char*
592 getuserinput(WScreen *scr, char *line, int *ptr, Bool advanced)
593 {
594 char *ret = NULL, *title = NULL, *prompt = NULL, *name = NULL;
595 int rv;
596
597 if(line[*ptr] == '(')
598 title = parseuserinputpart(line, ptr, ",)");
599 if(title != NULL && line[*ptr] == ',')
600 prompt = parseuserinputpart(line, ptr, ",)");
601 if(prompt != NULL && line[*ptr] == ',')
602 name = parseuserinputpart(line, ptr, ")");
603
604 if(advanced)
605 rv = wAdvancedInputDialog(scr,
606 title ? gettext(title):_("Program Arguments"),
607 prompt ? gettext(prompt):_("Enter command arguments:"),
608 name, &ret);
609 else
610 rv = wInputDialog(scr,
611 title ? gettext(title):_("Program Arguments"),
612 prompt ? gettext(prompt):_("Enter command arguments:"),
613 &ret);
614
615 if(title) wfree(title);
616 if(prompt) wfree(prompt);
617 if(name) wfree(name);
618
619 return rv ? ret : NULL;
620 }
621
622 #define S_NORMAL 0
623 #define S_ESCAPE 1
624 #define S_OPTION 2
625
626 /*
627 * state input new-state output
628 * NORMAL % OPTION <nil>
629 * NORMAL \ ESCAPE <nil>
630 * NORMAL etc. NORMAL <input>
631 * ESCAPE any NORMAL <input>
632 * OPTION s NORMAL <selection buffer>
633 * OPTION w NORMAL <selected window id>
634 * OPTION a NORMAL <input text>
635 * OPTION d NORMAL <OffiX DND selection object>
636 * OPTION W NORMAL <current workspace>
637 * OPTION etc. NORMAL %<input>
638 */
639 #define TMPBUFSIZE 64
640 char *ExpandOptions(WScreen * scr, char *cmdline)
641 {
642 int ptr, optr, state, len, olen;
643 char *out, *nout;
644 char *selection = NULL;
645 char *user_input = NULL;
646 #ifdef XDND
647 char *dropped_thing = NULL;
648 #endif
649 char tmpbuf[TMPBUFSIZE];
650 int slen;
651
652 len = strlen(cmdline);
653 olen = len + 1;
654 out = malloc(olen);
655 if (!out) {
656 wwarning(_("out of memory during expansion of \"%s\""));
657 return NULL;
658 }
659 *out = 0;
660 ptr = 0; /* input line pointer */
661 optr = 0; /* output line pointer */
662 state = S_NORMAL;
663 while (ptr < len) {
664 switch (state) {
665 case S_NORMAL:
666 switch (cmdline[ptr]) {
667 case '\\':
668 state = S_ESCAPE;
669 break;
670 case '%':
671 state = S_OPTION;
672 break;
673 default:
674 state = S_NORMAL;
675 out[optr++] = cmdline[ptr];
676 break;
677 }
678 break;
679 case S_ESCAPE:
680 switch (cmdline[ptr]) {
681 case 'n':
682 out[optr++] = 10;
683 break;
684
685 case 'r':
686 out[optr++] = 13;
687 break;
688
689 case 't':
690 out[optr++] = 9;
691 break;
692
693 default:
694 out[optr++] = cmdline[ptr];
695 }
696 state = S_NORMAL;
697 break;
698 case S_OPTION:
699 state = S_NORMAL;
700 switch (cmdline[ptr]) {
701 case 'w':
702 if (scr->focused_window && scr->focused_window->flags.focused) {
703 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x",
704 (unsigned int)scr->focused_window->client_win);
705 slen = strlen(tmpbuf);
706 olen += slen;
707 nout = realloc(out, olen);
708 if (!nout) {
709 wwarning(_("out of memory during expansion of \"%w\""));
710 goto error;
711 }
712 out = nout;
713 strcat(out, tmpbuf);
714 optr += slen;
715 } else {
716 out[optr++] = ' ';
717 }
718 break;
719
720 case 'W':
721 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x", (unsigned int)scr->current_workspace + 1);
722 slen = strlen(tmpbuf);
723 olen += slen;
724 nout = realloc(out, olen);
725 if (!nout) {
726 wwarning(_("out of memory during expansion of \"%W\""));
727 goto error;
728 }
729 out = nout;
730 strcat(out, tmpbuf);
731 optr += slen;
732 break;
733
734 case 'a':
735 case 'A':
736 ptr++;
737 user_input = getuserinput(scr, cmdline, &ptr, cmdline[ptr-1] == 'A');
738 if (user_input) {
739 slen = strlen(user_input);
740 olen += slen;
741 nout = realloc(out, olen);
742 if (!nout) {
743 wwarning(_("out of memory during expansion of \"%a\""));
744 goto error;
745 }
746 out = nout;
747 strcat(out, user_input);
748 optr += slen;
749 } else {
750 /* Not an error, but user has Canceled the dialog box.
751 * This will make the command to not be performed. */
752 goto error;
753 }
754 break;
755
756 #ifdef XDND
757 case 'd':
758 if (scr->xdestring) {
759 dropped_thing = wstrdup(scr->xdestring);
760 }
761 if (!dropped_thing) {
762 dropped_thing = get_dnd_selection(scr);
763 }
764 if (!dropped_thing) {
765 scr->flags.dnd_data_convertion_status = 1;
766 goto error;
767 }
768 slen = strlen(dropped_thing);
769 olen += slen;
770 nout = realloc(out, olen);
771 if (!nout) {
772 wwarning(_("out of memory during expansion of \"%d\""));
773 goto error;
774 }
775 out = nout;
776 strcat(out, dropped_thing);
777 optr += slen;
778 break;
779 #endif /* XDND */
780
781 case 's':
782 if (!selection) {
783 selection = getselection(scr);
784 }
785 if (!selection) {
786 wwarning(_("selection not available"));
787 goto error;
788 }
789 slen = strlen(selection);
790 olen += slen;
791 nout = realloc(out, olen);
792 if (!nout) {
793 wwarning(_("out of memory during expansion of \"%s\""));
794 goto error;
795 }
796 out = nout;
797 strcat(out, selection);
798 optr += slen;
799 break;
800
801 default:
802 out[optr++] = '%';
803 out[optr++] = cmdline[ptr];
804 }
805 break;
806 }
807 out[optr] = 0;
808 ptr++;
809 }
810 if (selection)
811 XFree(selection);
812 return out;
813
814 error:
815 wfree(out);
816 if (selection)
817 XFree(selection);
818 return NULL;
819 }
820
821 void ParseWindowName(WMPropList * value, char **winstance, char **wclass, char *where)
822 {
823 char *name;
824
825 *winstance = *wclass = NULL;
826
827 if (!WMIsPLString(value)) {
828 wwarning(_("bad window name value in %s state info"), where);
829 return;
830 }
831
832 name = WMGetFromPLString(value);
833 if (!name || strlen(name) == 0) {
834 wwarning(_("bad window name value in %s state info"), where);
835 return;
836 }
837
838 UnescapeWM_CLASS(name, winstance, wclass);
839 }
840
841 #if 0
842 static char *keysymToString(KeySym keysym, unsigned int state)
843 {
844 XKeyEvent kev;
845 char *buf = wmalloc(20);
846 int count;
847
848 kev.display = dpy;
849 kev.type = KeyPress;
850 kev.send_event = False;
851 kev.window = DefaultRootWindow(dpy);
852 kev.root = DefaultRootWindow(dpy);
853 kev.same_screen = True;
854 kev.subwindow = kev.root;
855 kev.serial = 0x12344321;
856 kev.time = CurrentTime;
857 kev.state = state;
858 kev.keycode = XKeysymToKeycode(dpy, keysym);
859 count = XLookupString(&kev, buf, 19, NULL, NULL);
860 buf[count] = 0;
861
862 return buf;
863 }
864 #endif
865
866 char *GetShortcutString(char *text)
867 {
868 char *buffer = NULL;
869 char *k;
870 int modmask = 0;
871 /* KeySym ksym; */
872 int control = 0;
873 char *tmp;
874
875 tmp = text = wstrdup(text);
876
877 /* get modifiers */
878 while ((k = strchr(text, '+')) != NULL) {
879 int mod;
880
881 *k = 0;
882 mod = wXModifierFromKey(text);
883 if (mod < 0) {
884 return wstrdup("bug");
885 }
886
887 modmask |= mod;
888
889 if (strcasecmp(text, "Meta") == 0) {
890 buffer = wstrappend(buffer, "M+");
891 } else if (strcasecmp(text, "Alt") == 0) {
892 buffer = wstrappend(buffer, "A+");
893 } else if (strcasecmp(text, "Shift") == 0) {
894 buffer = wstrappend(buffer, "Sh+");
895 } else if (strcasecmp(text, "Mod1") == 0) {
896 buffer = wstrappend(buffer, "M1+");
897 } else if (strcasecmp(text, "Mod2") == 0) {
898 buffer = wstrappend(buffer, "M2+");
899 } else if (strcasecmp(text, "Mod3") == 0) {
900 buffer = wstrappend(buffer, "M3+");
901 } else if (strcasecmp(text, "Mod4") == 0) {
902 buffer = wstrappend(buffer, "M4+");
903 } else if (strcasecmp(text, "Mod5") == 0) {
904 buffer = wstrappend(buffer, "M5+");
905 } else if (strcasecmp(text, "Control") == 0) {
906 control = 1;
907 } else {
908 buffer = wstrappend(buffer, text);
909 }
910 text = k + 1;
911 }
912
913 if (control) {
914 buffer = wstrappend(buffer, "^");
915 }
916 buffer = wstrappend(buffer, text);
917
918 /* get key */
919 /* ksym = XStringToKeysym(text);
920 tmp = keysymToString(ksym, modmask);
921 puts(tmp);
922 buffer = wstrappend(buffer, tmp);
923 */
924 wfree(tmp);
925
926 return buffer;
927 }
928
929 char *EscapeWM_CLASS(char *name, char *class)
930 {
931 char *ret;
932 char *ename = NULL, *eclass = NULL;
933 int i, j, l;
934
935 if (!name && !class)
936 return NULL;
937
938 if (name) {
939 l = strlen(name);
940 ename = wmalloc(l * 2 + 1);
941 j = 0;
942 for (i = 0; i < l; i++) {
943 if (name[i] == '\\') {
944 ename[j++] = '\\';
945 } else if (name[i] == '.') {
946 ename[j++] = '\\';
947 }
948 ename[j++] = name[i];
949 }
950 ename[j] = 0;
951 }
952 if (class) {
953 l = strlen(class);
954 eclass = wmalloc(l * 2 + 1);
955 j = 0;
956 for (i = 0; i < l; i++) {
957 if (class[i] == '\\') {
958 eclass[j++] = '\\';
959 } else if (class[i] == '.') {
960 eclass[j++] = '\\';
961 }
962 eclass[j++] = class[i];
963 }
964 eclass[j] = 0;
965 }
966
967 if (ename && eclass) {
968 int len = strlen(ename) + strlen(eclass) + 4;
969 ret = wmalloc(len);
970 snprintf(ret, len, "%s.%s", ename, eclass);
971 wfree(ename);
972 wfree(eclass);
973 } else if (ename) {
974 ret = wstrdup(ename);
975 wfree(ename);
976 } else {
977 ret = wstrdup(eclass);
978 wfree(eclass);
979 }
980
981 return ret;
982 }
983
984 void UnescapeWM_CLASS(char *str, char **name, char **class)
985 {
986 int i, j, k, dot;
987
988 j = strlen(str);
989 *name = wmalloc(j);
990 **name = 0;
991 *class = wmalloc(j);
992 **class = 0;
993
994 /* separate string in 2 parts */
995 dot = -1;
996 for (i = 0; i < j; i++) {
997 if (str[i] == '\\') {
998 i++;
999 continue;
1000 } else if (str[i] == '.') {
1001 dot = i;
1002 break;
1003 }
1004 }
1005
1006 /* unescape strings */
1007 for (i = 0, k = 0; i < dot; i++) {
1008 if (str[i] == '\\') {
1009 continue;
1010 } else {
1011 (*name)[k++] = str[i];
1012 }
1013 }
1014 (*name)[k] = 0;
1015
1016 for (i = dot + 1, k = 0; i < j; i++) {
1017 if (str[i] == '\\') {
1018 continue;
1019 } else {
1020 (*class)[k++] = str[i];
1021 }
1022 }
1023 (*class)[k] = 0;
1024
1025 if (!*name) {
1026 wfree(*name);
1027 *name = NULL;
1028 }
1029 if (!*class) {
1030 wfree(*class);
1031 *class = NULL;
1032 }
1033 }
1034
1035 void SendHelperMessage(WScreen * scr, char type, int workspace, char *msg)
1036 {
1037 char *buffer;
1038 int len;
1039 int i;
1040 char buf[16];
1041
1042 if (!scr->flags.backimage_helper_launched) {
1043 return;
1044 }
1045
1046 len = (msg ? strlen(msg) : 0) + (workspace >= 0 ? 4 : 0) + 1;
1047 buffer = wmalloc(len + 5);
1048 snprintf(buf, sizeof(buf), "%4i", len);
1049 memcpy(buffer, buf, 4);
1050 buffer[4] = type;
1051 i = 5;
1052 if (workspace >= 0) {
1053 snprintf(buf, sizeof(buf), "%4i", workspace);
1054 memcpy(&buffer[i], buf, 4);
1055 i += 4;
1056 buffer[i] = 0;
1057 }
1058 if (msg)
1059 strcpy(&buffer[i], msg);
1060
1061 if (write(scr->helper_fd, buffer, len + 4) < 0) {
1062 wsyserror(_("could not send message to background image helper"));
1063 }
1064 wfree(buffer);
1065 }
1066
1067 Bool UpdateDomainFile(WDDomain * domain)
1068 {
1069 struct stat stbuf;
1070 char path[PATH_MAX];
1071 WMPropList *shared_dict, *dict;
1072 Bool result, freeDict = False;
1073
1074 dict = domain->dictionary;
1075 if (WMIsPLDictionary(domain->dictionary)) {
1076 /* retrieve global system dictionary */
1077 snprintf(path, sizeof(path), "%s/WindowMaker/%s", SYSCONFDIR, domain->domain_name);
1078 if (stat(path, &stbuf) >= 0) {
1079 shared_dict = WMReadPropListFromFile(path);
1080 if (shared_dict) {
1081 if (WMIsPLDictionary(shared_dict)) {
1082 freeDict = True;
1083 dict = WMDeepCopyPropList(domain->dictionary);
1084 WMSubtractPLDictionaries(dict, shared_dict, True);
1085 }
1086 WMReleasePropList(shared_dict);
1087 }
1088 }
1089 }
1090
1091 result = WMWritePropListToFile(dict, domain->path, True);
1092
1093 if (freeDict) {
1094 WMReleasePropList(dict);
1095 }
1096
1097 return result;
1098 }
1099
1100 char *StrConcatDot(char *a, char *b)
1101 {
1102 int len;
1103 char *str;
1104
1105 if (!a)
1106 a = "";
1107 if (!b)
1108 b = "";
1109
1110 len = strlen(a) + strlen(b) + 4;
1111 str = wmalloc(len);
1112
1113 snprintf(str, len, "%s.%s", a, b);
1114
1115 return str;
1116 }
1117
1118 #define MAX_CMD_SIZE 4096
1119
1120 Bool GetCommandForPid(int pid, char ***argv, int *argc)
1121 {
1122 static char buf[MAX_CMD_SIZE];
1123 FILE *fPtr;
1124 int count, i, j;
1125 Bool ok = False;
1126
1127 sprintf(buf, "/proc/%d/cmdline", pid);
1128 fPtr = fopen(buf, "r");
1129 if (fPtr) {
1130 count = read(fileno(fPtr), buf, MAX_CMD_SIZE);
1131 if (count > 0) {
1132 buf[count - 1] = 0;
1133 for (i = 0, *argc = 0; i < count; i++) {
1134 if (buf[i] == 0) {
1135 (*argc)++;
1136 }
1137 }
1138 if ((*argc) == 0) {
1139 *argv = NULL;
1140 ok = False;
1141 } else {
1142 *argv = (char **)wmalloc(sizeof(char *) * (*argc));
1143 (*argv)[0] = buf;
1144 for (i = 0, j = 1; i < count; i++) {
1145 if (buf[i] != 0)
1146 continue;
1147 if (i < count - 1) {
1148 (*argv)[j++] = &buf[i + 1];
1149 }
1150 if (j == *argc) {
1151 break;
1152 }
1153 }
1154 ok = True;
1155 }
1156 }
1157 fclose(fPtr);
1158 }
1159
1160 return ok;
1161 }
1162
1163 static char *getCommandForWindow(Window win, int elements)
1164 {
1165 char **argv, *command = NULL;
1166 int argc;
1167
1168 if (XGetCommand(dpy, win, &argv, &argc)) {
1169 if (argc > 0 && argv != NULL) {
1170 if (elements == 0)
1171 elements = argc;
1172 command = wtokenjoin(argv, WMIN(argc, elements));
1173 if (command[0] == 0) {
1174 wfree(command);
1175 command = NULL;
1176 }
1177 }
1178 if (argv) {
1179 XFreeStringList(argv);
1180 }
1181 }
1182
1183 return command;
1184 }
1185
1186 /* Free result when done */
1187 char *GetCommandForWindow(Window win)
1188 {
1189 return getCommandForWindow(win, 0);
1190 }
1191
1192 /* Free result when done */
1193 char *GetProgramNameForWindow(Window win)
1194 {
1195 return getCommandForWindow(win, 1);
1196 }