Info Panel: Add libXRes info if supported
[wmaker-crm.git] / src / misc.c
blob4c7fa3a5caf81a278760dbb152e78e9c6286ea57
1 /*
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 along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "wconfig.h"
22 #include <X11/Xlib.h>
23 #include <X11/Xutil.h>
24 #include <X11/Xatom.h>
25 #include <sys/stat.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <stdarg.h>
33 #include <pwd.h>
34 #include <math.h>
35 #include <time.h>
36 #include <errno.h>
38 #include <X11/XKBlib.h>
40 #include <WINGs/WUtil.h>
41 #include <wraster.h>
43 #include "window.h"
44 #include "misc.h"
45 #include "WindowMaker.h"
46 #include "GNUstep.h"
47 #include "screen.h"
48 #include "wcore.h"
49 #include "window.h"
50 #include "framewin.h"
51 #include "dialog.h"
52 #include "xutil.h"
53 #include "xmodifier.h"
54 #include "main.h"
55 #include "event.h"
56 #include "shutdown.h"
59 #define ICON_SIZE wPreferences.icon_size
61 /**** Local prototypes *****/
62 static void UnescapeWM_CLASS(const char *str, char **name, char **class);
64 /* XFetchName Wrapper */
65 Bool wFetchName(Display *dpy, Window win, char **winname)
67 XTextProperty text_prop;
68 char **list;
69 int num;
71 if (XGetWMName(dpy, win, &text_prop)) {
72 if (text_prop.value && text_prop.nitems > 0) {
73 if (text_prop.encoding == XA_STRING) {
74 *winname = wstrdup((char *)text_prop.value);
75 XFree(text_prop.value);
76 } else {
77 text_prop.nitems = strlen((char *)text_prop.value);
78 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >=
79 Success && num > 0 && *list) {
80 XFree(text_prop.value);
81 *winname = wstrdup(*list);
82 XFreeStringList(list);
83 } else {
84 *winname = wstrdup((char *)text_prop.value);
85 XFree(text_prop.value);
88 } else {
89 /* the title is set, but it was set to none */
90 *winname = wstrdup("");
92 return True;
93 } else {
94 /* the hint is probably not set */
95 *winname = NULL;
97 return False;
101 /* XGetIconName Wrapper */
102 Bool wGetIconName(Display *dpy, Window win, char **iconname)
104 XTextProperty text_prop;
105 char **list;
106 int num;
108 if (XGetWMIconName(dpy, win, &text_prop) != 0 && text_prop.value && text_prop.nitems > 0) {
109 if (text_prop.encoding == XA_STRING)
110 *iconname = (char *)text_prop.value;
111 else {
112 text_prop.nitems = strlen((char *)text_prop.value);
113 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success && num > 0 && *list) {
114 XFree(text_prop.value);
115 *iconname = wstrdup(*list);
116 XFreeStringList(list);
117 } else
118 *iconname = (char *)text_prop.value;
120 return True;
122 *iconname = NULL;
123 return False;
126 static void eatExpose(void)
128 XEvent event, foo;
130 /* compress all expose events into a single one */
132 if (XCheckMaskEvent(dpy, ExposureMask, &event)) {
133 /* ignore other exposure events for this window */
134 while (XCheckWindowEvent(dpy, event.xexpose.window, ExposureMask, &foo)) ;
135 /* eat exposes for other windows */
136 eatExpose();
138 event.xexpose.count = 0;
139 XPutBackEvent(dpy, &event);
143 void move_window(Window win, int from_x, int from_y, int to_x, int to_y)
145 #ifdef USE_ANIMATIONS
146 if (wPreferences.no_animations)
147 XMoveWindow(dpy, win, to_x, to_y);
148 else
149 slide_window(win, from_x, from_y, to_x, to_y);
150 #else
151 XMoveWindow(dpy, win, to_x, to_y);
153 /* Tell the compiler it is normal that those parameters are not used in this case */
154 (void) from_x;
155 (void) from_y;
156 #endif
159 /* wins is an array of Window, sorted from left to right, the first is
160 * going to be moved from (from_x,from_y) to (to_x,to_y) and the
161 * following windows are going to be offset by (ICON_SIZE*i,0) */
162 void slide_windows(Window wins[], int n, int from_x, int from_y, int to_x, int to_y)
164 time_t time0 = time(NULL);
165 float dx, dy, x = from_x, y = from_y, px, py;
166 Bool is_dx_nul, is_dy_nul;
167 int dx_is_bigger = 0, dx_int, dy_int;
168 int slide_delay, slide_steps, slide_slowdown;
169 int i;
171 /* animation parameters */
172 static const struct {
173 int delay;
174 int steps;
175 int slowdown;
176 } apars[5] = {
177 {ICON_SLIDE_DELAY_UF, ICON_SLIDE_STEPS_UF, ICON_SLIDE_SLOWDOWN_UF},
178 {ICON_SLIDE_DELAY_F, ICON_SLIDE_STEPS_F, ICON_SLIDE_SLOWDOWN_F},
179 {ICON_SLIDE_DELAY_M, ICON_SLIDE_STEPS_M, ICON_SLIDE_SLOWDOWN_M},
180 {ICON_SLIDE_DELAY_S, ICON_SLIDE_STEPS_S, ICON_SLIDE_SLOWDOWN_S},
181 {ICON_SLIDE_DELAY_US, ICON_SLIDE_STEPS_US, ICON_SLIDE_SLOWDOWN_US}
184 slide_slowdown = apars[(int)wPreferences.icon_slide_speed].slowdown;
185 slide_steps = apars[(int)wPreferences.icon_slide_speed].steps;
186 slide_delay = apars[(int)wPreferences.icon_slide_speed].delay;
188 dx_int = to_x - from_x;
189 dy_int = to_y - from_y;
190 is_dx_nul = (dx_int == 0);
191 is_dy_nul = (dy_int == 0);
192 dx = (float) dx_int;
193 dy = (float) dy_int;
195 if (abs(dx_int) > abs(dy_int)) {
196 dx_is_bigger = 1;
199 if (dx_is_bigger) {
200 px = dx / slide_slowdown;
201 if (px < slide_steps && px > 0)
202 px = slide_steps;
203 else if (px > -slide_steps && px < 0)
204 px = -slide_steps;
205 py = (is_dx_nul ? 0.0F : px * dy / dx);
206 } else {
207 py = dy / slide_slowdown;
208 if (py < slide_steps && py > 0)
209 py = slide_steps;
210 else if (py > -slide_steps && py < 0)
211 py = -slide_steps;
212 px = (is_dy_nul ? 0.0F : py * dx / dy);
215 while (((int)x) != to_x ||
216 ((int)y) != to_y) {
217 x += px;
218 y += py;
219 if ((px < 0 && (int)x < to_x) || (px > 0 && (int)x > to_x))
220 x = (float)to_x;
221 if ((py < 0 && (int)y < to_y) || (py > 0 && (int)y > to_y))
222 y = (float)to_y;
224 if (dx_is_bigger) {
225 px = px * (1.0F - 1 / (float)slide_slowdown);
226 if (px < slide_steps && px > 0)
227 px = slide_steps;
228 else if (px > -slide_steps && px < 0)
229 px = -slide_steps;
230 py = (is_dx_nul ? 0.0F : px * dy / dx);
231 } else {
232 py = py * (1.0F - 1 / (float)slide_slowdown);
233 if (py < slide_steps && py > 0)
234 py = slide_steps;
235 else if (py > -slide_steps && py < 0)
236 py = -slide_steps;
237 px = (is_dy_nul ? 0.0F : py * dx / dy);
240 for (i = 0; i < n; i++) {
241 XMoveWindow(dpy, wins[i], (int)x + i * ICON_SIZE, (int)y);
243 XFlush(dpy);
244 if (slide_delay > 0) {
245 wusleep(slide_delay * 1000L);
246 } else {
247 wusleep(1000L);
249 if (time(NULL) - time0 > MAX_ANIMATION_TIME)
250 break;
252 for (i = 0; i < n; i++) {
253 XMoveWindow(dpy, wins[i], to_x + i * ICON_SIZE, to_y);
256 XSync(dpy, 0);
257 /* compress expose events */
258 eatExpose();
261 char *ShrinkString(WMFont *font, const char *string, int width)
263 int w, w1 = 0;
264 int p;
265 char *pos;
266 char *text;
267 int p1, p2, t;
269 p = strlen(string);
270 w = WMWidthOfString(font, string, p);
271 text = wmalloc(strlen(string) + 8);
272 strcpy(text, string);
273 if (w <= width)
274 return text;
276 pos = strchr(text, ' ');
277 if (!pos)
278 pos = strchr(text, ':');
280 if (pos) {
281 *pos = 0;
282 p = strlen(text);
283 w1 = WMWidthOfString(font, text, p);
284 if (w1 > width) {
285 p = 0;
286 *pos = ' ';
287 *text = 0;
288 } else {
289 *pos = 0;
290 width -= w1;
291 p++;
293 string += p;
294 p = strlen(string);
295 } else {
296 *text = 0;
298 strcat(text, "...");
299 width -= WMWidthOfString(font, "...", 3);
301 p1 = 0;
302 p2 = p;
303 t = (p2 - p1) / 2;
304 while (p2 > p1 && p1 != t) {
305 w = WMWidthOfString(font, &string[p - t], t);
306 if (w > width) {
307 p2 = t;
308 t = p1 + (p2 - p1) / 2;
309 } else if (w < width) {
310 p1 = t;
311 t = p1 + (p2 - p1) / 2;
312 } else
313 p2 = p1 = t;
315 strcat(text, &string[p - p1]);
317 return text;
320 char *FindImage(const char *paths, const char *file)
322 char *tmp, *path = NULL;
324 tmp = strrchr(file, ':');
325 if (tmp) {
326 *tmp = 0;
327 path = wfindfile(paths, file);
328 *tmp = ':';
330 if (!tmp || !path)
331 path = wfindfile(paths, file);
333 return path;
336 static void timeoutHandler(void *data)
338 *(int *)data = 1;
341 static char *getTextSelection(WScreen * screen, Atom selection)
343 int buffer = -1;
345 switch (selection) {
346 case XA_CUT_BUFFER0:
347 buffer = 0;
348 break;
349 case XA_CUT_BUFFER1:
350 buffer = 1;
351 break;
352 case XA_CUT_BUFFER2:
353 buffer = 2;
354 break;
355 case XA_CUT_BUFFER3:
356 buffer = 3;
357 break;
358 case XA_CUT_BUFFER4:
359 buffer = 4;
360 break;
361 case XA_CUT_BUFFER5:
362 buffer = 5;
363 break;
364 case XA_CUT_BUFFER6:
365 buffer = 6;
366 break;
367 case XA_CUT_BUFFER7:
368 buffer = 7;
369 break;
371 if (buffer >= 0) {
372 char *data;
373 int size;
375 data = XFetchBuffer(dpy, &size, buffer);
377 return data;
378 } else {
379 char *data;
380 int bits;
381 Atom rtype;
382 unsigned long len, bytes;
383 WMHandlerID timer;
384 int timeout = 0;
385 XEvent ev;
386 static Atom clipboard = 0;
388 if (!clipboard)
389 clipboard = XInternAtom(dpy, "CLIPBOARD", False);
391 XDeleteProperty(dpy, screen->info_window, clipboard);
393 XConvertSelection(dpy, selection, XA_STRING, clipboard, screen->info_window, CurrentTime);
395 timer = WMAddTimerHandler(1000, timeoutHandler, &timeout);
397 while (!XCheckTypedWindowEvent(dpy, screen->info_window, SelectionNotify, &ev) && !timeout) ;
399 if (!timeout) {
400 WMDeleteTimerHandler(timer);
401 } else {
402 wwarning("selection retrieval timed out");
403 return NULL;
406 /* nobody owns the selection or the current owner has
407 * nothing to do with what we need */
408 if (ev.xselection.property == None) {
409 return NULL;
412 if (XGetWindowProperty(dpy, screen->info_window,
413 clipboard, 0, 1024,
414 False, XA_STRING, &rtype, &bits, &len,
415 &bytes, (unsigned char **)&data) != Success) {
416 return NULL;
418 if (rtype != XA_STRING || bits != 8) {
419 wwarning("invalid data in text selection");
420 if (data)
421 XFree(data);
422 return NULL;
424 return data;
428 static char *getselection(WScreen * scr)
430 char *tmp;
432 tmp = getTextSelection(scr, XA_PRIMARY);
433 if (!tmp)
434 tmp = getTextSelection(scr, XA_CUT_BUFFER0);
435 return tmp;
438 static char*
439 parseuserinputpart(const char *line, int *ptr, const char *endchars)
441 int depth = 0, begin;
442 char *value = NULL;
443 begin = ++*ptr;
445 while(line[*ptr] != '\0') {
446 if(line[*ptr] == '(') {
447 ++depth;
448 } else if(depth > 0 && line[*ptr] == ')') {
449 --depth;
450 } else if(depth == 0 && strchr(endchars, line[*ptr]) != NULL) {
451 value = wmalloc(*ptr - begin + 1);
452 strncpy(value, line + begin, *ptr - begin);
453 value[*ptr - begin] = '\0';
454 break;
456 ++*ptr;
459 return value;
462 static char*
463 getuserinput(WScreen *scr, const char *line, int *ptr, Bool advanced)
465 char *ret = NULL, *title = NULL, *prompt = NULL, *name = NULL;
466 int rv;
468 if(line[*ptr] == '(')
469 title = parseuserinputpart(line, ptr, ",)");
470 if(title != NULL && line[*ptr] == ',')
471 prompt = parseuserinputpart(line, ptr, ",)");
472 if(prompt != NULL && line[*ptr] == ',')
473 name = parseuserinputpart(line, ptr, ")");
475 if(advanced)
476 rv = wAdvancedInputDialog(scr,
477 title ? _(title):_("Program Arguments"),
478 prompt ? _(prompt):_("Enter command arguments:"),
479 name, &ret);
480 else
481 rv = wInputDialog(scr,
482 title ? _(title):_("Program Arguments"),
483 prompt ? _(prompt):_("Enter command arguments:"),
484 &ret);
486 if(title) wfree(title);
487 if(prompt) wfree(prompt);
488 if(name) wfree(name);
490 return rv ? ret : NULL;
493 #define S_NORMAL 0
494 #define S_ESCAPE 1
495 #define S_OPTION 2
498 * state input new-state output
499 * NORMAL % OPTION <nil>
500 * NORMAL \ ESCAPE <nil>
501 * NORMAL etc. NORMAL <input>
502 * ESCAPE any NORMAL <input>
503 * OPTION s NORMAL <selection buffer>
504 * OPTION w NORMAL <selected window id>
505 * OPTION a NORMAL <input text>
506 * OPTION d NORMAL <OffiX DND selection object>
507 * OPTION W NORMAL <current workspace>
508 * OPTION etc. NORMAL %<input>
510 #define TMPBUFSIZE 64
511 char *ExpandOptions(WScreen *scr, const char *cmdline)
513 int ptr, optr, state, len, olen;
514 char *out, *nout;
515 char *selection = NULL;
516 char *user_input = NULL;
517 char tmpbuf[TMPBUFSIZE];
518 int slen;
520 len = strlen(cmdline);
521 olen = len + 1;
522 out = malloc(olen);
523 if (!out) {
524 wwarning(_("out of memory during expansion of \"%s\""), cmdline);
525 return NULL;
527 *out = 0;
528 ptr = 0; /* input line pointer */
529 optr = 0; /* output line pointer */
530 state = S_NORMAL;
531 while (ptr < len) {
532 switch (state) {
533 case S_NORMAL:
534 switch (cmdline[ptr]) {
535 case '\\':
536 state = S_ESCAPE;
537 break;
538 case '%':
539 state = S_OPTION;
540 break;
541 default:
542 state = S_NORMAL;
543 out[optr++] = cmdline[ptr];
544 break;
546 break;
547 case S_ESCAPE:
548 switch (cmdline[ptr]) {
549 case 'n':
550 out[optr++] = 10;
551 break;
553 case 'r':
554 out[optr++] = 13;
555 break;
557 case 't':
558 out[optr++] = 9;
559 break;
561 default:
562 out[optr++] = cmdline[ptr];
564 state = S_NORMAL;
565 break;
566 case S_OPTION:
567 state = S_NORMAL;
568 switch (cmdline[ptr]) {
569 case 'w':
570 if (scr->focused_window && scr->focused_window->flags.focused) {
571 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x",
572 (unsigned int)scr->focused_window->client_win);
573 slen = strlen(tmpbuf);
574 olen += slen;
575 nout = realloc(out, olen);
576 if (!nout) {
577 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%w", cmdline);
578 goto error;
580 out = nout;
581 strcat(out, tmpbuf);
582 optr += slen;
583 } else {
584 out[optr++] = ' ';
586 break;
588 case 'W':
589 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x", (unsigned int)scr->current_workspace + 1);
590 slen = strlen(tmpbuf);
591 olen += slen;
592 nout = realloc(out, olen);
593 if (!nout) {
594 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%W", cmdline);
595 goto error;
597 out = nout;
598 strcat(out, tmpbuf);
599 optr += slen;
600 break;
602 case 'a':
603 case 'A':
604 ptr++;
605 user_input = getuserinput(scr, cmdline, &ptr, cmdline[ptr-1] == 'A');
606 if (user_input) {
607 slen = strlen(user_input);
608 olen += slen;
609 nout = realloc(out, olen);
610 if (!nout) {
611 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%a", cmdline);
612 goto error;
614 out = nout;
615 strcat(out, user_input);
616 optr += slen;
617 } else {
618 /* Not an error, but user has Canceled the dialog box.
619 * This will make the command to not be performed. */
620 goto error;
622 break;
624 #ifdef USE_DOCK_XDND
625 case 'd':
626 if (!scr->xdestring) {
627 scr->flags.dnd_data_convertion_status = 1;
628 goto error;
630 slen = strlen(scr->xdestring);
631 olen += slen;
632 nout = realloc(out, olen);
633 if (!nout) {
634 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%d", cmdline);
635 goto error;
637 out = nout;
638 strcat(out, scr->xdestring);
639 optr += slen;
640 break;
641 #endif /* USE_DOCK_XDND */
643 case 's':
644 if (!selection) {
645 selection = getselection(scr);
647 if (!selection) {
648 wwarning(_("selection not available"));
649 goto error;
651 slen = strlen(selection);
652 olen += slen;
653 nout = realloc(out, olen);
654 if (!nout) {
655 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%s", cmdline);
656 goto error;
658 out = nout;
659 strcat(out, selection);
660 optr += slen;
661 break;
663 default:
664 out[optr++] = '%';
665 out[optr++] = cmdline[ptr];
667 break;
669 out[optr] = 0;
670 ptr++;
672 if (selection)
673 XFree(selection);
674 return out;
676 error:
677 wfree(out);
678 if (selection)
679 XFree(selection);
680 return NULL;
683 void ExecuteExitCommand(WScreen *scr, long quickmode)
685 static int inside = 0;
686 int result;
688 /* prevent reentrant calls */
689 if (inside)
690 return;
691 inside = 1;
693 #define R_CANCEL 0
694 #define R_EXIT 1
696 result = R_CANCEL;
698 if (quickmode == M_QUICK) {
699 result = R_EXIT;
700 } else {
701 int r, oldSaveSessionFlag;
703 oldSaveSessionFlag = wPreferences.save_session_on_exit;
704 r = wExitDialog(scr, _("Exit"),
705 _("Are you sure you want to quit Window Maker?"), _("Exit"), _("Cancel"), NULL);
707 if (r == WAPRDefault) {
708 result = R_EXIT;
709 } else if (r == WAPRAlternate) {
710 /* Don't modify the "save session on exit" flag if the
711 * user canceled the operation. */
712 wPreferences.save_session_on_exit = oldSaveSessionFlag;
715 if (result == R_EXIT)
716 Shutdown(WSExitMode);
718 #undef R_EXIT
719 #undef R_CANCEL
720 inside = 0;
723 void ExecuteInputCommand(WScreen *scr, const char *cmdline)
725 char *cmd;
727 cmd = ExpandOptions(scr, cmdline);
728 if (cmd) {
729 XGrabPointer(dpy, scr->root_win, True, 0,
730 GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime);
731 XSync(dpy, False);
733 ExecuteShellCommand(scr, cmd);
734 wfree(cmd);
736 XUngrabPointer(dpy, CurrentTime);
737 XSync(dpy, False);
741 void ParseWindowName(WMPropList *value, char **winstance, char **wclass, const char *where)
743 char *name;
745 *winstance = *wclass = NULL;
747 if (!WMIsPLString(value)) {
748 wwarning(_("bad window name value in %s state info"), where);
749 return;
752 name = WMGetFromPLString(value);
753 if (!name || strlen(name) == 0) {
754 wwarning(_("bad window name value in %s state info"), where);
755 return;
758 UnescapeWM_CLASS(name, winstance, wclass);
761 #if 0
762 static char *keysymToString(KeySym keysym, unsigned int state)
764 XKeyEvent kev;
765 char *buf = wmalloc(20);
766 int count;
768 kev.display = dpy;
769 kev.type = KeyPress;
770 kev.send_event = False;
771 kev.window = DefaultRootWindow(dpy);
772 kev.root = DefaultRootWindow(dpy);
773 kev.same_screen = True;
774 kev.subwindow = kev.root;
775 kev.serial = 0x12344321;
776 kev.time = CurrentTime;
777 kev.state = state;
778 kev.keycode = XKeysymToKeycode(dpy, keysym);
779 count = XLookupString(&kev, buf, 19, NULL, NULL);
780 buf[count] = 0;
782 return buf;
784 #endif
786 char *GetShortcutString(const char *shortcut)
788 char *buffer = NULL;
789 char *k, *tmp, *text;
791 tmp = text = wstrdup(shortcut);
793 /* get modifiers */
794 while ((k = strchr(text, '+')) != NULL) {
795 int mod;
796 const char *lbl;
798 *k = 0;
799 mod = wXModifierFromKey(text);
800 if (mod < 0) {
801 return wstrdup("bug");
803 lbl = wXModifierToShortcutLabel(mod);
804 if (lbl)
805 buffer = wstrappend(buffer, lbl);
806 else
807 buffer = wstrappend(buffer, text);
808 text = k + 1;
811 buffer = wstrappend(buffer, text);
812 wfree(tmp);
814 return buffer;
817 char *GetShortcutKey(WShortKey key)
819 const char *key_name;
820 char buffer[256];
821 char *wr;
823 void append_string(const char *text)
825 const char *string = text;
827 while (*string) {
828 if (wr >= buffer + sizeof(buffer) - 1)
829 break;
830 *wr++ = *string++;
834 void append_modifier(int modifier_index, const char *fallback_name)
836 if (wPreferences.modifier_labels[modifier_index]) {
837 append_string(wPreferences.modifier_labels[modifier_index]);
838 } else {
839 append_string(fallback_name);
843 key_name = XKeysymToString(XkbKeycodeToKeysym(dpy, key.keycode, 0, 0));
844 if (!key_name)
845 return NULL;
847 wr = buffer;
848 if (key.modifier & ControlMask) append_modifier(1, "Control+");
849 if (key.modifier & ShiftMask) append_modifier(0, "Shift+");
850 if (key.modifier & Mod1Mask) append_modifier(2, "Mod1+");
851 if (key.modifier & Mod2Mask) append_modifier(3, "Mod2+");
852 if (key.modifier & Mod3Mask) append_modifier(4, "Mod3+");
853 if (key.modifier & Mod4Mask) append_modifier(5, "Mod4+");
854 if (key.modifier & Mod5Mask) append_modifier(6, "Mod5+");
855 append_string(key_name);
856 *wr = '\0';
858 return GetShortcutString(buffer);
861 char *EscapeWM_CLASS(const char *name, const char *class)
863 char *ret;
864 char *ename = NULL, *eclass = NULL;
865 int i, j, l;
867 if (!name && !class)
868 return NULL;
870 if (name) {
871 l = strlen(name);
872 ename = wmalloc(l * 2 + 1);
873 j = 0;
874 for (i = 0; i < l; i++) {
875 if (name[i] == '\\') {
876 ename[j++] = '\\';
877 } else if (name[i] == '.') {
878 ename[j++] = '\\';
880 ename[j++] = name[i];
882 ename[j] = 0;
884 if (class) {
885 l = strlen(class);
886 eclass = wmalloc(l * 2 + 1);
887 j = 0;
888 for (i = 0; i < l; i++) {
889 if (class[i] == '\\') {
890 eclass[j++] = '\\';
891 } else if (class[i] == '.') {
892 eclass[j++] = '\\';
894 eclass[j++] = class[i];
896 eclass[j] = 0;
899 if (ename && eclass) {
900 int len = strlen(ename) + strlen(eclass) + 4;
901 ret = wmalloc(len);
902 snprintf(ret, len, "%s.%s", ename, eclass);
903 wfree(ename);
904 wfree(eclass);
905 } else if (ename) {
906 ret = wstrdup(ename);
907 wfree(ename);
908 } else {
909 ret = wstrdup(eclass);
910 wfree(eclass);
913 return ret;
916 static void UnescapeWM_CLASS(const char *str, char **name, char **class)
918 int i, j, k, dot;
919 int length_of_name;
921 j = strlen(str);
923 /* separate string in 2 parts */
924 length_of_name = 0;
925 dot = -1;
926 for (i = 0; i < j; i++, length_of_name++) {
927 if (str[i] == '\\') {
928 i++;
929 continue;
930 } else if (str[i] == '.') {
931 dot = i;
932 break;
936 /* unescape the name */
937 if (length_of_name > 0) {
938 *name = wmalloc(length_of_name + 1);
939 for (i = 0, k = 0; i < dot; i++) {
940 if (str[i] != '\\')
941 (*name)[k++] = str[i];
943 (*name)[k] = '\0';
944 } else {
945 *name = NULL;
948 /* unescape the class */
949 if (dot < j-1) {
950 *class = wmalloc(j - (dot + 1) + 1);
951 for (i = dot + 1, k = 0; i < j; i++) {
952 if (str[i] != '\\')
953 (*class)[k++] = str[i];
955 (*class)[k] = 0;
956 } else {
957 *class = NULL;
961 static void track_bg_helper_death(pid_t pid, unsigned int status, void *client_data)
963 WScreen *scr = (WScreen *) client_data;
965 /* Parameter not used, but tell the compiler that it is ok */
966 (void) pid;
967 (void) status;
969 close(scr->helper_fd);
970 scr->helper_fd = 0;
971 scr->helper_pid = 0;
972 scr->flags.backimage_helper_launched = 0;
975 Bool start_bg_helper(WScreen *scr)
977 pid_t pid;
978 int filedes[2];
980 if (pipe(filedes) < 0) {
981 werror(_("%s failed, can't set workspace specific background image (%s)"),
982 "pipe()", strerror(errno));
983 return False;
986 pid = fork();
987 if (pid < 0) {
988 werror(_("%s failed, can't set workspace specific background image (%s)"),
989 "fork()", strerror(errno));
990 close(filedes[0]);
991 close(filedes[1]);
992 return False;
994 } else if (pid == 0) {
995 const char *dither;
997 /* We don't need this side of the pipe in the child process */
998 close(filedes[1]);
1000 SetupEnvironment(scr);
1002 close(STDIN_FILENO);
1003 if (dup2(filedes[0], STDIN_FILENO) < 0) {
1004 werror(_("%s failed, can't set workspace specific background image (%s)"),
1005 "dup2()", strerror(errno));
1006 exit(1);
1008 close(filedes[0]);
1010 dither = wPreferences.no_dithering ? "-m" : "-d";
1011 if (wPreferences.smooth_workspace_back)
1012 execlp("wmsetbg", "wmsetbg", "-helper", "-S", dither, NULL);
1013 else
1014 execlp("wmsetbg", "wmsetbg", "-helper", dither, NULL);
1016 werror(_("could not execute \"%s\": %s"), "wmsetbg", strerror(errno));
1017 exit(1);
1019 } else {
1020 /* We don't need this side of the pipe in the parent process */
1021 close(filedes[0]);
1023 if (fcntl(filedes[1], F_SETFD, FD_CLOEXEC) < 0)
1024 wwarning(_("could not set close-on-exec flag for bg_helper's communication file handle (%s)"),
1025 strerror(errno));
1027 scr->helper_fd = filedes[1];
1028 scr->helper_pid = pid;
1029 scr->flags.backimage_helper_launched = 1;
1031 wAddDeathHandler(pid, track_bg_helper_death, scr);
1033 return True;
1037 void SendHelperMessage(WScreen *scr, char type, int workspace, const char *msg)
1039 char *buffer;
1040 int len;
1041 int i;
1042 char buf[16];
1044 if (!scr->flags.backimage_helper_launched) {
1045 return;
1048 len = (msg ? strlen(msg) : 0) + (workspace >= 0 ? 4 : 0) + 1;
1049 buffer = wmalloc(len + 5);
1050 snprintf(buf, sizeof(buf), "%4i", len);
1051 memcpy(buffer, buf, 4);
1052 buffer[4] = type;
1053 i = 5;
1054 if (workspace >= 0) {
1055 snprintf(buf, sizeof(buf), "%4i", workspace);
1056 memcpy(&buffer[i], buf, 4);
1057 i += 4;
1058 buffer[i] = 0;
1060 if (msg)
1061 strcpy(&buffer[i], msg);
1063 if (write(scr->helper_fd, buffer, len + 4) < 0) {
1064 werror(_("could not send message to background image helper"));
1066 wfree(buffer);
1069 Bool UpdateDomainFile(WDDomain * domain)
1071 struct stat stbuf;
1072 char path[PATH_MAX];
1073 WMPropList *shared_dict, *dict;
1074 Bool result, freeDict = False;
1076 dict = domain->dictionary;
1077 if (WMIsPLDictionary(domain->dictionary)) {
1078 /* retrieve global system dictionary */
1079 snprintf(path, sizeof(path), "%s/%s", PKGCONFDIR, domain->domain_name);
1080 if (stat(path, &stbuf) >= 0) {
1081 shared_dict = WMReadPropListFromFile(path);
1082 if (shared_dict) {
1083 if (WMIsPLDictionary(shared_dict)) {
1084 freeDict = True;
1085 dict = WMDeepCopyPropList(domain->dictionary);
1086 WMSubtractPLDictionaries(dict, shared_dict, True);
1088 WMReleasePropList(shared_dict);
1093 result = WMWritePropListToFile(dict, domain->path);
1095 if (freeDict) {
1096 WMReleasePropList(dict);
1099 return result;
1102 char *StrConcatDot(const char *a, const char *b)
1104 int len;
1105 char *str;
1107 if (!a)
1108 a = "";
1109 if (!b)
1110 b = "";
1112 len = strlen(a) + strlen(b) + 4;
1113 str = wmalloc(len);
1115 snprintf(str, len, "%s.%s", a, b);
1117 return str;
1120 static char *getCommandForWindow(Window win, int elements)
1122 char **argv, *command = NULL;
1123 int argc;
1125 if (XGetCommand(dpy, win, &argv, &argc)) {
1126 if (argc > 0 && argv != NULL) {
1127 if (elements == 0)
1128 elements = argc;
1129 command = wtokenjoin(argv, WMIN(argc, elements));
1130 if (command[0] == 0) {
1131 wfree(command);
1132 command = NULL;
1135 if (argv) {
1136 XFreeStringList(argv);
1140 return command;
1143 /* Free result when done */
1144 char *GetCommandForWindow(Window win)
1146 return getCommandForWindow(win, 0);