Update Serbian translation from master branch
[wmaker-crm.git] / src / misc.c
blob4cab3584bdb7cb5ef3e86f17e4e19c6a05c4db7d
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 <WINGs/WINGsP.h>
42 #include <wraster.h>
44 #include "window.h"
45 #include "misc.h"
46 #include "WindowMaker.h"
47 #include "GNUstep.h"
48 #include "screen.h"
49 #include "wcore.h"
50 #include "window.h"
51 #include "framewin.h"
52 #include "dialog.h"
53 #include "xutil.h"
54 #include "xmodifier.h"
55 #include "main.h"
56 #include "event.h"
57 #include "shutdown.h"
60 #define ICON_SIZE wPreferences.icon_size
62 /**** Local prototypes *****/
63 static void UnescapeWM_CLASS(const char *str, char **name, char **class);
65 /* XFetchName Wrapper */
66 Bool wFetchName(Display *dpy, Window win, char **winname)
68 XTextProperty text_prop;
69 char **list;
70 int num;
72 if (XGetWMName(dpy, win, &text_prop)) {
73 if (text_prop.value && text_prop.nitems > 0) {
74 if (text_prop.encoding == XA_STRING) {
75 *winname = wstrdup((char *)text_prop.value);
76 XFree(text_prop.value);
77 } else {
78 text_prop.nitems = strlen((char *)text_prop.value);
79 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >=
80 Success && num > 0 && *list) {
81 XFree(text_prop.value);
82 *winname = wstrdup(*list);
83 XFreeStringList(list);
84 } else {
85 *winname = wstrdup((char *)text_prop.value);
86 XFree(text_prop.value);
89 } else {
90 /* the title is set, but it was set to none */
91 *winname = wstrdup("");
93 return True;
94 } else {
95 /* the hint is probably not set */
96 *winname = NULL;
98 return False;
102 /* XGetIconName Wrapper */
103 Bool wGetIconName(Display *dpy, Window win, char **iconname)
105 XTextProperty text_prop;
106 char **list;
107 int num;
109 if (XGetWMIconName(dpy, win, &text_prop) != 0 && text_prop.value && text_prop.nitems > 0) {
110 if (text_prop.encoding == XA_STRING)
111 *iconname = (char *)text_prop.value;
112 else {
113 text_prop.nitems = strlen((char *)text_prop.value);
114 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success && num > 0 && *list) {
115 XFree(text_prop.value);
116 *iconname = wstrdup(*list);
117 XFreeStringList(list);
118 } else
119 *iconname = (char *)text_prop.value;
121 return True;
123 *iconname = NULL;
124 return False;
127 static void eatExpose(void)
129 XEvent event, foo;
131 /* compress all expose events into a single one */
133 if (XCheckMaskEvent(dpy, ExposureMask, &event)) {
134 /* ignore other exposure events for this window */
135 while (XCheckWindowEvent(dpy, event.xexpose.window, ExposureMask, &foo)) ;
136 /* eat exposes for other windows */
137 eatExpose();
139 event.xexpose.count = 0;
140 XPutBackEvent(dpy, &event);
144 void move_window(Window win, int from_x, int from_y, int to_x, int to_y)
146 #ifdef USE_ANIMATIONS
147 if (wPreferences.no_animations)
148 XMoveWindow(dpy, win, to_x, to_y);
149 else
150 slide_window(win, from_x, from_y, to_x, to_y);
151 #else
152 XMoveWindow(dpy, win, to_x, to_y);
154 /* Tell the compiler it is normal that those parameters are not used in this case */
155 (void) from_x;
156 (void) from_y;
157 #endif
160 /* wins is an array of Window, sorted from left to right, the first is
161 * going to be moved from (from_x,from_y) to (to_x,to_y) and the
162 * following windows are going to be offset by (ICON_SIZE*i,0) */
163 void slide_windows(Window wins[], int n, int from_x, int from_y, int to_x, int to_y)
165 time_t time0 = time(NULL);
166 float dx, dy, x = from_x, y = from_y, px, py;
167 Bool is_dx_nul, is_dy_nul;
168 int dx_is_bigger = 0, dx_int, dy_int;
169 int slide_delay, slide_steps, slide_slowdown;
170 int i;
172 /* animation parameters */
173 static const struct {
174 int delay;
175 int steps;
176 int slowdown;
177 } apars[5] = {
178 {ICON_SLIDE_DELAY_UF, ICON_SLIDE_STEPS_UF, ICON_SLIDE_SLOWDOWN_UF},
179 {ICON_SLIDE_DELAY_F, ICON_SLIDE_STEPS_F, ICON_SLIDE_SLOWDOWN_F},
180 {ICON_SLIDE_DELAY_M, ICON_SLIDE_STEPS_M, ICON_SLIDE_SLOWDOWN_M},
181 {ICON_SLIDE_DELAY_S, ICON_SLIDE_STEPS_S, ICON_SLIDE_SLOWDOWN_S},
182 {ICON_SLIDE_DELAY_US, ICON_SLIDE_STEPS_US, ICON_SLIDE_SLOWDOWN_US}
185 slide_slowdown = apars[(int)wPreferences.icon_slide_speed].slowdown;
186 slide_steps = apars[(int)wPreferences.icon_slide_speed].steps;
187 slide_delay = apars[(int)wPreferences.icon_slide_speed].delay;
189 dx_int = to_x - from_x;
190 dy_int = to_y - from_y;
191 is_dx_nul = (dx_int == 0);
192 is_dy_nul = (dy_int == 0);
193 dx = (float) dx_int;
194 dy = (float) dy_int;
196 if (abs(dx_int) > abs(dy_int)) {
197 dx_is_bigger = 1;
200 if (dx_is_bigger) {
201 px = dx / slide_slowdown;
202 if (px < slide_steps && px > 0)
203 px = slide_steps;
204 else if (px > -slide_steps && px < 0)
205 px = -slide_steps;
206 py = (is_dx_nul ? 0.0F : px * dy / dx);
207 } else {
208 py = dy / slide_slowdown;
209 if (py < slide_steps && py > 0)
210 py = slide_steps;
211 else if (py > -slide_steps && py < 0)
212 py = -slide_steps;
213 px = (is_dy_nul ? 0.0F : py * dx / dy);
216 while (((int)x) != to_x ||
217 ((int)y) != to_y) {
218 x += px;
219 y += py;
220 if ((px < 0 && (int)x < to_x) || (px > 0 && (int)x > to_x))
221 x = (float)to_x;
222 if ((py < 0 && (int)y < to_y) || (py > 0 && (int)y > to_y))
223 y = (float)to_y;
225 if (dx_is_bigger) {
226 px = px * (1.0F - 1 / (float)slide_slowdown);
227 if (px < slide_steps && px > 0)
228 px = slide_steps;
229 else if (px > -slide_steps && px < 0)
230 px = -slide_steps;
231 py = (is_dx_nul ? 0.0F : px * dy / dx);
232 } else {
233 py = py * (1.0F - 1 / (float)slide_slowdown);
234 if (py < slide_steps && py > 0)
235 py = slide_steps;
236 else if (py > -slide_steps && py < 0)
237 py = -slide_steps;
238 px = (is_dy_nul ? 0.0F : py * dx / dy);
241 for (i = 0; i < n; i++) {
242 XMoveWindow(dpy, wins[i], (int)x + i * ICON_SIZE, (int)y);
244 XFlush(dpy);
245 if (slide_delay > 0) {
246 wusleep(slide_delay * 1000L);
247 } else {
248 wusleep(1000L);
250 if (time(NULL) - time0 > MAX_ANIMATION_TIME)
251 break;
253 for (i = 0; i < n; i++) {
254 XMoveWindow(dpy, wins[i], to_x + i * ICON_SIZE, to_y);
257 XSync(dpy, 0);
258 /* compress expose events */
259 eatExpose();
262 char *ShrinkString(WMFont *font, const char *string, int width)
264 int w, w1 = 0;
265 int p;
266 char *pos;
267 char *text;
268 int p1, p2, t;
270 p = strlen(string);
271 w = WMWidthOfString(font, string, p);
272 text = wmalloc(strlen(string) + 8);
273 strcpy(text, string);
274 if (w <= width)
275 return text;
277 pos = strchr(text, ' ');
278 if (!pos)
279 pos = strchr(text, ':');
281 if (pos) {
282 *pos = 0;
283 p = strlen(text);
284 w1 = WMWidthOfString(font, text, p);
285 if (w1 > width) {
286 p = 0;
287 *pos = ' ';
288 *text = 0;
289 } else {
290 *pos = 0;
291 width -= w1;
292 p++;
294 string += p;
295 p = strlen(string);
296 } else {
297 *text = 0;
299 strcat(text, "...");
300 width -= WMWidthOfString(font, "...", 3);
302 p1 = 0;
303 p2 = p;
304 t = (p2 - p1) / 2;
305 while (p2 > p1 && p1 != t) {
306 w = WMWidthOfString(font, &string[p - t], t);
307 if (w > width) {
308 p2 = t;
309 t = p1 + (p2 - p1) / 2;
310 } else if (w < width) {
311 p1 = t;
312 t = p1 + (p2 - p1) / 2;
313 } else
314 p2 = p1 = t;
316 strcat(text, &string[p - p1]);
318 return text;
321 char *FindImage(const char *paths, const char *file)
323 char *tmp, *path = NULL;
325 tmp = strrchr(file, ':');
326 if (tmp) {
327 *tmp = 0;
328 path = wfindfile(paths, file);
329 *tmp = ':';
331 if (!tmp || !path)
332 path = wfindfile(paths, file);
334 return path;
337 static void timeoutHandler(void *data)
339 *(int *)data = 1;
342 static char *getTextSelection(WScreen * screen, Atom selection)
344 int buffer = -1;
346 switch (selection) {
347 case XA_CUT_BUFFER0:
348 buffer = 0;
349 break;
350 case XA_CUT_BUFFER1:
351 buffer = 1;
352 break;
353 case XA_CUT_BUFFER2:
354 buffer = 2;
355 break;
356 case XA_CUT_BUFFER3:
357 buffer = 3;
358 break;
359 case XA_CUT_BUFFER4:
360 buffer = 4;
361 break;
362 case XA_CUT_BUFFER5:
363 buffer = 5;
364 break;
365 case XA_CUT_BUFFER6:
366 buffer = 6;
367 break;
368 case XA_CUT_BUFFER7:
369 buffer = 7;
370 break;
372 if (buffer >= 0) {
373 char *data;
374 int size;
376 data = XFetchBuffer(dpy, &size, buffer);
378 return data;
379 } else {
380 char *data;
381 int bits;
382 Atom rtype;
383 unsigned long len, bytes;
384 WMHandlerID timer;
385 int timeout = 0;
386 XEvent ev;
387 static Atom clipboard = 0;
389 if (!clipboard)
390 clipboard = XInternAtom(dpy, "CLIPBOARD", False);
392 XDeleteProperty(dpy, screen->info_window, clipboard);
394 XConvertSelection(dpy, selection, XA_STRING, clipboard, screen->info_window, CurrentTime);
396 timer = WMAddTimerHandler(1000, timeoutHandler, &timeout);
398 while (!XCheckTypedWindowEvent(dpy, screen->info_window, SelectionNotify, &ev) && !timeout) ;
400 if (!timeout) {
401 WMDeleteTimerHandler(timer);
402 } else {
403 wwarning("selection retrieval timed out");
404 return NULL;
407 /* nobody owns the selection or the current owner has
408 * nothing to do with what we need */
409 if (ev.xselection.property == None) {
410 return NULL;
413 if (XGetWindowProperty(dpy, screen->info_window,
414 clipboard, 0, 1024,
415 False, XA_STRING, &rtype, &bits, &len,
416 &bytes, (unsigned char **)&data) != Success) {
417 return NULL;
419 if (rtype != XA_STRING || bits != 8) {
420 wwarning("invalid data in text selection");
421 if (data)
422 XFree(data);
423 return NULL;
425 return data;
429 static char *getselection(WScreen * scr)
431 char *tmp;
433 tmp = getTextSelection(scr, XA_PRIMARY);
434 if (!tmp)
435 tmp = getTextSelection(scr, XA_CUT_BUFFER0);
436 return tmp;
439 static char*
440 parseuserinputpart(const char *line, int *ptr, const char *endchars)
442 int depth = 0, begin;
443 char *value = NULL;
444 begin = ++*ptr;
446 while(line[*ptr] != '\0') {
447 if(line[*ptr] == '(') {
448 ++depth;
449 } else if(depth > 0 && line[*ptr] == ')') {
450 --depth;
451 } else if(depth == 0 && strchr(endchars, line[*ptr]) != NULL) {
452 value = wmalloc(*ptr - begin + 1);
453 strncpy(value, line + begin, *ptr - begin);
454 value[*ptr - begin] = '\0';
455 break;
457 ++*ptr;
460 return value;
463 static char*
464 getuserinput(WScreen *scr, const char *line, int *ptr, Bool advanced)
466 char *ret = NULL, *title = NULL, *prompt = NULL, *name = NULL;
467 int rv;
469 if(line[*ptr] == '(')
470 title = parseuserinputpart(line, ptr, ",)");
471 if(title != NULL && line[*ptr] == ',')
472 prompt = parseuserinputpart(line, ptr, ",)");
473 if(prompt != NULL && line[*ptr] == ',')
474 name = parseuserinputpart(line, ptr, ")");
476 if(advanced)
477 rv = wAdvancedInputDialog(scr,
478 title ? _(title):_("Program Arguments"),
479 prompt ? _(prompt):_("Enter command arguments:"),
480 name, &ret);
481 else
482 rv = wInputDialog(scr,
483 title ? _(title):_("Program Arguments"),
484 prompt ? _(prompt):_("Enter command arguments:"),
485 &ret);
487 if(title) wfree(title);
488 if(prompt) wfree(prompt);
489 if(name) wfree(name);
491 return rv ? ret : NULL;
494 #define S_NORMAL 0
495 #define S_ESCAPE 1
496 #define S_OPTION 2
499 * state input new-state output
500 * NORMAL % OPTION <nil>
501 * NORMAL \ ESCAPE <nil>
502 * NORMAL etc. NORMAL <input>
503 * ESCAPE any NORMAL <input>
504 * OPTION s NORMAL <selection buffer>
505 * OPTION w NORMAL <selected window id>
506 * OPTION a NORMAL <input text>
507 * OPTION d NORMAL <OffiX DND selection object>
508 * OPTION W NORMAL <current workspace>
509 * OPTION etc. NORMAL %<input>
511 #define TMPBUFSIZE 64
512 char *ExpandOptions(WScreen *scr, const char *cmdline)
514 int ptr, optr, state, len, olen;
515 char *out, *nout;
516 char *selection = NULL;
517 char *user_input = NULL;
518 char tmpbuf[TMPBUFSIZE];
519 int slen;
521 len = strlen(cmdline);
522 olen = len + 1;
523 out = malloc(olen);
524 if (!out) {
525 wwarning(_("out of memory during expansion of \"%s\""), cmdline);
526 return NULL;
528 *out = 0;
529 ptr = 0; /* input line pointer */
530 optr = 0; /* output line pointer */
531 state = S_NORMAL;
532 while (ptr < len) {
533 switch (state) {
534 case S_NORMAL:
535 switch (cmdline[ptr]) {
536 case '\\':
537 state = S_ESCAPE;
538 break;
539 case '%':
540 state = S_OPTION;
541 break;
542 default:
543 state = S_NORMAL;
544 out[optr++] = cmdline[ptr];
545 break;
547 break;
548 case S_ESCAPE:
549 switch (cmdline[ptr]) {
550 case 'n':
551 out[optr++] = 10;
552 break;
554 case 'r':
555 out[optr++] = 13;
556 break;
558 case 't':
559 out[optr++] = 9;
560 break;
562 default:
563 out[optr++] = cmdline[ptr];
565 state = S_NORMAL;
566 break;
567 case S_OPTION:
568 state = S_NORMAL;
569 switch (cmdline[ptr]) {
570 case 'w':
571 if (scr->focused_window && scr->focused_window->flags.focused) {
572 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x",
573 (unsigned int)scr->focused_window->client_win);
574 slen = strlen(tmpbuf);
575 olen += slen;
576 nout = realloc(out, olen);
577 if (!nout) {
578 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%w", cmdline);
579 goto error;
581 out = nout;
582 strcat(out, tmpbuf);
583 optr += slen;
584 } else {
585 out[optr++] = ' ';
587 break;
589 case 'W':
590 snprintf(tmpbuf, sizeof(tmpbuf), "0x%x", (unsigned int)scr->current_workspace + 1);
591 slen = strlen(tmpbuf);
592 olen += slen;
593 nout = realloc(out, olen);
594 if (!nout) {
595 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%W", cmdline);
596 goto error;
598 out = nout;
599 strcat(out, tmpbuf);
600 optr += slen;
601 break;
603 case 'a':
604 case 'A':
605 ptr++;
606 user_input = getuserinput(scr, cmdline, &ptr, cmdline[ptr-1] == 'A');
607 if (user_input) {
608 slen = strlen(user_input);
609 olen += slen;
610 nout = realloc(out, olen);
611 if (!nout) {
612 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%a", cmdline);
613 goto error;
615 out = nout;
616 strcat(out, user_input);
617 optr += slen;
618 } else {
619 /* Not an error, but user has Canceled the dialog box.
620 * This will make the command to not be performed. */
621 goto error;
623 break;
625 #ifdef USE_DOCK_XDND
626 case 'd':
627 if (!scr->xdestring) {
628 scr->flags.dnd_data_convertion_status = 1;
629 goto error;
631 slen = strlen(scr->xdestring);
632 olen += slen;
633 nout = realloc(out, olen);
634 if (!nout) {
635 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%d", cmdline);
636 goto error;
638 out = nout;
639 strcat(out, scr->xdestring);
640 optr += slen;
641 break;
642 #endif /* USE_DOCK_XDND */
644 case 's':
645 if (!selection) {
646 selection = getselection(scr);
648 if (!selection) {
649 wwarning(_("selection not available"));
650 goto error;
652 slen = strlen(selection);
653 olen += slen;
654 nout = realloc(out, olen);
655 if (!nout) {
656 wwarning(_("out of memory during expansion of '%s' for command \"%s\""), "%s", cmdline);
657 goto error;
659 out = nout;
660 strcat(out, selection);
661 optr += slen;
662 break;
664 default:
665 out[optr++] = '%';
666 out[optr++] = cmdline[ptr];
668 break;
670 out[optr] = 0;
671 ptr++;
673 if (selection)
674 XFree(selection);
675 return out;
677 error:
678 wfree(out);
679 if (selection)
680 XFree(selection);
681 return NULL;
684 void ExecuteExitCommand(WScreen *scr, long quickmode)
686 static int inside = 0;
687 int result;
689 /* prevent reentrant calls */
690 if (inside)
691 return;
692 inside = 1;
694 #define R_CANCEL 0
695 #define R_EXIT 1
697 result = R_CANCEL;
699 if (quickmode == M_QUICK) {
700 result = R_EXIT;
701 } else {
702 int r, oldSaveSessionFlag;
704 oldSaveSessionFlag = wPreferences.save_session_on_exit;
705 r = wExitDialog(scr, _("Exit"),
706 _("Are you sure you want to quit Window Maker?"), _("Exit"), _("Cancel"), NULL);
708 if (r == WAPRDefault) {
709 result = R_EXIT;
710 } else if (r == WAPRAlternate) {
711 /* Don't modify the "save session on exit" flag if the
712 * user canceled the operation. */
713 wPreferences.save_session_on_exit = oldSaveSessionFlag;
716 if (result == R_EXIT)
717 Shutdown(WSExitMode);
719 #undef R_EXIT
720 #undef R_CANCEL
721 inside = 0;
724 void ExecuteInputCommand(WScreen *scr, const char *cmdline)
726 char *cmd;
728 cmd = ExpandOptions(scr, cmdline);
729 if (cmd) {
730 XGrabPointer(dpy, scr->root_win, True, 0,
731 GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime);
732 XSync(dpy, False);
734 ExecuteShellCommand(scr, cmd);
735 wfree(cmd);
737 XUngrabPointer(dpy, CurrentTime);
738 XSync(dpy, False);
742 void ParseWindowName(WMPropList *value, char **winstance, char **wclass, const char *where)
744 char *name;
746 *winstance = *wclass = NULL;
748 if (!WMIsPLString(value)) {
749 wwarning(_("bad window name value in %s state info"), where);
750 return;
753 name = WMGetFromPLString(value);
754 if (!name || strlen(name) == 0) {
755 wwarning(_("bad window name value in %s state info"), where);
756 return;
759 UnescapeWM_CLASS(name, winstance, wclass);
762 #if 0
763 static char *keysymToString(KeySym keysym, unsigned int state)
765 XKeyEvent kev;
766 char *buf = wmalloc(20);
767 int count;
769 kev.display = dpy;
770 kev.type = KeyPress;
771 kev.send_event = False;
772 kev.window = DefaultRootWindow(dpy);
773 kev.root = DefaultRootWindow(dpy);
774 kev.same_screen = True;
775 kev.subwindow = kev.root;
776 kev.serial = 0x12344321;
777 kev.time = CurrentTime;
778 kev.state = state;
779 kev.keycode = XKeysymToKeycode(dpy, keysym);
780 count = XLookupString(&kev, buf, 19, NULL, NULL);
781 buf[count] = 0;
783 return buf;
785 #endif
787 char *GetShortcutString(const char *shortcut)
789 char *buffer = NULL;
790 char *k, *tmp, *text;
792 tmp = text = wstrdup(shortcut);
794 /* get modifiers */
795 while ((k = strchr(text, '+')) != NULL) {
796 int mod;
797 const char *lbl;
799 *k = 0;
800 mod = wXModifierFromKey(text);
801 if (mod < 0) {
802 wfree(buffer);
803 return wstrdup("bug");
805 lbl = wXModifierToShortcutLabel(mod);
806 if (lbl)
807 buffer = wstrappend(buffer, lbl);
808 else
809 buffer = wstrappend(buffer, text);
810 text = k + 1;
813 buffer = wstrappend(buffer, text);
814 wfree(tmp);
816 return buffer;
819 char *GetShortcutKey(WShortKey key)
821 const char *key_name;
822 char buffer[256];
823 char *wr;
825 void append_string(const char *text)
827 const char *string = text;
829 while (*string) {
830 if (wr >= buffer + sizeof(buffer) - 1)
831 break;
832 *wr++ = *string++;
836 void append_modifier(int modifier_index, const char *fallback_name)
838 if (wPreferences.modifier_labels[modifier_index]) {
839 append_string(wPreferences.modifier_labels[modifier_index]);
840 } else {
841 append_string(fallback_name);
845 key_name = XKeysymToString(W_KeycodeToKeysym(dpy, key.keycode, 0));
846 if (!key_name)
847 return NULL;
849 wr = buffer;
850 if (key.modifier & ControlMask) append_modifier(1, "Control+");
851 if (key.modifier & ShiftMask) append_modifier(0, "Shift+");
852 if (key.modifier & Mod1Mask) append_modifier(2, "Mod1+");
853 if (key.modifier & Mod2Mask) append_modifier(3, "Mod2+");
854 if (key.modifier & Mod3Mask) append_modifier(4, "Mod3+");
855 if (key.modifier & Mod4Mask) append_modifier(5, "Mod4+");
856 if (key.modifier & Mod5Mask) append_modifier(6, "Mod5+");
857 append_string(key_name);
858 *wr = '\0';
860 return GetShortcutString(buffer);
863 char *EscapeWM_CLASS(const char *name, const char *class)
865 char *ret;
866 char *ename = NULL, *eclass = NULL;
867 int i, j, l;
869 if (!name && !class)
870 return NULL;
872 if (name) {
873 l = strlen(name);
874 ename = wmalloc(l * 2 + 1);
875 j = 0;
876 for (i = 0; i < l; i++) {
877 if (name[i] == '\\') {
878 ename[j++] = '\\';
879 } else if (name[i] == '.') {
880 ename[j++] = '\\';
882 ename[j++] = name[i];
884 ename[j] = 0;
886 if (class) {
887 l = strlen(class);
888 eclass = wmalloc(l * 2 + 1);
889 j = 0;
890 for (i = 0; i < l; i++) {
891 if (class[i] == '\\') {
892 eclass[j++] = '\\';
893 } else if (class[i] == '.') {
894 eclass[j++] = '\\';
896 eclass[j++] = class[i];
898 eclass[j] = 0;
901 if (ename && eclass) {
902 int len = strlen(ename) + strlen(eclass) + 4;
903 ret = wmalloc(len);
904 snprintf(ret, len, "%s.%s", ename, eclass);
905 wfree(ename);
906 wfree(eclass);
907 } else if (ename) {
908 ret = wstrdup(ename);
909 wfree(ename);
910 } else {
911 ret = wstrdup(eclass);
912 wfree(eclass);
915 return ret;
918 static void UnescapeWM_CLASS(const char *str, char **name, char **class)
920 int i, j, k, dot;
921 int length_of_name;
923 j = strlen(str);
925 /* separate string in 2 parts */
926 length_of_name = 0;
927 dot = -1;
928 for (i = 0; i < j; i++, length_of_name++) {
929 if (str[i] == '\\') {
930 i++;
931 continue;
932 } else if (str[i] == '.') {
933 dot = i;
934 break;
938 /* unescape the name */
939 if (length_of_name > 0) {
940 *name = wmalloc(length_of_name + 1);
941 for (i = 0, k = 0; i < dot; i++) {
942 if (str[i] != '\\')
943 (*name)[k++] = str[i];
945 (*name)[k] = '\0';
946 } else {
947 *name = NULL;
950 /* unescape the class */
951 if (dot < j-1) {
952 *class = wmalloc(j - (dot + 1) + 1);
953 for (i = dot + 1, k = 0; i < j; i++) {
954 if (str[i] != '\\')
955 (*class)[k++] = str[i];
957 (*class)[k] = 0;
958 } else {
959 *class = NULL;
963 static void track_bg_helper_death(pid_t pid, unsigned int status, void *client_data)
965 WScreen *scr = (WScreen *) client_data;
967 /* Parameter not used, but tell the compiler that it is ok */
968 (void) pid;
969 (void) status;
971 close(scr->helper_fd);
972 scr->helper_fd = 0;
973 scr->helper_pid = 0;
974 scr->flags.backimage_helper_launched = 0;
977 Bool start_bg_helper(WScreen *scr)
979 pid_t pid;
980 int filedes[2];
982 if (pipe(filedes) < 0) {
983 werror(_("%s failed, can't set workspace specific background image (%s)"),
984 "pipe()", strerror(errno));
985 return False;
988 pid = fork();
989 if (pid < 0) {
990 werror(_("%s failed, can't set workspace specific background image (%s)"),
991 "fork()", strerror(errno));
992 close(filedes[0]);
993 close(filedes[1]);
994 return False;
996 } else if (pid == 0) {
997 const char *dither;
999 /* We don't need this side of the pipe in the child process */
1000 close(filedes[1]);
1002 SetupEnvironment(scr);
1004 close(STDIN_FILENO);
1005 if (dup2(filedes[0], STDIN_FILENO) < 0) {
1006 werror(_("%s failed, can't set workspace specific background image (%s)"),
1007 "dup2()", strerror(errno));
1008 exit(1);
1010 close(filedes[0]);
1012 dither = wPreferences.no_dithering ? "-m" : "-d";
1013 if (wPreferences.smooth_workspace_back)
1014 execlp("wmsetbg", "wmsetbg", "-helper", "-S", dither, NULL);
1015 else
1016 execlp("wmsetbg", "wmsetbg", "-helper", dither, NULL);
1018 werror(_("could not execute \"%s\": %s"), "wmsetbg", strerror(errno));
1019 exit(1);
1021 } else {
1022 /* We don't need this side of the pipe in the parent process */
1023 close(filedes[0]);
1025 if (fcntl(filedes[1], F_SETFD, FD_CLOEXEC) < 0)
1026 wwarning(_("could not set close-on-exec flag for bg_helper's communication file handle (%s)"),
1027 strerror(errno));
1029 scr->helper_fd = filedes[1];
1030 scr->helper_pid = pid;
1031 scr->flags.backimage_helper_launched = 1;
1033 wAddDeathHandler(pid, track_bg_helper_death, scr);
1035 return True;
1039 void SendHelperMessage(WScreen *scr, char type, int workspace, const char *msg)
1041 char *buffer;
1042 int len;
1043 int i;
1044 char buf[16];
1046 if (!scr->flags.backimage_helper_launched) {
1047 return;
1050 len = (msg ? strlen(msg) : 0) + (workspace >= 0 ? 4 : 0) + 1;
1051 buffer = wmalloc(len + 5);
1052 snprintf(buf, sizeof(buf), "%4i", len);
1053 memcpy(buffer, buf, 4);
1054 buffer[4] = type;
1055 i = 5;
1056 if (workspace >= 0) {
1057 snprintf(buf, sizeof(buf), "%4i", workspace);
1058 memcpy(&buffer[i], buf, 4);
1059 i += 4;
1060 buffer[i] = 0;
1062 if (msg)
1063 strcpy(&buffer[i], msg);
1065 if (write(scr->helper_fd, buffer, len + 4) < 0) {
1066 werror(_("could not send message to background image helper"));
1068 wfree(buffer);
1071 Bool UpdateDomainFile(WDDomain * domain)
1073 struct stat stbuf;
1074 char path[PATH_MAX];
1075 WMPropList *shared_dict, *dict;
1076 Bool result, freeDict = False;
1078 dict = domain->dictionary;
1079 if (WMIsPLDictionary(domain->dictionary)) {
1080 /* retrieve global system dictionary */
1081 snprintf(path, sizeof(path), "%s/%s", PKGCONFDIR, domain->domain_name);
1082 if (stat(path, &stbuf) >= 0) {
1083 shared_dict = WMReadPropListFromFile(path);
1084 if (shared_dict) {
1085 if (WMIsPLDictionary(shared_dict)) {
1086 freeDict = True;
1087 dict = WMDeepCopyPropList(domain->dictionary);
1088 WMSubtractPLDictionaries(dict, shared_dict, True);
1090 WMReleasePropList(shared_dict);
1095 result = WMWritePropListToFile(dict, domain->path);
1097 if (freeDict) {
1098 WMReleasePropList(dict);
1101 return result;
1104 char *StrConcatDot(const char *a, const char *b)
1106 int len;
1107 char *str;
1109 if (!a)
1110 a = "";
1111 if (!b)
1112 b = "";
1114 len = strlen(a) + strlen(b) + 4;
1115 str = wmalloc(len);
1117 snprintf(str, len, "%s.%s", a, b);
1119 return str;
1122 static char *getCommandForWindow(Window win, int elements)
1124 char **argv, *command = NULL;
1125 int argc;
1127 if (XGetCommand(dpy, win, &argv, &argc)) {
1128 if (argc > 0 && argv != NULL) {
1129 if (elements == 0)
1130 elements = argc;
1131 command = wtokenjoin(argv, WMIN(argc, elements));
1132 if (command[0] == 0) {
1133 wfree(command);
1134 command = NULL;
1137 if (argv) {
1138 XFreeStringList(argv);
1142 return command;
1145 /* Free result when done */
1146 char *GetCommandForWindow(Window win)
1148 return getCommandForWindow(win, 0);