Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Hobbestigrou 2012-02-08 16:38:19 +01:00
commit 35209a860c
26 changed files with 1841 additions and 82 deletions

View File

@ -57,6 +57,9 @@ install: all
@echo installing default config file to ${DESTDIR}${XDG_CONFIG_DIR}/wmfs/
mkdir -p ${DESTDIR}${XDG_CONFIG_DIR}/wmfs/
install -m 444 wmfsrc ${DESTDIR}${XDG_CONFIG_DIR}/wmfs/
@echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1/
mkdir -p ${DESTDIR}${MANPREFIX}/man1/
install -m 644 wmfs.1 ${DESTDIR}${MANPREFIX}/man1/wmfs.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@ -65,6 +68,8 @@ uninstall:
@echo removing config file from ${DESTDIR}${XDG_CONFIG_DIR}/wmfs/
rm -f ${DESTDIR}${XDG_CONFIG_DIR}/wmfs/wmfsrc
rmdir ${DESTDIR}${XDG_CONFIG_DIR}/wmfs/
@echo removing manual pages from ${DESTDIR}${MANPREFIX}/man1
rm -f ${DESTDIR}${MANPREFIX}/man1/wmfs.1
dist:
@echo "Generate wmfs-`date +%Y%m`.tar.gz"

649
debian/wmfs.1 vendored
View File

@ -1,29 +1,626 @@
.TH WMFS: "1" "January 2012" "Window Manager From Scratch" "User Commands"
.SH NAME
.\" title: wmfs
.\" dev: xorg62
.\" man: arpinux
.\"
.TH "WMFS" "1" "2012/02/08" "wmfs" "manual of wmfs"
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.SH "NAME"
wmfs \- Window Manager From Scratch
.SH DESCRIPTION
usage: wmfs [\-hv] [\-c <func> <cmd] [\-C <file>]
.TP
\fB\-h\fR
Show this page
.TP
.SH "SYNOPSIS"
\fBwmfs\fR [\fB\-hv\fR] [\fB\-C <file>\fR] [\fB\-c <uicb_function> <cmd>\fR]
.sp
.SH "DESCRIPTION"
\fBWMFS\fR is a lightweight and highly configurable tiling window manager for X written in C\&.
.sp
.SH "OPTIONS"
.PP
\fB\-C <file>\fR
.RS 4
Load a configuration file\&.
.RE
.PP
\fB\-c <uicb_function> <cmd>\fR
.RS 4
Execute an uicb function to control WMFS\&.
.RE
.PP
\fB\-v\fR
Show WMFS version
.TP
\fB\-c\fR <func> <cmd>
Execute a specified UICB function
.TP
\fB\-C\fR <file>
Launch WMFS with a specified configuration file
.TP
.RS 4
Print version information to standard output, then exit\&.
.RE
.PP
\fB\-h\fR
Show this page
.TP
\fB\-v\fR
Show WMFS version
.TP
\fB\-c\fR <func> <cmd>
Execute a specified UICB function
.TP
\fB\-C\fR <file>
Launch WMFS with a specified configuration file
.RS 4
Print help information, then exit\&.
.RE
.SH "DEFAULT KEY BINDINGS"
.PP
\fBControl\-Alt + r\fR
.RS 4
Reload WMFS binary
.RE
.PP
\fBSuper + Return\fR
.RS 4
Run a terminal (urxvt by default)
.RE
.PP
\fBSuper + q\fR
.RS 4
Quit the selected client
.RE
.PP
\fBControl\-Alt + q\fR
.RS 4
Exit WMFS
.RE
.PP
\fBSuper + f \fR
.RS 4
Toggle free the selected client
.RE
.PP
\fBAlt + Tab\fR
.RS 4
Give the focus to the next client
.RE
.PP
\fBAlt\-Shift + Tab\fR
.RS 4
Give the focus to the previous client
.RE
.PP
\fBAlt + h\fR
.RS 4
Give the focus to the client on the left
.RE
.PP
\fBAlt + l\fR
.RS 4
Give the focus to the client on the right
.RE
.PP
\fBAlt + k\fR
.RS 4
Give the focus to the client on the top
.RE
.PP
\fBAlt + j\fR
.RS 4
Give the focus to the client on the bottom
.RE
.PP
\fBSuper + Tab\fR
.RS 4
Give the focus to the next tabbed client
.RE
.PP
\fBSuper\-Shift + Tab\fR
.RS 4
Give the focus to the previous tabbed client
.RE
.PP
\fBControl\-Shift + h\fR
.RS 4
Swap with the client on the left
.RE
.PP
\fBControl\-Shift + l\fR
.RS 4
Swap with the client on the right
.RE
.PP
\fBControl\-Shift + k\fR
.RS 4
Swap with the client on the top
.RE
.PP
\fBControl\-Shift + j\fR
.RS 4
Swap with the client on the bottom
.RE
.PP
\fBAlt\-Shift + h\fR
.RS 4
Tab in the client on the left
.RE
.PP
\fBAlt\-Shift + l\fR
.RS 4
Tab in the client on the right
.RE
.PP
\fBAlt\-Shift + k\fR
.RS 4
Tab in the client on the top
.RE
.PP
\fBAlt\-Shift + j\fR
.RS 4
Tab in the client on the bottom
.RE
.PP
\fBAlt\-Shift + u\fR
.RS 4
Untab the client
.RE
.PP
\fBSuper + h\fR
.RS 4
Increase the client to the left
.RE
.PP
\fBSuper + l\fR
.RS 4
Decrease the client from the left
.RE
.PP
\fBSuper + k\fR
.RS 4
Increase the client to the top
.RE
.PP
\fBSuper + j\fR
.RS 4
Decrease the client from the top
.RE
.PP
\fBSuper\-Control + h\fR
.RS 4
Decrease the client from the right
.RE
.PP
\fBSuper\-Control + l\fR
.RS 4
Increase the client to the right
.RE
.PP
\fBSuper\-Control + k\fR
.RS 4
Decrease the client from the bottom
.RE
.PP
\fBSuper\-Control + j\fR
.RS 4
Increase the client to the bottom
.RE
.PP
\fBControl + Right\fR
.RS 4
Next tag
.RE
.PP
\fBControl + Left\fR
.RS 4
Previous tag
.RE
.PP
\fBControl + Up\fR
.RS 4
Next screen
.RE
.PP
\fBControl + Down\fR
.RS 4
Previous screen
.RE
.PP
\fBSuper + m\fR
.RS 4
Vertical mirror layout
.RE
.PP
\fBSuper\-Shift + m\fR
.RS 4
Horizontal mirror layout
.RE
.PP
\fBSuper + r\fR
.RS 4
Rotate layout right
.RE
.PP
\fBSuper\-Shift + r\fR
.RS 4
Rotate layout left
.RE
.PP
\fBSuper\-Control\-Alt + h\fR
.RS 4
Integrate client in left layout
.RE
.PP
\fBSuper\-Control\-Alt + j\fR
.RS 4
Integrate client in bottom layout
.RE
.PP
\fBSuper\-Control\-Alt + k\fR
.RS 4
Integrate client in top layout
.RE
.PP
\fBSuper\-Control\-Alt + l\fR
.RS 4
Integrate client in right layout
.RE
.PP
\fBSuper + o\fR
.RS 4
Restore previous layout
.RE
.PP
\fBSuper\-Shift + o\fR
.RS 4
Restore next layout
.RE
.PP
\fBSuper + p\fR
.RS 4
Make a launcher in the statusbar to run an unix command\fR
.RE
.PP
\fBSuper + F[1\&.\&.9]\fR
.RS 4
Change tag view
.RE
.PP
\fBSuper\-Shift + F[1\&.\&.9]\fR
.RS 4
Transfert the selected client to the wanted tag
.RE
.PP
\fBSuper + -\fR
.RS 4
Delete current tag\fR
.RE
.PP
\fBSuper\-Shift + -\fR
.RS 4
Add current tag\fR
.RE
.SH "CONFIGURATION"
WMFS is configured by \fI$HOME/\&.config/wmfs/wmfsrc\fR\&.
.RE
.PP
\fB\ include\fR
wmfsrc supports ”@include” to split configuration file by section\&.
.RS 2
\fB\ Usage:\fR "@include ~/.config/wmfs/wmfs_themes"\&.
.RE
.PP
\fB\ [themes]\fR
wmfsrc supports themes for client and statusbar\&.
.RS 2
\fB Misc\fR
.RS 2
\fB\ name\fR
theme name: will be used in next sections\&.
.PP
\fB\ font\fR
theme font: in XLFD format\&.
.PP
.RE
\fB\ Bars\fR
.RS 2
\fB\ bars_width\fR
bar height in pixels\&.
.PP
\fB\ bars_fg/bg\fR
statusbar text/background color\&.
.PP
.RE
\fB\ Tags\fR
.RS 2
\fB\ tags_normal_fg/bg\fR
normal tag text/button color\&.
.PP
\fB\ tags_normal_statusline\fR
normal tag statusline\&.
.PP
\fB\ tags_sel_fg/bg\fR
selected tag text/button color\&.
.PP
\fB\ tags_sel_statusline\fR
selected tag statusline\&.
.PP
\fB\ tags_occupied_fg/bg\fR
occupied tag text/button color\&.
.PP
\fB\ tags_occupied_statusline\fR
occupied tag statusline\&.
.PP
\fB\ tags_urgent_fg/bg\fR
urgent tag text/button color\&.
.PP
\fB\ tags_urgent_statusline\fR
urgent tag statusline\&.
.PP
\fB\ tags_border_color\fR
tag button border color\&.
.PP
\fB\ tags_border_width\fR
tag button border width\&.
.PP
.RE
\fB\ Clients\fR
.RS 2
\fB\ client_normal_fg/bg\fR
normal client titlebar text/background color\&.
.PP
\fB\ client_normal_statusline\fR
normal client statusline\&.
.PP
\fB\ client_sel_fg/bg\fR
selected client titlebar text/background color\&.
.PP
\fB\ client_sel_statusline\fR
selected client statusline\&.
.PP
\fB\ frame_bg\fR
client border color\&.
.PP
\fB\ client_titlebar_width\fR
client titlebar height in pixels\&.
.PP
\fB\ client_border_width\fR
client border height in pixels\&.
.RE
.PP
.RE
\fB\ [bars]\fR
.RS 2
\fB\ position\fR
statusbar position on screen: 0=Top; 1=Bottom, 2=Hide\&.
.PP
\fB\ screen\fR
screen to display statusbar(start ar 0), set to\fB -1\fR to display on every screen\&.
.PP
\fB\ elements\fR
t=Tags, s=Statustext, y=Systray, l=Launcher\&.
.PP
\fB\ theme\fR
names of the statusbar theme\&.
.RE
.PP
.RE
\fB\ [tags]\fR
.RS 2
\fB\ screen\fR
screen to display tag. use no screen option or screen =\fB -1\fR to set tag on each screen\&.
.PP
\fB\ name\fR
display tagname\&.
.PP
\fB\ statusline\fR
draw a custom statusline in the specific tag (can display any sequences)\&.
.PP
\fB\ mousebinds\fR
mouse actions on the tag buttons\&.
.RE
.PP
.RE
\fB\ [client]\fR
.RS 2
\fB\ theme\fR
apply theme to client by default\&.
.PP
\fB\ key_modifier\fR
key modifier to perform actions on clients\&.
.PP
\fB\ mousebinds\fR
mouse actions on client\&.
.RE
.PP
.RE
\fB\ [rules]\fR
specific rules for clients: to identify an application, use xprop\&.
.RS 2
\fB\ instance\fR
first part of WM_CLASS\&.
.PP
\fB\ class\fR
second part of WM_CLASS\&.
.PP
\fB\ role\fR
WM_WINDOW_ROLE\&.
.PP
\fB\ name\fR
_NET_WM_NAME\&.
.PP
\fB\ theme\fR
apply theme to client\&.
.PP
\fB\ tag\fR
set tag to client(start at 0)\&.
.PP
\fB\ screen\fR
display client on a specific screen\&.
.PP
\fB\ free\fR
client in auto-free mode (true/false)\&.
.PP
\fB\ tab\fR
open client in a tab (true/false)\&.
.RE
.PP
.RE
\fB\ [launchers]\fR
.RS 2
\fB\ name\fR
launcher-name, will be used in the [keys] section\&.
.PP
\fB\ prompt\fR
display text at the beginning of the prompt\&.
.PP
\fB\ command\fR
command used by the launcher. can be an uicb function or an uicb function + extension\&.
.RE
.PP
.RE
\fB\ [keys]\fR
.RS 2
each line is contained within\fB\ [key]...[/key]\fR
.PP
\fB\ mod\fR
key modifier (Alt, Control, Shift, Super)\&.
.PP
\fB\ key\fR
key to press\&.
.PP
\fB\ func\fR
uicb function to launch\&.
.PP
\fB\ cmd\fR
if\fB\ func = "spawn"\fR set the external command to launch\&.
.sp
.SH "UICB Functions"
UICB functions list. for “User Interface Call Backs”\&.
.PP
\fB\ usage in the wmfsrc:\fR func = "tag_next"\fB\ or\fR func = "spawn" cmd = "urxvt -e vim"\&.
.RE
\fB\ usage in the status.sh:\fR wmfs -c status "<barname> ^s[<position>;<color>;next](1;tag_next)"\&.
.RE
\fB\ usage in your terminal:\fR wmfs -c tag_next\&.
.PP
\fB\ spawn\fR
launch a command. ex: func = "spawn" cmd = "urxvtc -e screen irssi"\&.
.PP
\fB\ quit\fR
quit wmfs\&.
.PP
\fB\ reload\fR
reload wmfs\&.
.PP
\fB\ tag_set\fR
set tag by number\&.
.PP
\fB\ tag\fR
set tag by name\&.
.PP
\fB\ tag_next/prev\fR
set next/previous tag\&.
.PP
\fB\ tag_client\fR
tag the client\&.
.PP
\fB\ tag_move_client_next/prev\fR
tag the client with next/previous tag\&.
.PP
\fB\ tag_click\fR
display tag with a clic on tag button\&.
.PP
\fB\ tag_new/del\fR
add/delete a tag\&.
.PP
\fB\ layout_vmirror\fR
vertical mirror tiling\&.
.PP
\fB\ layout_hmirror\fR
horizontal mirror tiling\&.
.PP
\fB\ layout_rotate_left\fR
tiling rotate anti/clockwise\&.
.PP
\fB\ layout_prev_set\fR
back to previous set layout\&.
.PP
\fB\ layout_next_set\fR
go to next set layout\&.
.PP
\fB\ layout_integrate_left/right/top/bottom\fR
client integration in the client zone by direction\&.
.PP
\fB\ client_close\fR
close the client\&.
.PP
\fB\ client_resize_right/left/top/bottom\fR
resize client with direction\&.
.PP
\fB\ client_focus_right/left/top/bottom\fR
focus client with direction\&.
.PP
\fB\ client_tab_right/left/top/bottom\fR
tab client with direction\&.
.PP
\fB\ client_swap_right/left/top/bottom\fR
swap client with direction\&.
.PP
\fB\ client_focus_next/prev\fR
move focus to the next/previous client\&.
.PP
\fB\ client_swap_next/prev\fR
swap with the next/previous client\&.
.PP
\fB\ client_untab\fR
untab the client\&.
.PP
\fB\ client_focus_next_tab\fR
move focus to next tab-client\&.
.PP
\fB\ client_focus_prev_tab\fR
move focus to previous tab-client\&.
.PP
\fB\ client_focus_click\fR
give focus to client with a clic\&.
.PP
\fB\ client_toggle_free\fR
togle free the client\&.
.PP
\fB\ client_tab_next_opened\fR
open the client in a tab\&.
.PP
\fB\ status\fR
display the argument text in the statusbar\&.
.PP
\fB\ status_surface\fR
display a surface. can contain sequences\&.
.PP
\fB\ mouse_resize\fR
resize the client\&.
.PP
\fB\ mouse_move\fR
move the client\&.
.PP
\fB\ mouse_swap\fR
swap the client\&.
.PP
\fB\ mouse_tab\fR
tab the client\&.
.PP
\fB\ screen_next/prev\fR
go to next/previous screen\&.
.PP
\fB\ screen_move_client_next/prev\fR
move the client to next/previous screen\&.
.PP
\fB\ launcher\fR
native prompt. ex:\fB\ func = "launcher" cmd = "exec"\fR display the “exec” launcher\&.
.RE
.PP
.sp
.SH "BUGS"
WMFS isn\'t stable for now\&. So it certainly contains some bugs\&.
.sp
.SH "AUTHORS"
Martin Duquesnoy <\fIxorg62@gmail\&.com\fR\&[1]>\&.
.sp
.SH "WWW"
Main site: \fIhttps://github\&.com/xorg62/wmfs\fR
.PP
Wiki: \fIhttps://github\&.com/xorg62/wmfs/wiki\fR
.PP
Bug tracker: \fIhttps://github\&.com/xorg62/wmfs/issues\fR
.sp
.SH "COPYING"
WMFS is under the BSD license\&. See COPYING for more information\&.
.sp
.SH "NOTES"
.IP " 1." 4
xorg62@gmail.com
.RS 4
\%mailto:xorg62@gmail.com
.RE

