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.
23 #include <X11/Xutil.h>
24 #include <X11/Xatom.h>
36 #include <X11/XKBlib.h>
38 #include <WINGs/WUtil.h>
43 #include "WindowMaker.h"
51 #include "xmodifier.h"
53 /**** global variables *****/
54 extern WPreferences wPreferences
;
55 #define ICON_SIZE wPreferences.icon_size
57 /**** Local prototypes *****/
58 static void UnescapeWM_CLASS(const char *str
, char **name
, char **class);
60 /* XFetchName Wrapper */
61 Bool
wFetchName(Display
*dpy
, Window win
, char **winname
)
63 XTextProperty text_prop
;
67 if (XGetWMName(dpy
, win
, &text_prop
)) {
68 if (text_prop
.value
&& text_prop
.nitems
> 0) {
69 if (text_prop
.encoding
== XA_STRING
) {
70 *winname
= wstrdup((char *)text_prop
.value
);
71 XFree(text_prop
.value
);
73 text_prop
.nitems
= strlen((char *)text_prop
.value
);
74 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >=
75 Success
&& num
> 0 && *list
) {
76 XFree(text_prop
.value
);
77 *winname
= wstrdup(*list
);
78 XFreeStringList(list
);
80 *winname
= wstrdup((char *)text_prop
.value
);
81 XFree(text_prop
.value
);
85 /* the title is set, but it was set to none */
86 *winname
= wstrdup("");
90 /* the hint is probably not set */
97 /* XGetIconName Wrapper */
98 Bool
wGetIconName(Display
*dpy
, Window win
, char **iconname
)
100 XTextProperty text_prop
;
104 if (XGetWMIconName(dpy
, win
, &text_prop
) != 0 && text_prop
.value
&& text_prop
.nitems
> 0) {
105 if (text_prop
.encoding
== XA_STRING
)
106 *iconname
= (char *)text_prop
.value
;
108 text_prop
.nitems
= strlen((char *)text_prop
.value
);
109 if (XmbTextPropertyToTextList(dpy
, &text_prop
, &list
, &num
) >= Success
&& num
> 0 && *list
) {
110 XFree(text_prop
.value
);
111 *iconname
= wstrdup(*list
);
112 XFreeStringList(list
);
114 *iconname
= (char *)text_prop
.value
;
122 static void eatExpose(void)
126 /* compress all expose events into a single one */
128 if (XCheckMaskEvent(dpy
, ExposureMask
, &event
)) {
129 /* ignore other exposure events for this window */
130 while (XCheckWindowEvent(dpy
, event
.xexpose
.window
, ExposureMask
, &foo
)) ;
131 /* eat exposes for other windows */
134 event
.xexpose
.count
= 0;
135 XPutBackEvent(dpy
, &event
);
139 void move_window(Window win
, int from_x
, int from_y
, int to_x
, int to_y
)
142 if (wPreferences
.no_animations
)
143 XMoveWindow(dpy
, win
, to_x
, to_y
);
145 SlideWindow(win
, from_x
, from_y
, to_x
, to_y
);
147 XMoveWindow(dpy
, win
, to_x
, to_y
);
151 void SlideWindow(Window win
, int from_x
, int from_y
, int to_x
, int to_y
)
153 Window
*wins
[1] = { &win
};
154 SlideWindows(wins
, 1, from_x
, from_y
, to_x
, to_y
);
157 /* wins is an array of Window, sorted from left to right, the first is
158 * going to be moved from (from_x,from_y) to (to_x,to_y) and the
159 * following windows are going to be offset by (ICON_SIZE*i,0) */
160 void SlideWindows(Window
*wins
[], int n
, int from_x
, int from_y
, int to_x
, int to_y
)
162 time_t time0
= time(NULL
);
163 float dx
, dy
, x
= from_x
, y
= from_y
, px
, py
;
164 Bool is_dx_nul
, is_dy_nul
;
165 int dx_is_bigger
= 0, dx_int
, dy_int
;
166 int slide_delay
, slide_steps
, slide_slowdown
;
169 /* animation parameters */
170 static const struct {
175 {ICON_SLIDE_DELAY_UF
, ICON_SLIDE_STEPS_UF
, ICON_SLIDE_SLOWDOWN_UF
},
176 {ICON_SLIDE_DELAY_F
, ICON_SLIDE_STEPS_F
, ICON_SLIDE_SLOWDOWN_F
},
177 {ICON_SLIDE_DELAY_M
, ICON_SLIDE_STEPS_M
, ICON_SLIDE_SLOWDOWN_M
},
178 {ICON_SLIDE_DELAY_S
, ICON_SLIDE_STEPS_S
, ICON_SLIDE_SLOWDOWN_S
},
179 {ICON_SLIDE_DELAY_US
, ICON_SLIDE_STEPS_US
, ICON_SLIDE_SLOWDOWN_US
}
182 slide_slowdown
= apars
[(int)wPreferences
.icon_slide_speed
].slowdown
;
183 slide_steps
= apars
[(int)wPreferences
.icon_slide_speed
].steps
;
184 slide_delay
= apars
[(int)wPreferences
.icon_slide_speed
].delay
;
186 dx_int
= to_x
- from_x
;
187 dy_int
= to_y
- from_y
;
188 is_dx_nul
= (dx_int
== 0);
189 is_dy_nul
= (dy_int
== 0);
193 if (fabs(dx
) > fabs(dy
)) {
198 px
= dx
/ slide_slowdown
;
199 if (px
< slide_steps
&& px
> 0)
201 else if (px
> -slide_steps
&& px
< 0)
203 py
= (is_dx_nul
? 0.0 : px
* dy
/ dx
);
205 py
= dy
/ slide_slowdown
;
206 if (py
< slide_steps
&& py
> 0)
208 else if (py
> -slide_steps
&& py
< 0)
210 px
= (is_dy_nul
? 0.0 : py
* dx
/ dy
);
213 while (((int)x
) != to_x
||
217 if ((px
< 0 && (int)x
< to_x
) || (px
> 0 && (int)x
> to_x
))
219 if ((py
< 0 && (int)y
< to_y
) || (py
> 0 && (int)y
> to_y
))
223 px
= px
* (1.0 - 1 / (float)slide_slowdown
);
224 if (px
< slide_steps
&& px
> 0)
226 else if (px
> -slide_steps
&& px
< 0)
228 py
= (is_dx_nul
? 0.0 : px
* dy
/ dx
);
230 py
= py
* (1.0 - 1 / (float)slide_slowdown
);
231 if (py
< slide_steps
&& py
> 0)
233 else if (py
> -slide_steps
&& py
< 0)
235 px
= (is_dy_nul
? 0.0 : py
* dx
/ dy
);
238 for (i
= 0; i
< n
; i
++) {
239 XMoveWindow(dpy
, *wins
[i
], (int)x
+ i
* ICON_SIZE
, (int)y
);
242 if (slide_delay
> 0) {
243 wusleep(slide_delay
* 1000L);
247 if (time(NULL
) - time0
> MAX_ANIMATION_TIME
)
250 for (i
= 0; i
< n
; i
++) {
251 XMoveWindow(dpy
, *wins
[i
], to_x
+ i
* ICON_SIZE
, to_y
);
255 /* compress expose events */
259 char *ShrinkString(WMFont
*font
, const char *string
, int width
)
268 w
= WMWidthOfString(font
, string
, p
);
269 text
= wmalloc(strlen(string
) + 8);
270 strcpy(text
, string
);
274 pos
= strchr(text
, ' ');
276 pos
= strchr(text
, ':');
281 w1
= WMWidthOfString(font
, text
, p
);
298 width
-= WMWidthOfString(font
, "...", 3);
303 while (p2
> p1
&& p1
!= t
) {
304 w
= WMWidthOfString(font
, &string
[p
- t
], t
);
307 t
= p1
+ (p2
- p1
) / 2;
308 } else if (w
< width
) {
310 t
= p1
+ (p2
- p1
) / 2;
314 strcat(text
, &string
[p
- p1
]);
319 char *FindImage(const char *paths
, const char *file
)
321 char *tmp
, *path
= NULL
;
323 tmp
= strrchr(file
, ':');
326 path
= wfindfile(paths
, file
);
330 path
= wfindfile(paths
, file
);
335 static void timeoutHandler(void *data
)
340 static char *getTextSelection(WScreen
* screen
, Atom selection
)
374 data
= XFetchBuffer(dpy
, &size
, buffer
);
381 unsigned long len
, bytes
;
385 static Atom clipboard
= 0;
388 clipboard
= XInternAtom(dpy
, "CLIPBOARD", False
);
390 XDeleteProperty(dpy
, screen
->info_window
, clipboard
);
392 XConvertSelection(dpy
, selection
, XA_STRING
, clipboard
, screen
->info_window
, CurrentTime
);
394 timer
= WMAddTimerHandler(1000, timeoutHandler
, &timeout
);
396 while (!XCheckTypedWindowEvent(dpy
, screen
->info_window
, SelectionNotify
, &ev
) && !timeout
) ;
399 WMDeleteTimerHandler(timer
);
401 wwarning("selection retrieval timed out");
405 /* nobody owns the selection or the current owner has
406 * nothing to do with what we need */
407 if (ev
.xselection
.property
== None
) {
411 if (XGetWindowProperty(dpy
, screen
->info_window
,
413 False
, XA_STRING
, &rtype
, &bits
, &len
,
414 &bytes
, (unsigned char **)&data
) != Success
) {
417 if (rtype
!= XA_STRING
|| bits
!= 8) {
418 wwarning("invalid data in text selection");
427 static char *getselection(WScreen
* scr
)
431 tmp
= getTextSelection(scr
, XA_PRIMARY
);
433 tmp
= getTextSelection(scr
, XA_CUT_BUFFER0
);
438 parseuserinputpart(const char *line
, int *ptr
, const char *endchars
)
440 int depth
= 0, begin
;
444 while(line
[*ptr
] != '\0') {
445 if(line
[*ptr
] == '(') {
447 } else if(depth
> 0 && line
[*ptr
] == ')') {
449 } else if(depth
== 0 && strchr(endchars
, line
[*ptr
]) != NULL
) {
450 value
= wmalloc(*ptr
- begin
+ 1);
451 strncpy(value
, line
+ begin
, *ptr
- begin
);
452 value
[*ptr
- begin
] = '\0';
462 getuserinput(WScreen
*scr
, const char *line
, int *ptr
, Bool advanced
)
464 char *ret
= NULL
, *title
= NULL
, *prompt
= NULL
, *name
= NULL
;
467 if(line
[*ptr
] == '(')
468 title
= parseuserinputpart(line
, ptr
, ",)");
469 if(title
!= NULL
&& line
[*ptr
] == ',')
470 prompt
= parseuserinputpart(line
, ptr
, ",)");
471 if(prompt
!= NULL
&& line
[*ptr
] == ',')
472 name
= parseuserinputpart(line
, ptr
, ")");
475 rv
= wAdvancedInputDialog(scr
,
476 title
? _(title
):_("Program Arguments"),
477 prompt
? _(prompt
):_("Enter command arguments:"),
480 rv
= wInputDialog(scr
,
481 title
? _(title
):_("Program Arguments"),
482 prompt
? _(prompt
):_("Enter command arguments:"),
485 if(title
) wfree(title
);
486 if(prompt
) wfree(prompt
);
487 if(name
) wfree(name
);
489 return rv
? ret
: NULL
;
497 * state input new-state output
498 * NORMAL % OPTION <nil>
499 * NORMAL \ ESCAPE <nil>
500 * NORMAL etc. NORMAL <input>
501 * ESCAPE any NORMAL <input>
502 * OPTION s NORMAL <selection buffer>
503 * OPTION w NORMAL <selected window id>
504 * OPTION a NORMAL <input text>
505 * OPTION d NORMAL <OffiX DND selection object>
506 * OPTION W NORMAL <current workspace>
507 * OPTION etc. NORMAL %<input>
509 #define TMPBUFSIZE 64
510 char *ExpandOptions(WScreen
*scr
, const char *cmdline
)
512 int ptr
, optr
, state
, len
, olen
;
514 char *selection
= NULL
;
515 char *user_input
= NULL
;
517 char *dropped_thing
= NULL
;
519 char tmpbuf
[TMPBUFSIZE
];
522 len
= strlen(cmdline
);
526 wwarning(_("out of memory during expansion of \"%s\""), cmdline
);
530 ptr
= 0; /* input line pointer */
531 optr
= 0; /* output line pointer */
536 switch (cmdline
[ptr
]) {
545 out
[optr
++] = cmdline
[ptr
];
550 switch (cmdline
[ptr
]) {
564 out
[optr
++] = cmdline
[ptr
];
570 switch (cmdline
[ptr
]) {
572 if (scr
->focused_window
&& scr
->focused_window
->flags
.focused
) {
573 snprintf(tmpbuf
, sizeof(tmpbuf
), "0x%x",
574 (unsigned int)scr
->focused_window
->client_win
);
575 slen
= strlen(tmpbuf
);
577 nout
= realloc(out
, olen
);
579 wwarning(_("out of memory during expansion of \"%%w\""));
591 snprintf(tmpbuf
, sizeof(tmpbuf
), "0x%x", (unsigned int)scr
->current_workspace
+ 1);
592 slen
= strlen(tmpbuf
);
594 nout
= realloc(out
, olen
);
596 wwarning(_("out of memory during expansion of \"%%W\""));
607 user_input
= getuserinput(scr
, cmdline
, &ptr
, cmdline
[ptr
-1] == 'A');
609 slen
= strlen(user_input
);
611 nout
= realloc(out
, olen
);
613 wwarning(_("out of memory during expansion of \"%%a\""));
617 strcat(out
, user_input
);
620 /* Not an error, but user has Canceled the dialog box.
621 * This will make the command to not be performed. */
628 if (scr
->xdestring
) {
629 dropped_thing
= wstrdup(scr
->xdestring
);
631 if (!dropped_thing
) {
632 dropped_thing
= get_dnd_selection(scr
);
634 if (!dropped_thing
) {
635 scr
->flags
.dnd_data_convertion_status
= 1;
638 slen
= strlen(dropped_thing
);
640 nout
= realloc(out
, olen
);
642 wwarning(_("out of memory during expansion of \"%%d\""));
646 strcat(out
, dropped_thing
);
653 selection
= getselection(scr
);
656 wwarning(_("selection not available"));
659 slen
= strlen(selection
);
661 nout
= realloc(out
, olen
);
663 wwarning(_("out of memory during expansion of \"%%s\""));
667 strcat(out
, selection
);
673 out
[optr
++] = cmdline
[ptr
];
691 void ParseWindowName(WMPropList
*value
, char **winstance
, char **wclass
, const char *where
)
695 *winstance
= *wclass
= NULL
;
697 if (!WMIsPLString(value
)) {
698 wwarning(_("bad window name value in %s state info"), where
);
702 name
= WMGetFromPLString(value
);
703 if (!name
|| strlen(name
) == 0) {
704 wwarning(_("bad window name value in %s state info"), where
);
708 UnescapeWM_CLASS(name
, winstance
, wclass
);
712 static char *keysymToString(KeySym keysym
, unsigned int state
)
715 char *buf
= wmalloc(20);
720 kev
.send_event
= False
;
721 kev
.window
= DefaultRootWindow(dpy
);
722 kev
.root
= DefaultRootWindow(dpy
);
723 kev
.same_screen
= True
;
724 kev
.subwindow
= kev
.root
;
725 kev
.serial
= 0x12344321;
726 kev
.time
= CurrentTime
;
728 kev
.keycode
= XKeysymToKeycode(dpy
, keysym
);
729 count
= XLookupString(&kev
, buf
, 19, NULL
, NULL
);
736 char *GetShortcutString(const char *shortcut
)
745 tmp
= text
= wstrdup(shortcut
);
748 while ((k
= strchr(text
, '+')) != NULL
) {
752 mod
= wXModifierFromKey(text
);
754 return wstrdup("bug");
759 if (strcasecmp(text
, "Meta") == 0) {
760 buffer
= wstrappend(buffer
, "M+");
761 } else if (strcasecmp(text
, "Alt") == 0) {
762 buffer
= wstrappend(buffer
, "A+");
763 } else if (strcasecmp(text
, "Shift") == 0) {
764 buffer
= wstrappend(buffer
, "Sh+");
765 } else if (strcasecmp(text
, "Mod1") == 0) {
766 buffer
= wstrappend(buffer
, "M1+");
767 } else if (strcasecmp(text
, "Mod2") == 0) {
768 buffer
= wstrappend(buffer
, "M2+");
769 } else if (strcasecmp(text
, "Mod3") == 0) {
770 buffer
= wstrappend(buffer
, "M3+");
771 } else if (strcasecmp(text
, "Mod4") == 0) {
772 buffer
= wstrappend(buffer
, "M4+");
773 } else if (strcasecmp(text
, "Mod5") == 0) {
774 buffer
= wstrappend(buffer
, "M5+");
775 } else if (strcasecmp(text
, "Control") == 0) {
778 buffer
= wstrappend(buffer
, text
);
784 buffer
= wstrappend(buffer
, "^");
786 buffer
= wstrappend(buffer
, text
);
789 /* ksym = XStringToKeysym(text);
790 tmp = keysymToString(ksym, modmask);
792 buffer = wstrappend(buffer, tmp);
799 char *GetShortcutKey(WShortKey key
)
802 char *k
= XKeysymToString(XkbKeycodeToKeysym(dpy
, key
.keycode
, 0, 0));
805 char **m
= wPreferences
.modifier_labels
;
806 if (key
.modifier
& ControlMask
) tmp
= wstrappend(tmp
, m
[1] ? m
[1] : "Ctrl+");
807 if (key
.modifier
& ShiftMask
) tmp
= wstrappend(tmp
, m
[0] ? m
[0] : "Shift+");
808 if (key
.modifier
& Mod1Mask
) tmp
= wstrappend(tmp
, m
[2] ? m
[2] : "Mod1+");
809 if (key
.modifier
& Mod2Mask
) tmp
= wstrappend(tmp
, m
[3] ? m
[3] : "Mod2+");
810 if (key
.modifier
& Mod3Mask
) tmp
= wstrappend(tmp
, m
[4] ? m
[4] : "Mod3+");
811 if (key
.modifier
& Mod4Mask
) tmp
= wstrappend(tmp
, m
[5] ? m
[5] : "Mod4+");
812 if (key
.modifier
& Mod5Mask
) tmp
= wstrappend(tmp
, m
[6] ? m
[6] : "Mod5+");
813 tmp
= wstrappend(tmp
, k
);
815 return GetShortcutString(tmp
);
818 char *EscapeWM_CLASS(const char *name
, const char *class)
821 char *ename
= NULL
, *eclass
= NULL
;
829 ename
= wmalloc(l
* 2 + 1);
831 for (i
= 0; i
< l
; i
++) {
832 if (name
[i
] == '\\') {
834 } else if (name
[i
] == '.') {
837 ename
[j
++] = name
[i
];
843 eclass
= wmalloc(l
* 2 + 1);
845 for (i
= 0; i
< l
; i
++) {
846 if (class[i
] == '\\') {
848 } else if (class[i
] == '.') {
851 eclass
[j
++] = class[i
];
856 if (ename
&& eclass
) {
857 int len
= strlen(ename
) + strlen(eclass
) + 4;
859 snprintf(ret
, len
, "%s.%s", ename
, eclass
);
863 ret
= wstrdup(ename
);
866 ret
= wstrdup(eclass
);
873 static void UnescapeWM_CLASS(const char *str
, char **name
, char **class)
883 /* separate string in 2 parts */
885 for (i
= 0; i
< j
; i
++) {
886 if (str
[i
] == '\\') {
889 } else if (str
[i
] == '.') {
895 /* unescape strings */
896 for (i
= 0, k
= 0; i
< dot
; i
++) {
897 if (str
[i
] == '\\') {
900 (*name
)[k
++] = str
[i
];
905 for (i
= dot
+ 1, k
= 0; i
< j
; i
++) {
906 if (str
[i
] == '\\') {
909 (*class)[k
++] = str
[i
];
924 void SendHelperMessage(WScreen
*scr
, char type
, int workspace
, const char *msg
)
931 if (!scr
->flags
.backimage_helper_launched
) {
935 len
= (msg
? strlen(msg
) : 0) + (workspace
>= 0 ? 4 : 0) + 1;
936 buffer
= wmalloc(len
+ 5);
937 snprintf(buf
, sizeof(buf
), "%4i", len
);
938 memcpy(buffer
, buf
, 4);
941 if (workspace
>= 0) {
942 snprintf(buf
, sizeof(buf
), "%4i", workspace
);
943 memcpy(&buffer
[i
], buf
, 4);
948 strcpy(&buffer
[i
], msg
);
950 if (write(scr
->helper_fd
, buffer
, len
+ 4) < 0) {
951 werror(_("could not send message to background image helper"));
956 Bool
UpdateDomainFile(WDDomain
* domain
)
960 WMPropList
*shared_dict
, *dict
;
961 Bool result
, freeDict
= False
;
963 dict
= domain
->dictionary
;
964 if (WMIsPLDictionary(domain
->dictionary
)) {
965 /* retrieve global system dictionary */
966 snprintf(path
, sizeof(path
), "%s/WindowMaker/%s", SYSCONFDIR
, domain
->domain_name
);
967 if (stat(path
, &stbuf
) >= 0) {
968 shared_dict
= WMReadPropListFromFile(path
);
970 if (WMIsPLDictionary(shared_dict
)) {
972 dict
= WMDeepCopyPropList(domain
->dictionary
);
973 WMSubtractPLDictionaries(dict
, shared_dict
, True
);
975 WMReleasePropList(shared_dict
);
980 result
= WMWritePropListToFile(dict
, domain
->path
);
983 WMReleasePropList(dict
);
989 char *StrConcatDot(const char *a
, const char *b
)
999 len
= strlen(a
) + strlen(b
) + 4;
1002 snprintf(str
, len
, "%s.%s", a
, b
);
1007 static char *getCommandForWindow(Window win
, int elements
)
1009 char **argv
, *command
= NULL
;
1012 if (XGetCommand(dpy
, win
, &argv
, &argc
)) {
1013 if (argc
> 0 && argv
!= NULL
) {
1016 command
= wtokenjoin(argv
, WMIN(argc
, elements
));
1017 if (command
[0] == 0) {
1023 XFreeStringList(argv
);
1030 /* Free result when done */
1031 char *GetCommandForWindow(Window win
)
1033 return getCommandForWindow(win
, 0);