64
scripts/keybind_help.sh Executable file
View File

@ -0,0 +1,64 @@
#! /bin/sh
# simple help script for WMFS2 by arpinux
# default keybinds list
xpos="5"
ypos="5"
width="350"
height="730"
bg="#222222"
fg="#7D7D7D"
l01="^s[80;12;$bg;WMFS² Keybinds Help]"
l03="^s[15;35;$fg;launch terminal:]^s[190;35;$fg;Super + Return]"
l04="^s[15;50;$fg;launch prompt:]^s[190;50;$fg;Super + p]"
l05="^s[15;65;$fg;close client:]^s[190;65;$fg;Super + q]"
l06="^s[15;80;$fg;reload wmfs:]^s[190;80;$fg;Control + Alt + r]"
l07="^s[15;95;$fg;quit wmfs:]^s[190;95;$fg;Control + Alt + q]"
l08="^s[15;115;$fg;next client:]^s[190;115;$fg;Alt + Tab]"
l09="^s[15;130;$fg;prev client:]^s[190;130;$fg;Alt + Shift + Tab]"
l10="^s[15;145;$fg;next tabbed client:]^s[190;145;$fg;Super + Tab]"
l11="^s[15;160;$fg;prev tabbed client:]^s[190;160;$fg;Super + Shift + Tab]"
l12="^s[15;175;$fg;left client:]^s[190;175;$fg;Alt + h]"
l13="^s[15;190;$fg;right client:]^s[190;190;$fg;Alt + l]"
l14="^s[15;205;$fg;top client:]^s[190;205;$fg;Alt + k]"
l15="^s[15;220;$fg;bottom client:]^s[190;220;$fg;Alt + j]"
l16="^s[15;235;$fg;swap client left:]^s[190;235;$fg;Control + Shift + h]"
l17="^s[15;250;$fg;swap client right:]^s[190;250;$fg;Control + Shift + l]"
l18="^s[15;265;$fg;swap client top:]^s[190;265;$fg;Control + Shift + k]"
l19="^s[15;280;$fg;swap client bottom:]^s[190;280;$fg;Control + Shift + j]"
l20="^s[15;295;$fg;tab client left:]^s[190;295;$fg;Alt + Shift + h]"
l21="^s[15;310;$fg;tab client right:]^s[190;310;$fg;Alt + Shift + l]"
l22="^s[15;325;$fg;tab client top:]^s[190;325;$fg;Alt + Shift + k]"
l23="^s[15;340;$fg;tab client bottom:]^s[190;340;$fg;Alt + Shift + j]"
l24="^s[15;355;$fg;untab client:]^s[190;355;$fg;Alt + Shift + u]"
l25="^s[15;375;$fg;increase client on left:]^s[190;375;$fg;Super + h]"
l26="^s[15;390;$fg;decrease client from left:]^s[190;390;$fg;Super + l]"
l27="^s[15;405;$fg;increase client on top:]^s[190;405;$fg;Super + k]"
l28="^s[15;420;$fg;decrease client from top:]^s[190;420;$fg;Super + j]"
l29="^s[15;435;$fg;decrease client from right:]^s[190;435;$fg;Super + Control + h]"
l30="^s[15;450;$fg;increase client on right:]^s[190;450;$fg;Super + Control + l]"
l31="^s[15;465;$fg;decrease client from bottom:]^s[190;465;$fg;Super + Control + k]"
l32="^s[15;480;$fg;increase client to bottom:]^s[190;480;$fg;Super + Control + j]"
l33="^s[15;495;$fg;integrate client to left:]^s[190;495;$fg;Super + Control + Alt + h]"
l34="^s[15;510;$fg;integrate client to right:]^s[190;510;$fg;Super + Control + Alt + l]"
l35="^s[15;525;$fg;integrate client to top:]^s[190;525;$fg;Super + Control + Alt + k]"
l36="^s[15;540;$fg;integrate client to bottom:]^s[190;540;$fg;Super + Control + Alt + j]"
l37="^s[15;560;$fg;horizontal layout:]^s[190;560;$fg;Super + Shift + m]"
l38="^s[15;575;$fg;vertical layout:]^s[190;575;$fg;Super + m]"
l39="^s[15;590;$fg;layout rotate right:]^s[190;590;$fg;Super + r]"
l40="^s[15;605;$fg;layout rotate left:]^s[190;605;$fg;Super + Shift + r]"
l41="^s[15;620;$fg;toggle client free:]^s[190;620;$fg;Super + f]"
l42="^s[15;640;$fg;prev/next tag:]^s[190;640;$fg;Control + Left/Right]"
l43="^s[15;655;$fg;prev/next screen:]^s[190;655;$fg;Control + Up/Down]"
l44="^s[15;670;$fg;set tag (x):]^s[190;670;$fg;Super + F(x)]"
l45="^s[15;685;$fg;tag client with (x):]^s[190;685;$fg;Super + Shift + F(x)]"
l46="^s[15;700;$fg;add tag:]^s[190;700;$fg;Super + -]"
l47="^s[15;715;$fg;delete tag:]^s[190;715;$fg;Super + Shift + -]"
frame="^R[0;0;350;15;$fg] ^R[0;728;350;2;$fg] ^R[0;0;2;730;$fg] ^R[348;0;2;730;$fg]"
wmfs -c status_surface "$xpos,$ypos,$width,$height,$bg $frame $l01 $l03 $l04 $l05 $l06 $l07 $l08 $l09 $l10 $l11 $l12 $l13 $l14 $l15 $l16 $l17 $l18 $l19 $l20 $l21 $l22 $l23 $l24 $l25 $l26 $l27 $l28 $l29 $l30 $l31 $l32 $l33 $l34 $l35 $l36 $l37 $l38 $l39 $l40 $l41 $l42 $l43 $l44 $l45 $l46 $l47"

View File

@ -626,6 +626,8 @@ client_focus(struct client *c)
}
XSetInputFocus(W->dpy, c->win, RevertToPointerRoot, CurrentTime);
XChangeProperty(W->dpy, W->root, W->net_atom[net_active_window], XA_WINDOW, 32,
PropModeReplace, (unsigned char *)&c->win, 1);
}
else
{
@ -875,20 +877,21 @@ client_apply_rule(struct client *c)
c->theme = r->theme;
/* free = false for originally free client */
if(r->flags & RULE_FREE)
c->flags |= CLIENT_FREE;
/* free = false for originally free client */
else
c->flags &= ~CLIENT_FREE;
/* Free rule is not compatible with tab rule */
if(r->flags & RULE_TAB)
W->flags ^= WMFS_TABNOC; /* < can be disable by client_tab_next_opened */
/* TODO
if(r->flags & RULE_MAX)
{}
if(r->flags & RULE_IGNORE_TAG)
{}
*/
c->flags |= CLIENT_RULED;
}
flags = 0;
@ -969,6 +972,8 @@ client_new(Window w, XWindowAttributes *wa, bool scan)
ewmh_manage_window_type(c);
}
ewmh_get_client_list();
return c;
}
@ -1407,6 +1412,8 @@ client_remove(struct client *c)
XSetErrorHandler(wmfs_error_handler);
free(c);
ewmh_get_client_list();
}
void
@ -1431,6 +1438,14 @@ uicb_client_toggle_free(Uicb cmd)
}
}
void
uicb_client_tab_next_opened(Uicb cmd)
{
(void)cmd;
W->flags ^= WMFS_TABNOC;
}
void
client_free(void)
{

View File

@ -58,6 +58,7 @@ void client_update_props(struct client *c, Flags f);
void client_fac_hint(struct client *c);
void uicb_client_untab(Uicb cmd);
void uicb_client_toggle_free(Uicb cmd);
void uicb_client_tab_next_opened(Uicb cmd);
/* Generated */
void uicb_client_resize_Right(Uicb);

View File

@ -177,9 +177,10 @@ static void
config_tag(void)
{
struct screen *s;
struct tag *t;
size_t i, n;
struct conf_sec *sec, **ks, **mb;
char *name;
char *name, *tmp;
int screenid;
/* [tags] */
@ -202,7 +203,14 @@ config_tag(void)
SLIST_FOREACH(s, &W->h.screen, next)
if(screenid == s->id || screenid == -1)
tag_new(s, name);
{
t = tag_new(s, name);
t->statusctx = status_new_ctx(NULL, NULL);
ISTRDUP(t->statusctx.status, fetch_opt_first(ks[i], "", "statusline").str);
if(t->statusctx.status)
status_parse(&t->statusctx);
}
}
/* If no tag at all on a screen, add one anyway */
@ -264,7 +272,7 @@ config_rule(void)
r->tag = fetch_opt_first(ks[i], "-1", "tag").num;
FLAGAPPLY(r->flags, fetch_opt_first(ks[i], "false", "free").boolean, RULE_FREE);
FLAGAPPLY(r->flags, fetch_opt_first(ks[i], "false", "max").boolean, RULE_MAX);
FLAGAPPLY(r->flags, fetch_opt_first(ks[i], "false", "tab").boolean, RULE_TAB);
FLAGAPPLY(r->flags, fetch_opt_first(ks[i], "false", "ignore_tag").boolean, RULE_IGNORE_TAG);
if((tn = fetch_opt_first(ks[i], "", "theme").str))

View File

@ -38,6 +38,8 @@ static const struct { char *name; void (*func)(Uicb cmd); } uicb_list[] =
{ "tag_move_client_next", uicb_tag_move_client_next },
{ "tag_move_client_prev", uicb_tag_move_client_prev },
{ "tag_click", uicb_tag_click },
{ "tag_new", uicb_tag_new },
{ "tag_del", uicb_tag_del },
/* Layout */
{ "layout_vmirror", uicb_layout_vmirror },
@ -78,9 +80,11 @@ static const struct { char *name; void (*func)(Uicb cmd); } uicb_list[] =
{ "client_focus_prev_tab", uicb_client_focus_prev_tab },
{ "client_focus_click", uicb_client_focus_click },
{ "client_toggle_free", uicb_client_toggle_free },
{ "client_tab_next_opened", uicb_client_tab_next_opened },
/* Status */
{ "status" , uicb_status },
{ "status" , uicb_status },
{ "status_surface", uicb_status_surface },
/* Mouse */
{ "mouse_resize", uicb_mouse_resize },

View File

@ -83,6 +83,12 @@ draw_reversed_rect(Drawable dr, struct client *c, bool t)
g->h - (i << 1));
}
static inline void
draw_line(Drawable d, int x1, int y1, int x2, int y2)
{
XDrawLine(W->dpy, d, W->gc, x1, y1, x2, y2);
}
static inline unsigned short
draw_textw(struct theme *t, const char *str)
{

View File

@ -29,6 +29,7 @@ event_buttonpress(XEvent *e)
struct barwin *b;
screen_update_sel();
status_flush_surface();
SLIST_FOREACH(b, &W->h.barwin, next)
if(b->win == ev->window)
@ -325,6 +326,7 @@ event_keypress(XEvent *e)
struct keybind *k;
screen_update_sel();
status_flush_surface();
SLIST_FOREACH(k, &W->h.keybind, next)
if(k->keysym == keysym && KEYPRESS_MASK(k->mod) == KEYPRESS_MASK(ev->state))

View File

@ -111,6 +111,31 @@ ewmh_set_wm_state(Window w, int state)
W->net_atom[wm_state], 32, PropModeReplace, d, 2);
}
/*
* _NET_CLIENT_LIST
*/
void
ewmh_get_client_list(void)
{
Window *list;
struct client *c;
int win_n = 0;
SLIST_FOREACH(c, &W->h.client, next)
++win_n;
list = xcalloc(win_n, sizeof(Window));
win_n = 0;
SLIST_FOREACH(c, &W->h.client, next)
list[win_n++] = c->win;
XChangeProperty(W->dpy, W->root, W->net_atom[net_client_list], XA_WINDOW, 32,
PropModeReplace, (unsigned char *)list, win_n);
XFree(list);
}
/*
* Get xembed state
*/
@ -168,30 +193,29 @@ void
ewmh_manage_state(long data[], struct client *c)
{
/* _NET_WM_STATE_FULLSCREEN */
if(data[1] == (long)W->net_atom[net_wm_state_fullscreen])
if(data[1] == (long)W->net_atom[net_wm_state_fullscreen]
|| data[2] == (long)W->net_atom[net_wm_state_fullscreen])
{
if(data[0] == _NET_WM_STATE_ADD
|| (data[0] == _NET_WM_STATE_TOGGLE && !(c->flags & CLIENT_FULLSCREEN)))
{
c->flags |= CLIENT_FULLSCREEN;
XChangeProperty(W->dpy, c->win, W->net_atom[net_wm_state], XA_ATOM, 32, PropModeReplace,
(unsigned char*)&W->net_atom[net_wm_state_fullscreen], 1);
XReparentWindow(W->dpy, c->win, W->root, c->screen->geo.x, c->screen->geo.y);
XResizeWindow(W->dpy, c->win, c->screen->geo.w, c->screen->geo.h);
XChangeProperty(W->dpy, c->win, W->net_atom[net_wm_state], XA_ATOM, 32, PropModeReplace,
(unsigned char*)&W->net_atom[net_wm_state_fullscreen], true);
client_focus(c);
XRaiseWindow(W->dpy, c->win);
}
else if(data[0] == _NET_WM_STATE_REMOVE
|| (data[0] == _NET_WM_STATE_TOGGLE && c->flags & CLIENT_FULLSCREEN))
else
{
c->flags &= ~CLIENT_FULLSCREEN;
XReparentWindow(W->dpy, c->win, c->frame, c->wgeo.x, c->wgeo.y);
XChangeProperty(W->dpy, c->win, W->net_atom[net_wm_state], XA_ATOM, 32, PropModeReplace,
(unsigned char*)&W->net_atom[net_wm_state_fullscreen], false);
(unsigned char*)0, 0);
XReparentWindow(W->dpy, c->win, c->frame, c->wgeo.x, c->wgeo.y);
client_moveresize(c, &c->geo);
}

View File

@ -118,6 +118,7 @@ ewmh_send_message(Window d, Window w, char *atom, long d0, long d1, long d2, lon
void ewmh_init(void);
void ewmh_set_wm_state(Window w, int state);
void ewmh_get_client_list(void);
long ewmh_get_xembed_state(Window win);
void ewmh_update_wmfs_props(void);
void ewmh_manage_state(long data[], struct client *c);

View File

@ -12,6 +12,14 @@
#include "status.h"
#include "systray.h"
#define ELEM_FREE_BARWIN(e) \
while(!SLIST_EMPTY(&e->bars)) \
{ \
b = SLIST_FIRST(&e->bars); \
SLIST_REMOVE_HEAD(&e->bars, enext); \
barwin_remove(b); \
}
static void infobar_elem_tag_init(struct element *e);
static void infobar_elem_tag_update(struct element *e);
static void infobar_elem_status_init(struct element *e);
@ -53,9 +61,16 @@ infobar_elem_tag_init(struct element *e)
e->geo.h -= (e->infobar->theme->tags_border_width << 1);
e->statusctx = &e->infobar->theme->tags_n_sl;
e->statusctx->flags |= STATUS_BLOCK_REFRESH;
if(SLIST_EMPTY(&e->bars))
if(SLIST_EMPTY(&e->bars) || (e->infobar->screen->flags & SCREEN_TAG_UPDATE))
{
if((e->infobar->screen->flags & SCREEN_TAG_UPDATE))
{
ELEM_FREE_BARWIN(e);
SLIST_INIT(&e->bars);
}
TAILQ_FOREACH(t, &e->infobar->screen->tags, next)
{
s = draw_textw(e->infobar->theme, t->name) + PAD;
@ -63,6 +78,10 @@ infobar_elem_tag_init(struct element *e)
/* Init barwin */
b = barwin_new(e->infobar->bar->win, j, 0, s, e->geo.h, 0, 0, false);
/* Status doesn't have theme yet */
t->statusctx.theme = e->infobar->theme;
t->statusctx.flags |= STATUS_BLOCK_REFRESH;
/* Set border */
if(e->infobar->theme->tags_border_width)
{
@ -140,6 +159,10 @@ infobar_elem_tag_update(struct element *e)
status_copy_mousebind(e->statusctx);
status_render(e->statusctx);
t->statusctx.barwin = b;
status_copy_mousebind(&t->statusctx);
status_render(&t->statusctx);
draw_text(b->dr, e->infobar->theme, (PAD >> 1),
TEXTY(e->infobar->theme, e->geo.h), b->fg, t->name);
@ -380,12 +403,7 @@ infobar_elem_remove(struct element *e)
TAILQ_REMOVE(&e->infobar->elements, e, next);
while(!SLIST_EMPTY(&e->bars))
{
b = SLIST_FIRST(&e->bars);
SLIST_REMOVE_HEAD(&e->bars, enext);
barwin_remove(b);
}
ELEM_FREE_BARWIN(e);
free(e);
}

View File

@ -389,10 +389,19 @@ layout_split_integrate(struct client *c, struct client *sc)
{
client_maximize(c);
c->flags |= CLIENT_TILED;
W->flags &= ~WMFS_TABNOC;
return;
}
}
/* Tab Next Opened Client */
if(W->flags & WMFS_TABNOC)
{
W->flags ^= WMFS_TABNOC;
_client_tab(c, sc);
return;
}
c->flags |= CLIENT_TILED;
g = layout_split(sc, (sc->geo.h < sc->geo.w));

View File

@ -7,6 +7,7 @@
#define LAYOUT_H
#include "wmfs.h"
#include "client.h"
/* Check lateral direction (if p is Right or Left) */
#define LDIR(P) (P < Top)

View File

@ -26,7 +26,7 @@ mouse_resize(struct client *c)
int d, u, ox, oy, ix, iy;
int mx, my;
XQueryPointer(W->dpy, W->root, &w, &w, &ox, &oy, &d, &d, (uint *)&u);
XQueryPointer(W->dpy, W->root, &w, &w, &ox, &oy, &d, &d, (unsigned int *)&u);
XGrabServer(W->dpy);
if(c->flags & CLIENT_FREE)

View File

@ -21,6 +21,7 @@ screen_new(struct geo *g, int id)
s->geo = s->ugeo = *g;
s->seltag = NULL;
s->id = id;
s->flags = 0;
TAILQ_INIT(&s->tags);
SLIST_INIT(&s->infobars);

View File

@ -40,10 +40,26 @@ status_new_ctx(struct barwin *b, struct theme *t)
struct status_ctx ctx = { .barwin = b, .theme = t };
SLIST_INIT(&ctx.statushead);
SLIST_INIT(&ctx.gcache);
return ctx;
}
static void
status_gcache_free(struct status_ctx *ctx)
{
struct status_gcache *gc;
while(!SLIST_EMPTY(&ctx->gcache))
{
gc = SLIST_FIRST(&ctx->gcache);
SLIST_REMOVE_HEAD(&ctx->gcache, next);
free(gc->datas);
free(gc->name);
free(gc);
}
}
void
status_free_ctx(struct status_ctx *ctx)
{
@ -53,6 +69,62 @@ status_free_ctx(struct status_ctx *ctx)
SLIST_INIT(&ctx->barwin->statusmousebinds);
status_flush_list(ctx);
status_gcache_free(ctx);
}
static void
status_graph_draw(struct status_ctx *ctx, struct status_seq *sq, struct status_gcache *gc)
{
int i, j, y;
int ys = sq->geo.y + sq->geo.h - 1;
XSetForeground(W->dpy, W->gc, sq->color2);
for(i = sq->geo.x + sq->geo.w - 1, j = gc->ndata - 1;
j >= 0 && i >= sq->geo.x;
--j, --i)
{
/* You divided by zero didn't you? */
if(gc->datas[j])
{
y = ys - (sq->geo.h / ((float)sq->data[2] / (float)gc->datas[j])) + 1;
draw_line(ctx->barwin->dr, i, y, i, ys);
}
}
}
static void
status_graph_process(struct status_ctx *ctx, struct status_seq *sq, char *name)
{
int j;
struct status_gcache *gc;
/* Graph already exist and have a cache */
SLIST_FOREACH(gc, &ctx->gcache, next)
if(!strcmp(name, gc->name))
{
/* shift buffer to remove unused old value */
if(gc->ndata > (sq->geo.w << 1))
for(gc->ndata /= 2, j = 0;
j < gc->ndata;
gc->datas[j] = gc->datas[j + gc->ndata], ++j);
gc->datas[gc->ndata++] = sq->data[1];
status_graph_draw(ctx, sq, gc);
return;
}
/* No? Make a cache for it */
gc = xcalloc(1, sizeof(struct status_gcache));
gc->name = xstrdup(name);
gc->ndata = 1;
gc->datas = xcalloc(sq->geo.w + sq->geo.w, sizeof(int));
gc->datas[0] = sq->data[1];
SLIST_INSERT_HEAD(&ctx->gcache, gc, next);
status_graph_draw(ctx, sq, gc);
}
/* Parse mousebind sequence next normal sequence: \<seq>[](button;func;cmd) */
@ -90,9 +162,9 @@ void
status_parse(struct status_ctx *ctx)
{
struct status_seq *sq, *prev = NULL;
int i, shift = 0;
int i, tmp, shift = 0;
char *dstr = xstrdup(ctx->status), *sauv = dstr;
char type, *p, *pp, *end, *arg[6] = { NULL };
char type, *p, *pp, *end, *arg[10] = { NULL };
for(; *dstr; ++dstr)
{
@ -107,7 +179,7 @@ status_parse(struct status_ctx *ctx)
while(*(end - 1) == '\\')
end = strchr(end + 1, ']');
if(!(strchr("sRi", *p)) || !end)
if(!(strchr("sRpPig", *p)) || !end)
continue;
/* Then parse & list it */
@ -144,6 +216,49 @@ status_parse(struct status_ctx *ctx)
break;
/*
* Progress bar sequence: \p[left/right;w;h;bord;val;valmax;bg;fg] OR x;y
* Position bar sequence: \P[left/right;w;h;tickbord;val;valmax;bg;fg] OR x;y
*/
case 'p':
case 'P':
i = parse_args(p + 2, ';', ']', 9, arg);
STATUS_CHECK_ARGS(i, 7, 8, dstr, end);
sq = status_new_seq(type, i, 7, arg, &shift);
sq->geo.w = ATOI(arg[1 + shift]);
sq->geo.h = ATOI(arg[2 + shift]);
sq->data[0] = ATOI(arg[3 + shift]); /* Border */
sq->data[1] = ((tmp = ATOI(arg[4 + shift])) ? tmp : 1); /* Value */
sq->data[2] = ATOI(arg[5 + shift]); /* Value Max */
sq->color = color_atoh(arg[6 + shift]);
sq->color2 = color_atoh(arg[7 + shift]);
break;
/*
* Graph sequence: \g[left/right;w;h;val;valmax;bg;fg;name] OR x;y
*/
case 'g':
i = parse_args(p + 2, ';', ']', 9, arg);
STATUS_CHECK_ARGS(i, 7, 8, dstr, end);
sq = status_new_seq(type, i, 7, arg, &shift);
sq->geo.w = ATOI(arg[1 + shift]);
sq->geo.h = ATOI(arg[2 + shift]);
sq->data[1] = ATOI(arg[3 + shift]); /* Value */
sq->data[2] = ATOI(arg[4 + shift]); /* Value Max */
sq->color = color_atoh(arg[5 + shift]);
sq->color2 = color_atoh(arg[6 + shift]);
sq->str = xstrdup(arg[7 + shift]);
break;
/*
* Image sequence: \i[left/right;w;h;/path/img] OR \i[x;y;w;h;/path/img]
*/
@ -161,7 +276,7 @@ status_parse(struct status_ctx *ctx)
#endif /* HAVE_IMLIB2 */
}
if (sq->align == Right)
if(sq->align == Right)
SLIST_INSERT_HEAD(&ctx->statushead, sq, next);
else
SLIST_INSERT_TAIL(&ctx->statushead, sq, next, prev);
@ -195,11 +310,21 @@ status_parse(struct status_ctx *ctx)
sq->geo.x = ctx->barwin->geo.w - right - sq->geo.w; \
right += sq->geo.w; \
}
#define STORE_MOUSEBIND() \
if(!SLIST_EMPTY(&sq->mousebinds)) \
SLIST_FOREACH(m, &sq->mousebinds, snext) \
m->area = sq->geo;
#define NOALIGN_Y() \
if(sq->align != NoAlign) \
sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
static void
status_apply_list(struct status_ctx *ctx)
{
struct status_seq *sq;
struct mousebind *m;
struct geo g;
int left = 0, right = 0, w, h;
SLIST_FOREACH(sq, &ctx->statushead, next)
@ -229,16 +354,71 @@ status_apply_list(struct status_ctx *ctx)
/* Rectangle */
case 'R':
if(sq->align != NoAlign)
sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
NOALIGN_Y();
STATUS_ALIGN(sq->align);
draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
if(!SLIST_EMPTY(&sq->mousebinds))
SLIST_FOREACH(m, &sq->mousebinds, snext)
m->area = sq->geo;
STORE_MOUSEBIND();
break;
/* Progress */
case 'p':
NOALIGN_Y();
STATUS_ALIGN(sq->align);
draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
/* Progress bar geo */
g.x = sq->geo.x + sq->data[0];
g.y = sq->geo.y + sq->data[0];
g.w = sq->geo.w - sq->data[0] - sq->data[0];
g.h = sq->geo.h - sq->data[0] - sq->data[0];
if(sq->geo.w > sq->geo.h)
g.w /= ((float)sq->data[2] / (float)sq->data[1]);
else
{
g.y += g.h;
g.h /= ((float)sq->data[2] / (float)sq->data[1]);
g.y -= g.h;
}
draw_rect(ctx->barwin->dr, &g, sq->color2);
STORE_MOUSEBIND();
break;
/* Position */
case 'P':
NOALIGN_Y();
STATUS_ALIGN(sq->align);
draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
g.x = sq->geo.x + ((sq->geo.w - sq->data[0]) / ((float)sq->data[2] / (float)sq->data[1]));
g.y = sq->geo.y;
g.w = sq->data[0];
g.h = sq->geo.h;
draw_rect(ctx->barwin->dr, &g, sq->color2);
STORE_MOUSEBIND();
break;
/* Graph */
case 'g':
NOALIGN_Y();
STATUS_ALIGN(sq->align);
draw_rect(ctx->barwin->dr, &sq->geo, sq->color);
status_graph_process(ctx, sq, sq->str);
STORE_MOUSEBIND();
break;
@ -256,12 +436,8 @@ status_apply_list(struct status_ctx *ctx)
sq->geo.y = (ctx->barwin->geo.h >> 1) - (sq->geo.h >> 1);
STATUS_ALIGN(sq->align);
draw_image(ctx->barwin->dr, &sq->geo);
if(!SLIST_EMPTY(&sq->mousebinds))
SLIST_FOREACH(m, &sq->mousebinds, snext)
m->area = sq->geo;
STORE_MOUSEBIND();
break;
#endif /* HAVE_IMLIB2 */
@ -277,7 +453,8 @@ status_render(struct status_ctx *ctx)
if(!ctx->status)
return;
barwin_refresh_color(ctx->barwin);
if(!(ctx->flags & STATUS_BLOCK_REFRESH))
barwin_refresh_color(ctx->barwin);
/* Use simple text instead sequence if no sequence found */
if(SLIST_EMPTY(&ctx->statushead))
@ -351,6 +528,78 @@ status_manage(struct status_ctx *ctx)
status_copy_mousebind(ctx);
}
void
status_flush_surface(void)
{
struct barwin *b;
while(!SLIST_EMPTY(&W->h.vbarwin))
{
b = SLIST_FIRST(&W->h.vbarwin);
SLIST_REMOVE_HEAD(&W->h.vbarwin, vnext);
barwin_remove(b);
}
}
static void
status_surface(int x, int y, int w, int h, Color bg, char *status)
{
struct barwin *b;
struct screen *s;
struct status_ctx ctx;
int d;
if(!status)
return;
if(x + y < 0)
XQueryPointer(W->dpy, W->root, (Window*)&d, (Window*)&d, &x, &y, &d, &d, (unsigned int *)&d);
s = screen_gb_geo(x, y);
if(x + w > s->geo.x + s->geo.w)
x -= w;
if(y + h > s->geo.y + s->geo.h)
y -= h;
b = barwin_new(W->root, x, y, w, h, 0, bg, false);
barwin_map(b);
/* Use client theme */
ctx = status_new_ctx(b, W->ctheme);
ctx.status = xstrdup(status);
SLIST_INSERT_HEAD(&W->h.vbarwin, b, vnext);
status_manage(&ctx);
status_free_ctx(&ctx);
}
void
uicb_status_surface(Uicb cmd)
{
char *p, *ccmd = xstrdup(cmd);
int s, w, h, x = -1, y = -1;
Color bg;
if(!ccmd || !(p = strchr(ccmd, ' ')))
return;
*p = '\0';
++p;
if(!(((s = sscanf(ccmd, "%d,%d,#%x", &w, &h, &bg)) == 3)
|| (s = sscanf(ccmd, "%d,%d,%d,%d,#%x", &x, &y, &w, &h, &bg)) == 5))
{
free(ccmd);
return;
}
status_surface(x, y, w, h, bg, p);
free(ccmd);
}
/* Syntax: "<infobar name> <status string>" */
void
uicb_status(Uicb cmd)

View File

@ -16,6 +16,8 @@ void status_copy_mousebind(struct status_ctx *ctx);
void status_parse(struct status_ctx *ctx);
void status_render(struct status_ctx *ctx);
void status_manage(struct status_ctx *ctx);
void status_flush_surface(void);
void uicb_status(Uicb cmd);
void uicb_status_surface(Uicb cmd);
#endif /* STATUS_H */

View File

@ -72,7 +72,7 @@ systray_add(Window win)
s->geo.h = W->systray.barwin->geo.h;
s->geo.w = W->systray.barwin->geo.h + SYSTRAY_SPACING;
ewmh_set_wm_state(s->win, WithdrawnState);
ewmh_set_wm_state(s->win, NormalState);
XSelectInput(W->dpy, s->win, StructureNotifyMask | PropertyChangeMask| EnterWindowMask | FocusChangeMask);
XReparentWindow(W->dpy, s->win, W->systray.win, 0, 0);

View File

@ -22,7 +22,6 @@ tag_new(struct screen *s, char *name)
t = xcalloc(1, sizeof(struct tag));
t->screen = s;
t->name = xstrdup(name);
t->flags = 0;
t->id = 0;
t->sel = NULL;
@ -31,6 +30,11 @@ tag_new(struct screen *s, char *name)
if((l = TAILQ_LAST(&s->tags, tsub)))
t->id = l->id + 1;
if(!name || !strlen(name))
xasprintf(&t->name, "%d", t->id + 1);
else
t->name = xstrdup(name);
SLIST_INIT(&t->clients);
TAILQ_INIT(&t->sets);
@ -42,7 +46,7 @@ tag_new(struct screen *s, char *name)
void
tag_screen(struct screen *s, struct tag *t)
{
if(t == s->seltag)
if(t == s->seltag && TAILQ_NEXT(TAILQ_FIRST(&s->tags), next))
t = t->prev;
if(!t)
@ -88,6 +92,8 @@ tag_client(struct tag *t, struct client *c)
c->flags &= ~CLIENT_RULED;
infobar_elem_screen_update(c->screen, ElemTag);
/* Client remove */
if(!t)
return;
@ -100,10 +106,6 @@ tag_client(struct tag *t, struct client *c)
SLIST_INSERT_HEAD(&t->clients, c, tnext);
infobar_elem_screen_update(t->screen, ElemTag);
layout_client(c);
if(c->flags & CLIENT_TABMASTER && c->prevtag)
{
struct client *cc;
@ -116,6 +118,8 @@ tag_client(struct tag *t, struct client *c)
}
}
layout_client(c);
if(t != c->screen->seltag || c->flags & CLIENT_TABBED)
client_unmap(c);
}
@ -177,7 +181,7 @@ uicb_tag_client(Uicb cmd)
struct tag *t;
int id = ATOI(cmd);
if((t = tag_gb_id(W->screen, id)))
if(W->client && (t = tag_gb_id(W->screen, id)))
tag_client(t, W->client);
}
@ -187,6 +191,9 @@ uicb_tag_move_client_next(Uicb cmd)
(void)cmd;
struct tag *t;
if(!W->client)
return;
if((t = TAILQ_NEXT(W->screen->seltag, next)))
tag_client(t, W->client);
else if( /* CIRCULAR OPTION */ 1)
@ -199,6 +206,9 @@ uicb_tag_move_client_prev(Uicb cmd)
(void)cmd;
struct tag *t;
if(!W->client)
return;
if((t = TAILQ_PREV(W->screen->seltag, tsub, next)))
tag_client(t, W->client);
else if( /* CIRCULAR OPTION */ 1)
@ -219,6 +229,8 @@ uicb_tag_click(Uicb cmd)
static void
tag_remove(struct tag *t)
{
TAILQ_REMOVE(&t->screen->tags, t, next);
free(t->name);
layout_free_set(t);
@ -226,14 +238,49 @@ tag_remove(struct tag *t)
free(t);
}
void
uicb_tag_new(Uicb cmd)
{
struct screen *s = W->screen;
struct infobar *i;
tag_new(s, (char*)cmd);
s->flags |= SCREEN_TAG_UPDATE;
SLIST_FOREACH(i, &s->infobars, next)
infobar_elem_reinit(i);
s->flags ^= SCREEN_TAG_UPDATE;
}
void
uicb_tag_del(Uicb cmd)
{
struct infobar *i;
struct tag *t = W->screen->seltag;
(void)cmd;
if(SLIST_EMPTY(&t->clients)
&& TAILQ_NEXT(TAILQ_FIRST(&W->screen->tags), next))
{
tag_screen(W->screen, TAILQ_NEXT(t, next));
tag_remove(t);
W->screen->flags |= SCREEN_TAG_UPDATE;
SLIST_FOREACH(i, &W->screen->infobars, next)
infobar_elem_reinit(i);
W->screen->flags ^= SCREEN_TAG_UPDATE;
}
}
void
tag_free(struct screen *s)
{
struct tag *t;
TAILQ_FOREACH(t, &s->tags, next)
{
TAILQ_REMOVE(&s->tags, t, next);
tag_remove(t);
}
}

View File

@ -32,5 +32,7 @@ void uicb_tag_client(Uicb cmd);
void uicb_tag_move_client_next(Uicb cmd);
void uicb_tag_move_client_prev(Uicb cmd);
void uicb_tag_click(Uicb cmd);
void uicb_tag_new(Uicb cmd);
void uicb_tag_del(Uicb cmd);
#endif /* TAG_H */

View File

@ -149,7 +149,7 @@ parse_args(char *str, char delim, char end, int narg, char *args[])
int i = 0;
for(args[0] = str; *str && (*str != end || *(str - 1) == '\\') && i < narg; ++str)
if(*str == delim)
if(*str == delim && i < narg - 1)
{
*str = '\0';
args[++i] = ++str;

View File

@ -160,6 +160,7 @@ wmfs_xinit(void)
* Barwin linked list
*/
SLIST_INIT(&W->h.barwin);
SLIST_INIT(&W->h.vbarwin);
/*
* Optional dep init

View File

@ -95,15 +95,17 @@ struct barwin
SLIST_HEAD(, mousebind) statusmousebinds;
SLIST_ENTRY(barwin) next; /* global barwin */
SLIST_ENTRY(barwin) enext; /* element barwin */
SLIST_ENTRY(barwin) vnext; /* volatile barwin */
};
struct status_seq
{
struct geo geo;
enum position align;
int data[4];
char type;
char *str;
Color color;
Color color, color2;
SLIST_HEAD(, mousebind) mousebinds;
SLIST_ENTRY(status_seq) next;
};
@ -112,11 +114,22 @@ struct status_ctx
{
struct barwin *barwin;
struct theme *theme;
#define STATUS_BLOCK_REFRESH 0x01
Flags flags;
char *status;
bool update;
SLIST_HEAD(, status_gcache) gcache;
SLIST_HEAD(, status_seq) statushead;
};
struct status_gcache
{
char *name;
int *datas;
int ndata;
SLIST_ENTRY(status_gcache) next;
};
struct element
{
struct geo geo;
@ -149,6 +162,8 @@ struct screen
{
struct geo geo, ugeo;
struct tag *seltag;
#define SCREEN_TAG_UPDATE 0x01
Flags flags;
int id;
TAILQ_HEAD(tsub, tag) tags;
SLIST_HEAD(, infobar) infobars;
@ -163,6 +178,7 @@ struct tag
struct client *sel;
struct client *prevsel;
struct tag *prev;
struct status_ctx statusctx;
char *name;
int id;
#define TAG_URGENT 0x01
@ -274,7 +290,7 @@ struct rule
char *name;
int tag, screen;
#define RULE_FREE 0x01
#define RULE_MAX 0x02
#define RULE_TAB 0x02
#define RULE_IGNORE_TAG 0x04
Flags flags;
SLIST_ENTRY(rule) next;
@ -325,6 +341,7 @@ struct wmfs
#define WMFS_LOG 0x10
#define WMFS_LAUNCHER 0x20
#define WMFS_SIGCHLD 0x40
#define WMFS_TABNOC 0x80 /* tab next opened client */
Flags flags;
GC gc, rgc;
Atom *net_atom;
@ -347,6 +364,7 @@ struct wmfs
SLIST_HEAD(, rule) rule;
SLIST_HEAD(, mousebind) mousebind;
SLIST_HEAD(, launcher) launcher;
SLIST_HEAD(, barwin) vbarwin;
} h;
/*

679
wmfs.1 Normal file
View File

@ -0,0 +1,679 @@
.\" title: wmfs
.\" dev: xorg62
.\" man: arpinux
.\"
.TH "WMFS" "1" "2012/02/08" "wmfs" "manual of wmfs"
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.SH "NAME"
wmfs \- Window Manager From Scratch
.SH "SYNOPSIS"
\fBwmfs\fR [\fB\-hv\fR] [\fB\-C <file>\fR] [\fB\-c <uicb_function> <cmd>\fR]
.sp
.SH "DESCRIPTION"
\fBWMFS\fR is a lightweight and highly configurable tiling window manager for X written in C\&.
.sp
.SH "OPTIONS"
.PP
\fB\-C <file>\fR
.RS 4
Load a configuration file\&.
.RE
.PP
\fB\-c <uicb_function> <cmd>\fR
.RS 4
Execute an uicb function to control WMFS\&.
.RE
.PP
\fB\-v\fR
.RS 4
Print version information to standard output, then exit\&.
.RE
.PP
\fB\-h\fR
.RS 4
Print help information, then exit\&.
.RE
.SH "DEFAULT KEY BINDINGS"
.PP
\fBControl\-Alt + r\fR
.RS 4
Reload WMFS binary
.RE
.PP
\fBSuper + Return\fR
.RS 4
Run a terminal (urxvt by default)
.RE
.PP
\fBSuper + q\fR
.RS 4
Quit the selected client
.RE
.PP
\fBControl\-Alt + q\fR
.RS 4
Exit WMFS
.RE
.PP
\fBSuper + f \fR
.RS 4
Toggle free the selected client
.RE
.PP
\fBAlt + Tab\fR
.RS 4
Give the focus to the next client
.RE
.PP
\fBAlt\-Shift + Tab\fR
.RS 4
Give the focus to the previous client
.RE
.PP
\fBAlt + h\fR
.RS 4
Give the focus to the client on the left
.RE
.PP
\fBAlt + l\fR
.RS 4
Give the focus to the client on the right
.RE
.PP
\fBAlt + k\fR
.RS 4
Give the focus to the client on the top
.RE
.PP
\fBAlt + j\fR
.RS 4
Give the focus to the client on the bottom
.RE
.PP
\fBSuper + Tab\fR
.RS 4
Give the focus to the next tabbed client
.RE
.PP
\fBSuper\-Shift + Tab\fR
.RS 4
Give the focus to the previous tabbed client
.RE
.PP
\fBControl\-Shift + h\fR
.RS 4
Swap with the client on the left
.RE
.PP
\fBControl\-Shift + l\fR
.RS 4
Swap with the client on the right
.RE
.PP
\fBControl\-Shift + k\fR
.RS 4
Swap with the client on the top
.RE
.PP
\fBControl\-Shift + j\fR
.RS 4
Swap with the client on the bottom
.RE
.PP
\fBAlt\-Shift + h\fR
.RS 4
Tab in the client on the left
.RE
.PP
\fBAlt\-Shift + l\fR
.RS 4
Tab in the client on the right
.RE
.PP
\fBAlt\-Shift + k\fR
.RS 4
Tab in the client on the top
.RE
.PP
\fBAlt\-Shift + j\fR
.RS 4
Tab in the client on the bottom
.RE
.PP
\fBAlt\-Shift + u\fR
.RS 4
Untab the client
.RE
.PP
\fBSuper + h\fR
.RS 4
Increase the client to the left
.RE
.PP
\fBSuper + l\fR
.RS 4
Decrease the client from the left
.RE
.PP
\fBSuper + k\fR
.RS 4
Increase the client to the top
.RE
.PP
\fBSuper + j\fR
.RS 4
Decrease the client from the top
.RE
.PP
\fBSuper\-Control + h\fR
.RS 4
Decrease the client from the right
.RE
.PP
\fBSuper\-Control + l\fR
.RS 4
Increase the client to the right
.RE
.PP
\fBSuper\-Control + k\fR
.RS 4
Decrease the client from the bottom
.RE
.PP
\fBSuper\-Control + j\fR
.RS 4
Increase the client to the bottom
.RE
.PP
\fBControl + Right\fR
.RS 4
Next tag
.RE
.PP
\fBControl + Left\fR
.RS 4
Previous tag
.RE
.PP
\fBControl + Up\fR
.RS 4
Next screen
.RE
.PP
\fBControl + Down\fR
.RS 4
Previous screen
.RE
.PP
\fBSuper + m\fR
.RS 4
Vertical mirror layout
.RE
.PP
\fBSuper\-Shift + m\fR
.RS 4
Horizontal mirror layout
.RE
.PP
\fBSuper + r\fR
.RS 4
Rotate layout right
.RE
.PP
\fBSuper\-Shift + r\fR
.RS 4
Rotate layout left
.RE
.PP
\fBSuper\-Control\-Alt + h\fR
.RS 4
Integrate client in left layout
.RE
.PP
\fBSuper\-Control\-Alt + j\fR
.RS 4
Integrate client in bottom layout
.RE
.PP
\fBSuper\-Control\-Alt + k\fR
.RS 4
Integrate client in top layout
.RE
.PP
\fBSuper\-Control\-Alt + l\fR
.RS 4
Integrate client in right layout
.RE
.PP
\fBSuper + o\fR
.RS 4
Restore previous layout
.RE
.PP
\fBSuper\-Shift + o\fR
.RS 4
Restore next layout
.RE
.PP
\fBSuper + p\fR
.RS 4
Make a launcher in the statusbar to run an unix command\fR
.RE
.PP
\fBSuper + F[1\&.\&.9]\fR
.RS 4
Change tag view
.RE
.PP
\fBSuper\-Shift + F[1\&.\&.9]\fR
.RS 4
Transfert the selected client to the wanted tag
.RE
.PP
\fBSuper + -\fR
.RS 4
Delete current tag\fR
.RE
.PP
\fBSuper\-Shift + -\fR
.RS 4
Add current tag\fR
.RE
.SH "CONFIGURATION"
WMFS is configured by \fI$HOME/\&.config/wmfs/wmfsrc\fR\&.
.RE
.PP
\fB\ include\fR
wmfsrc supports ”@include” to split configuration file by section\&.
.RS 2
\fB\ Usage:\fR "@include ~/.config/wmfs/wmfs_themes"\&.
.RE
.PP
\fB\ [themes]\fR
wmfsrc supports themes for client and statusbar\&.
.RS 2
\fB Misc\fR
.RS 2
\fB\ name\fR
theme name: will be used in next sections\&.
.PP
\fB\ font\fR
theme font: in XLFD format\&.
.PP
.RE
\fB\ Bars\fR
.RS 2
\fB\ bars_width\fR
bar height in pixels\&.
.PP
\fB\ bars_fg/bg\fR
statusbar text/background color\&.
.PP
.RE
\fB\ Tags\fR
.RS 2
\fB\ tags_normal_fg/bg\fR
normal tag text/button color\&.
.PP
\fB\ tags_normal_statusline\fR
normal tag statusline\&.
.PP
\fB\ tags_sel_fg/bg\fR
selected tag text/button color\&.
.PP
\fB\ tags_sel_statusline\fR
selected tag statusline\&.
.PP
\fB\ tags_occupied_fg/bg\fR
occupied tag text/button color\&.
.PP
\fB\ tags_occupied_statusline\fR
occupied tag statusline\&.
.PP
\fB\ tags_urgent_fg/bg\fR
urgent tag text/button color\&.
.PP
\fB\ tags_urgent_statusline\fR
urgent tag statusline\&.
.PP
\fB\ tags_border_color\fR
tag button border color\&.
.PP
\fB\ tags_border_width\fR
tag button border width\&.
.PP
.RE
\fB\ Clients\fR
.RS 2
\fB\ client_normal_fg/bg\fR
normal client titlebar text/background color\&.
.PP
\fB\ client_normal_statusline\fR
normal client statusline\&.
.PP
\fB\ client_sel_fg/bg\fR
selected client titlebar text/background color\&.
.PP
\fB\ client_sel_statusline\fR
selected client statusline\&.
.PP
\fB\ frame_bg\fR
client border color\&.
.PP
\fB\ client_titlebar_width\fR
client titlebar height in pixels\&.
.PP
\fB\ client_border_width\fR
client border height in pixels\&.
.RE
.PP
.RE
\fB\ [bars]\fR
.RS 2
\fB\ position\fR
statusbar position on screen: 0=Top; 1=Bottom, 2=Hide\&.
.PP
\fB\ screen\fR
screen to display statusbar(start ar 0), set to\fB -1\fR to display on every screen\&.
.PP
\fB\ elements\fR
t=Tags, s=Statustext, y=Systray, l=Launcher\&.
.PP
\fB\ theme\fR
names of the statusbar theme\&.
.RE
.PP
.RE
\fB\ [tags]\fR
.RS 2
\fB\ screen\fR
screen to display tag. use no screen option or screen =\fB -1\fR to set tag on each screen\&.
.PP
\fB\ name\fR
display tagname\&.
.PP
\fB\ statusline\fR
draw a custom statusline in the specific tag (can display any sequences)\&.
.PP
\fB\ mousebinds\fR
mouse actions on the tag buttons\&.
.RE
.PP
.RE
\fB\ [client]\fR
.RS 2
\fB\ theme\fR
apply theme to client by default\&.
.PP
\fB\ key_modifier\fR
key modifier to perform actions on clients\&.
.PP
\fB\ mousebinds\fR
mouse actions on client\&.
.RE
.PP
.RE
\fB\ [rules]\fR
specific rules for clients: to identify an application, use xprop\&.
.RS 2
\fB\ instance\fR
first part of WM_CLASS\&.
.PP
\fB\ class\fR
second part of WM_CLASS\&.
.PP
\fB\ role\fR
WM_WINDOW_ROLE\&.
.PP
\fB\ name\fR
_NET_WM_NAME\&.
.PP
\fB\ theme\fR
apply theme to client\&.
.PP
\fB\ tag\fR
set tag to client(start at 0)\&.
.PP
\fB\ screen\fR
display client on a specific screen\&.
.PP
\fB\ free\fR
client in auto-free mode (true/false)\&.
.PP
\fB\ tab\fR
open client in a tab (true/false)\&.
.RE
.PP
.RE
\fB\ [launchers]\fR
.RS 2
\fB\ name\fR
launcher-name, will be used in the [keys] section\&.
.PP
\fB\ prompt\fR
display text at the beginning of the prompt\&.
.PP
\fB\ command\fR
command used by the launcher. can be an uicb function or an uicb function + extension\&.
.RE
.PP
.RE
\fB\ [keys]\fR
.RS 2
each line is contained within\fB\ [key]...[/key]\fR
.PP
\fB\ mod\fR
key modifier (Alt, Control, Shift, Super)\&.
.PP
\fB\ key\fR
key to press\&.
.PP
\fB\ func\fR
uicb function to launch\&.
.PP
\fB\ cmd\fR
if\fB\ func = "spawn"\fR set the external command to launch\&.
.sp
.SH "STATUS"
statusbars, tags, surfaces and titlebars support sequences to display text, images bars and graphs through the\fB\ wmfs -c status\fR command.
.PP
\fB\ Syntax\fR
.PP
.RS 4
\fB\ position:\fR “left/right” (relative) or “x;y” (absolute)\&.
.PP
\fB\ dimension:\fR “ww;hh” for width;height of the rectangle or the image, to display an image at its original size, set it to “0;0”\&.
.PP
\fB\ color:\fR ”#rrggbb”\&.
.PP
\fB\ imagepath:\fR absolute path for the image\&.
.PP
\fB\ border:\fR width of the progressbar border in pixels\&.
.PP
\fB\ curser:\fR width of the curser in the positionbar\&.
.PP
\fB\ value:\fR a variable, to draw progressbar\&.
.PP
\fB\ valuemax:\fR maximum value of the value used in the progressbar\&.
.RE
.PP
\fB\ basic usage\fR
wmfs -c status "<barname> TEXT visible on 'barname'"\&.
.PP
\fB\ display colors\fR
wmfs -c status "<barname> ^s[<position>;<color>;<text>]"\&.
.PP
\fB\ display rectangles\fR
wmfs -c status "<barname> ^R[<position>;<dimensions>;<color>]"\&.
.PP
\fB\ display images\fR
wmfs -c status "<barname> ^i[<position>;<dimensions>;<imagepath>]"\&.
.PP
\fB\ display progressbars\fR
wmfs -c status "<barname> ^p[<position>;<dimensions>;<border>;<value>;<valuemax>;<bgcolor>;<fgcolor>]"\&.
.PP
\fB\ display positionbars\fR
wmfs -c status "<barname> ^P[<position>;<dimensions>;<curser>;<value>;<valuemax>;<bgcolor>;<fgcolor>]"\&.
.PP
\fB\ display graph\fR
wmfs -c status "<barname> ^g[<position>;<dimensions>;<value>;<valuemax>;<bgcolor>;<fgcolor>;<name>]"\&.
.RE
.PP
\fB\ mousebinds\fR
sequences supports mousebinds with format\fB\ (<key>;<uicb-function>)\fR or\fB\ (<key>;<spawn>;<command>)\fR
.RE
.PP
\fB\ surfaces\fR
you can display popups from the statusbar with the mousebind\fB\ (<key>;status_surface;<position>,<dimension>,<color> <datas>)\fR
.PP
.sp
.SH "UICB Functions"
UICB functions list. for “User Interface Call Backs”\&.
.PP
\fB\ usage in the wmfsrc:\fR func = "tag_next"\fB\ or\fR func = "spawn" cmd = "urxvt -e vim"\&.
.RE
\fB\ usage in the status.sh:\fR wmfs -c status "<barname> ^s[<position>;<color>;next](1;tag_next)"\&.
.RE
\fB\ usage in your terminal:\fR wmfs -c tag_next\&.
.PP
\fB\ spawn\fR
launch a command. ex: func = "spawn" cmd = "urxvtc -e screen irssi"\&.
.PP
\fB\ quit\fR
quit wmfs\&.
.PP
\fB\ reload\fR
reload wmfs\&.
.PP
\fB\ tag_set\fR
set tag by number\&.
.PP
\fB\ tag\fR
set tag by name\&.
.PP
\fB\ tag_next/prev\fR
set next/previous tag\&.
.PP
\fB\ tag_client\fR
tag the client\&.
.PP
\fB\ tag_move_client_next/prev\fR
tag the client with next/previous tag\&.
.PP
\fB\ tag_click\fR
display tag with a clic on tag button\&.
.PP
\fB\ tag_new/del\fR
add/delete a tag\&.
.PP
\fB\ layout_vmirror\fR
vertical mirror tiling\&.
.PP
\fB\ layout_hmirror\fR
horizontal mirror tiling\&.
.PP
\fB\ layout_rotate_left\fR
tiling rotate anti/clockwise\&.
.PP
\fB\ layout_prev_set\fR
back to previous set layout\&.
.PP
\fB\ layout_next_set\fR
go to next set layout\&.
.PP
\fB\ layout_integrate_left/right/top/bottom\fR
client integration in the client zone by direction\&.
.PP
\fB\ client_close\fR
close the client\&.
.PP
\fB\ client_resize_right/left/top/bottom\fR
resize client with direction\&.
.PP
\fB\ client_focus_right/left/top/bottom\fR
focus client with direction\&.
.PP
\fB\ client_tab_right/left/top/bottom\fR
tab client with direction\&.
.PP
\fB\ client_swap_right/left/top/bottom\fR
swap client with direction\&.
.PP
\fB\ client_focus_next/prev\fR
move focus to the next/previous client\&.
.PP
\fB\ client_swap_next/prev\fR
swap with the next/previous client\&.
.PP
\fB\ client_untab\fR
untab the client\&.
.PP
\fB\ client_focus_next_tab\fR
move focus to next tab-client\&.
.PP
\fB\ client_focus_prev_tab\fR
move focus to previous tab-client\&.
.PP
\fB\ client_focus_click\fR
give focus to client with a clic\&.
.PP
\fB\ client_toggle_free\fR
togle free the client\&.
.PP
\fB\ client_tab_next_opened\fR
open the client in a tab\&.
.PP
\fB\ status\fR
display the argument text in the statusbar\&.
.PP
\fB\ status_surface\fR
display a surface. can contain sequences\&.
.PP
\fB\ mouse_resize\fR
resize the client\&.
.PP
\fB\ mouse_move\fR
move the client\&.
.PP
\fB\ mouse_swap\fR
swap the client\&.
.PP
\fB\ mouse_tab\fR
tab the client\&.
.PP
\fB\ screen_next/prev\fR
go to next/previous screen\&.
.PP
\fB\ screen_move_client_next/prev\fR
move the client to next/previous screen\&.
.PP
\fB\ launcher\fR
native prompt. ex:\fB\ func = "launcher" cmd = "exec"\fR display the “exec” launcher\&.
.RE
.PP
.sp
.SH "BUGS"
WMFS isn\'t stable for now\&. So it certainly contains some bugs\&.
.sp
.SH "AUTHORS"
Martin Duquesnoy <\fIxorg62@gmail\&.com\fR\&[1]>\&.
.sp
.SH "WWW"
Main site: \fIhttps://github\&.com/xorg62/wmfs\fR
.PP
Wiki: \fIhttps://github\&.com/xorg62/wmfs/wiki\fR
.PP
Bug tracker: \fIhttps://github\&.com/xorg62/wmfs/issues\fR
.sp
.SH "COPYING"
WMFS is under the BSD license\&. See COPYING for more information\&.
.sp
.SH "NOTES"
.IP " 1." 4
xorg62@gmail.com
.RS 4
\%mailto:xorg62@gmail.com
.RE

9
wmfsrc
View File

@ -30,7 +30,7 @@
tags_occupied_fg = "#AABBAA"
tags_occupied_bg = "#445544"
tags_occupied_statusline = "\R[0;0;3;3;#AABBAA]"
tags_occupied_statusline = "\R[0;0;100;1;#AABBAA]"
tags_urgent_fg = "#223322"
tags_urgent_bg = "#CC5544"
@ -93,6 +93,7 @@
[tag]
screen = -1
name = "1"
# statusline=""
[/tag]
[tag] name = "2" [/tag]
@ -137,7 +138,7 @@
screen = 0
free = false
max = false
tab = false
ignore_tag = false
[/rule]
@ -185,6 +186,9 @@
[key] mod = {"Super", "Shift"} key = "F7" func = "tag_client" cmd = "6" [/key]
[key] mod = {"Super", "Shift"} key = "F8" func = "tag_client" cmd = "7" [/key]
[key] mod = {"Super"} key = "minus" func = "tag_del" [/key]
[key] mod = {"Super", "Shift"} key = "minus" func = "tag_new" [/key]
# tag function: cmd = nameofthetag
#[key] mod = {"Super"} key = "z" func = "tag" cmd = "2" [/key]
@ -230,6 +234,7 @@
[key] mod = {"Alt", "Shift"} key = "k" func = "client_tab_top" [/key]
[key] mod = {"Alt", "Shift"} key = "j" func = "client_tab_bottom" [/key]
[key] mod = {"Alt", "Shift"} key = "u" func = "client_untab" [/key]
[key] mod = {"Super"} key = "t" func = "client_tab_next_opened" [/key]
# Layout manipulation
[key] mod = {"Super"} key = "m" func = "layout_vmirror" [/key]