Replace the symlink configuration scheme with a simple yacc parser as

found in other places of the tree.  Remove sticky and font commandline
options and add another one for alternative config locations.
Split off cwmrc(5) from cwm(1), nuke #ifdef __OpenBSD__ while there.

tested by various kind people, feedback from oga@ and okan@ - thanks!
ok oga@, jasper@, okan@
This commit is contained in:
simon 2008-03-23 15:09:21 +00:00
parent 38ff7a904e
commit c3aa344e78
11 changed files with 874 additions and 426 deletions

View File

@ -8,16 +8,17 @@ PROG= cwm
SRCS= calmwm.c screen.c xmalloc.c client.c grab.c search.c \
util.c xutil.c conf.c input.c xevents.c group.c \
kbfunc.c font.c
kbfunc.c font.c parse.y
CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2
CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 -I${.CURDIR}
LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lXau -lXdmcp \
-lfontconfig -lexpat -lfreetype -lz -lX11 -lXau -lXdmcp -lXext
MANDIR= ${X11BASE}/man/cat
MAN= cwm.1 cwmrc.5
CLEANFILES= cwm.cat1
CLEANFILES= cwm.cat1 cwmrc.cat5
obj: _xenocara_obj

View File

@ -38,8 +38,7 @@ struct client_ctx_q Clientq;
int Doshape, Shape_ev;
int Starting;
struct conf Conf;
struct fontdesc *DefaultFont;
char *DefaultFontName;
struct fontdesc *DefaultFont = NULL;
/* From TWM */
#define gray_width 2
@ -53,23 +52,18 @@ int
main(int argc, char **argv)
{
int ch;
int conf_flags = 0;
const char *conffile = NULL;
char *display_name = NULL;
DefaultFontName = "sans-serif:pixelsize=14:bold";
while ((ch = getopt(argc, argv, "d:sf:")) != -1) {
while ((ch = getopt(argc, argv, "c:d:")) != -1) {
switch (ch) {
case 'c':
conffile = optarg;
break;
case 'd':
display_name = optarg;
break;
case 's':
conf_flags |= CONF_STICKY_GROUPS;
break;
case 'f':
DefaultFontName = xstrdup(optarg);
break;
default:
usage();
}
@ -87,8 +81,7 @@ main(int argc, char **argv)
group_init();
Starting = 1;
conf_setup(&Conf);
Conf.flags |= conf_flags;
conf_setup(&Conf, conffile);
client_setup();
x_setup(display_name);
Starting = 0;
@ -209,7 +202,7 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
GCLineWidth|GCSubwindowMode, &gv);
font_init(sc);
DefaultFont = font_getx(sc, DefaultFontName);
DefaultFont = font_getx(sc, Conf.DefaultFontName);
/*
* XXX - this should *really* be in screen_init(). ordering

View File

@ -30,6 +30,8 @@
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define CONFFILE ".cwmrc"
enum conftype {
CONF_BWIDTH, CONF_IGNORE,
};
@ -164,6 +166,12 @@ struct client_ctx {
TAILQ_HEAD(client_ctx_q, client_ctx);
static char *shortcut_to_name[] = {
"XXX", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine"
};
struct group_ctx {
TAILQ_ENTRY(group_ctx) entry;
struct client_ctx_q clients;
@ -204,6 +212,20 @@ enum directions {
CWM_UP=0, CWM_DOWN, CWM_LEFT, CWM_RIGHT,
};
/*
* Match a window.
*/
#define CONF_MAX_WINTITLE 256
#define CONF_IGNORECASE 0x01
struct winmatch {
TAILQ_ENTRY(winmatch) entry;
char title[CONF_MAX_WINTITLE];
int opts;
};
TAILQ_HEAD(winmatch_q, winmatch);
/* for cwm_exec */
#define CWM_EXEC_PROGRAM 0x1
#define CWM_EXEC_WM 0x2
@ -236,16 +258,20 @@ TAILQ_HEAD(cmd_q, cmd);
/* Global configuration */
struct conf {
struct keybinding_q keybindingq;
struct autogroupwin_q autogroupq;
char menu_path[MAXPATHLEN];
struct cmd_q cmdq;
struct keybinding_q keybindingq;
struct autogroupwin_q autogroupq;
struct winmatch_q ignoreq;
char conf_path[MAXPATHLEN];
struct cmd_q cmdq;
int flags;
int flags;
#define CONF_STICKY_GROUPS 0x0001
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
#define DEFAULTFONTNAME "sans-serif:pixelsize=14:bold"
char *DefaultFontName;
};
/* Menu stuff */
@ -397,21 +423,15 @@ struct screen_ctx *screen_current(void);
void screen_updatestackingorder(void);
void screen_infomsg(char *);
void conf_setup(struct conf *);
void conf_setup(struct conf *, const char *);
int conf_get_int(struct client_ctx *, enum conftype);
void conf_client(struct client_ctx *);
void conf_bindkey(struct conf *, void (*)(struct client_ctx *, void *),
int, int, int, void *);
void conf_bindname(struct conf *, char *, char *);
void conf_unbind(struct conf *, struct keybinding *);
void conf_parsekeys(struct conf *, char *);
void conf_parsesettings(struct conf *, char *);
void conf_parseignores(struct conf *, char *);
void conf_parseautogroups(struct conf *, char *);
void conf_cmd_clear(struct conf *);
int conf_cmd_changed(char *);
void conf_cmd_populate(struct conf *, char *);
void conf_cmd_refresh(struct conf *c);
int conf_changed(char *);
void conf_reload(struct conf *c);
char *conf_get_str(struct client_ctx *, enum conftype);
void kbfunc_client_lower(struct client_ctx *, void *);

299
conf.c
View File

@ -28,58 +28,7 @@
((tsp)->tv_sec cmp (usp)->tv_sec))
#endif
#define CONF_MAX_WINTITLE 256
#define CONF_IGNORECASE 0x01
/*
* Match a window.
*/
struct winmatch {
TAILQ_ENTRY(winmatch) entry;
char title[CONF_MAX_WINTITLE];
int opts;
};
TAILQ_HEAD(winmatch_q, winmatch);
struct winmatch_q ignoreq;
/* XXX - until we get a real configuration parser. */
#define WINMATCH_ADD(queue, str) do { \
struct winmatch *wm; \
XCALLOC(wm, struct winmatch); \
strlcpy(wm->title, str, sizeof(wm->title)); \
wm->opts |= CONF_IGNORECASE; \
TAILQ_INSERT_TAIL(queue, wm, entry); \
} while (0)
/* Initializes the command menu */
void
conf_cmd_init(struct conf *c)
{
TAILQ_INIT(&c->cmdq);
}
/* Removes all static entries */
void
conf_cmd_clear(struct conf *c)
{
struct cmd *cmd, *next;
for (cmd = TAILQ_FIRST(&c->cmdq); cmd != NULL; cmd = next) {
next = TAILQ_NEXT(cmd, entry);
/* Do not remove static entries */
if (cmd->flags & CMD_STATIC)
continue;
TAILQ_REMOVE(&c->cmdq, cmd, entry);
free(cmd);
}
}
extern struct screen_ctx *Curscreen;
/* Add an command menu entry to the end of the menu */
void
@ -102,94 +51,45 @@ conf_cmd_add(struct conf *c, char *image, char *label, int flags)
}
int
conf_cmd_changed(char *path)
conf_changed(char *path)
{
#ifdef __OpenBSD__
static struct timespec old_ts;
#else
static time_t old_time;
#endif
struct stat sb;
int changed;
/* If the directory does not exist we pretend that nothing changed */
if (stat(path, &sb) == -1 || !(sb.st_mode & S_IFDIR))
/* If the file does not exist we pretend that nothing changed */
if (stat(path, &sb) == -1 || !(sb.st_mode & S_IFREG))
return (0);
#ifdef __OpenBSD__
changed = !timespeccmp(&sb.st_mtimespec, &old_ts, ==);
old_ts = sb.st_mtimespec;
#else
changed = old_time != sb.st_mtime;
old_time = sb.st_mtime;
#endif
return (changed);
}
void
conf_cmd_populate(struct conf *c, char *path)
conf_reload(struct conf *c)
{
DIR *dir;
struct dirent *file;
char fullname[PATH_MAX];
int off;
if (strlen(path) >= sizeof (fullname) - 2)
errx(1, "directory name too long");
dir = opendir(path);
if (dir == NULL)
err(1, "opendir");
strlcpy(fullname, path, sizeof (fullname));
off = strlen(fullname);
if (fullname[off - 1] != '/') {
strlcat(fullname, "/", sizeof(fullname));
off++;
}
while ((file = readdir(dir)) != NULL) {
char *filename = file->d_name;
if (filename[0] == '.')
continue;
strlcpy(fullname + off, filename, sizeof(fullname) - off);
/* Add a dynamic entry to the command menu */
conf_cmd_add(c, fullname, filename, 0);
}
closedir(dir);
}
void
conf_cmd_refresh(struct conf *c)
{
if (!conf_cmd_changed(c->menu_path))
if (!conf_changed(c->conf_path))
return;
conf_cmd_clear(c);
conf_cmd_populate(c, c->menu_path);
if (parse_config(c->conf_path, c) == -1) {
warnx("config file %s has errors, not reloading", c->conf_path);
return;
}
DefaultFont = font_getx(Curscreen, c->DefaultFontName);
}
void
conf_setup(struct conf *c)
conf_init(struct conf *c)
{
char dir_keydefs[MAXPATHLEN];
char dir_settings[MAXPATHLEN];
char dir_ignored[MAXPATHLEN];
char dir_autogroup[MAXPATHLEN];
char *home = getenv("HOME");
if (home == NULL)
errx(1, "No HOME directory.");
snprintf(c->menu_path, sizeof(c->menu_path), "%s/.calmwm", home);
conf_cmd_init(c);
c->flags = 0;
TAILQ_INIT(&c->ignoreq);
TAILQ_INIT(&c->cmdq);
TAILQ_INIT(&c->keybindingq);
TAILQ_INIT(&c->autogroupq);
conf_bindname(c, "CM-Return", "terminal");
conf_bindname(c, "CM-Delete", "lock");
@ -247,39 +147,31 @@ conf_setup(struct conf *c)
conf_bindname(c, "CS-Up", "bigptrmoveup");
conf_bindname(c, "CS-Right", "bigptrmoveright");
snprintf(dir_keydefs, sizeof(dir_keydefs), "%s/.calmwm/.keys", home);
if (dirent_isdir(dir_keydefs))
conf_parsekeys(c, dir_keydefs);
snprintf(dir_settings, sizeof(dir_settings),
"%s/.calmwm/.settings", home);
if (dirent_isdir(dir_settings))
conf_parsesettings(c, dir_settings);
TAILQ_INIT(&ignoreq);
snprintf(dir_ignored, sizeof(dir_ignored), "%s/.calmwm/.ignore", home);
if (dirent_isdir(dir_ignored))
conf_parseignores(c, dir_ignored);
else {
WINMATCH_ADD(&ignoreq, "XMMS");
WINMATCH_ADD(&ignoreq, "xwi");
WINMATCH_ADD(&ignoreq, "xapm");
WINMATCH_ADD(&ignoreq, "xclock");
}
TAILQ_INIT(&c->autogroupq);
snprintf(dir_autogroup, sizeof(dir_autogroup),
"%s/.calmwm/.autogroup", home);
if (dirent_isdir(dir_autogroup))
conf_parseautogroups(c, dir_autogroup);
c->flags = 0;
/* Default term/lock */
strlcpy(Conf.termpath, "xterm", sizeof(Conf.termpath));
strlcpy(Conf.lockpath, "xlock", sizeof(Conf.lockpath));
strlcpy(c->termpath, "xterm", sizeof(c->termpath));
strlcpy(c->lockpath, "xlock", sizeof(c->lockpath));
c->DefaultFontName = DEFAULTFONTNAME;
}
void
conf_setup(struct conf *c, const char *conffile)
{
if (conffile == NULL) {
char *home = getenv("HOME");
if (home == NULL)
errx(1, "No HOME directory.");
snprintf(c->conf_path, sizeof(c->conf_path), "%s/%s", home,
CONFFILE);
}
else
snprintf(c->conf_path, sizeof(c->conf_path), "%s", conffile);
conf_init(c);
(void)parse_config(c->conf_path, c);
}
int
@ -294,7 +186,7 @@ conf_get_int(struct client_ctx *cc, enum conftype ctype)
/* Can wname be NULL? */
if (wname != NULL) {
TAILQ_FOREACH(wm, &ignoreq, entry) {
TAILQ_FOREACH(wm, &Conf.ignoreq, entry) {
int (*cmpfun)(const char *, const char *, size_t) =
wm->opts & CONF_IGNORECASE ? strncasecmp : strncmp;
if ((*cmpfun)(wm->title, wname, strlen(wm->title)) == 0) {
@ -392,37 +284,6 @@ struct {
{ NULL, NULL, 0, 0},
};
void
conf_parsekeys(struct conf *c, char *filename)
{
DIR *dir;
struct dirent *ent;
char buffer[MAXPATHLEN];
char current_file[MAXPATHLEN];
dir = opendir(filename);
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
snprintf(current_file, sizeof(current_file),
"%s/%s", filename, ent->d_name);
if (strchr(ent->d_name, '-') == NULL && ent->d_name[0] != '[')
continue;
if (!dirent_islink(current_file))
continue;
memset(buffer, 0, MAXPATHLEN);
if (readlink(current_file, buffer, MAXPATHLEN) < 0)
continue;
conf_bindname(c, ent->d_name, buffer);
}
closedir(dir);
}
void
conf_bindname(struct conf *c, char *name, char *binding)
{
@ -518,79 +379,3 @@ void conf_unbind(struct conf *c, struct keybinding *unbind)
TAILQ_REMOVE(&c->keybindingq, key, entry);
}
}
void
conf_parsesettings(struct conf *c, char *filename)
{
DIR *dir;
struct dirent *ent;
dir = opendir(filename);
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
if (strncmp(ent->d_name, "sticky", 7)==0)
Conf.flags |= CONF_STICKY_GROUPS;
}
closedir(dir);
}
void
conf_parseignores(struct conf *c, char *filename)
{
DIR *dir;
struct dirent *ent;
dir = opendir(filename);
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
WINMATCH_ADD(&ignoreq, ent->d_name);
}
closedir(dir);
}
void
conf_parseautogroups(struct conf *c, char *filename)
{
DIR *dir;
struct dirent *ent;
struct autogroupwin *aw;
char current_file[MAXPATHLEN], *p;
char group[CALMWM_MAXNAMELEN];
int len;
dir = opendir(filename);
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
snprintf(current_file, sizeof(current_file),
"%s/%s", filename, ent->d_name);
if (!dirent_islink(current_file))
continue;
if ((len = readlink(current_file,
group, sizeof(group) - 1)) < 0)
continue;
group[len] = '\0';
XCALLOC(aw, struct autogroupwin);
if ((p = strchr(ent->d_name, ',')) == NULL) {
aw->name = NULL;
aw->class = xstrdup(ent->d_name);
} else {
*(p++) = '\0';
aw->name = xstrdup(ent->d_name);
aw->class = xstrdup(p);
}
aw->group = xstrdup(group);
TAILQ_INSERT_TAIL(&c->autogroupq, aw, entry);
}
closedir(dir);
}

131
cwm.1
View File

@ -15,7 +15,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\" The following requests are required for all man pages.
.Dd June 29, 2007
.Dd $Mdocdate$
.Dt CWM 1
.Os
.Sh NAME
@ -24,9 +24,8 @@
.Sh SYNOPSIS
.\" For a program: program [-abc] file ...
.Nm cwm
.Op Fl s
.Op Fl d Ar display
.Op Fl f Ar fontname
.Op Fl c Ar file
.Sh DESCRIPTION
.Nm
is a window manager for X11 which contains many features that
@ -132,17 +131,9 @@ are as follows:
.Bl -tag -width Ds
.It Fl d Ar display
Specify the display to use.
.It Fl f Ar fontname
Makes the
.Xr Xft 3
font string
.Ar fontname
the default font.
.It Fl s
Set sticky group mode on.
The default behavior for new windows is to not assign any group.
This changes the default behavior to assigning the currently selected
group to any newly created windows.
.It Fl c Ar file
Specify the config file to use. Defaults to
.Pa ~/.cwmrc .
.El
.Sh POINTER MOVEMENT
The pointer can be moved with the use of the keyboard through bindings.
@ -205,7 +196,7 @@ perform operations on the entire group instead of just one window.
Currently, the only operation that is supported is to hide and unhide
the grouped windows.
Together with the
.Fl s
.Pa sticky
option, this can be used to emulate virtual desktops.
.Pp
To edit groups, use the group selection commands to toggle membership
@ -224,7 +215,7 @@ Show list of currently defined groups.
Clicking on an item will hide/unhide that group.
.It M3
Show list of applications as defined in
.Pa ~/.calmwm .
.Pa ~/.cwmrc .
Clicking on an item will spawn that application.
.El
.Sh ENVIRONMENT
@ -237,111 +228,9 @@ option is given.
.El
.Sh FILES
.Bl -tag -width Ds
.It Pa ~/.calmwm
Any directory entries here are shown in the application menu.
When it is selected, the image is executed with
.Xr execve 2 .
One use of this is to create symbolic links for your favorite
applications in this directory using
.Xr ln 1 .
.Pp
The entries
.Nm term
and
.Nm lock
have a special meaning.
When they exist they point to the terminal program and screen locking
programs used by the keybindings specified above.
The defaults for these are
.Xr xterm 1
and
.Xr xlock 1 ,
respectively.
.It Pa ~/.calmwm/.autogroup
Symlinks in this directory are read upon startup and control the
automatic grouping feature, which is based on the window name and class
properties.
To obtain the name and class of a window, use
.Ql xprop WM_CLASS ,
then click on the window.
The first quoted string is the window name; the second one is the
window class.
.Pp
The name of a link can be the window class, or the window class and name
separated by a comma.
The link target is a group name (one, two, \&..., nine).
For example, to make all windows in the
.Xr xterm 1
class go to the third group:
.Bd -literal -offset indent
$ ln -s three ~/.calmwm/.autogroup/XTerm
.Ed
.It Pa ~/.calmwm/.settings
Files in this directory cause various configuration options to be
set or unset.
Currently the only setting availiable is whether or not sticky groups
are activated.
To activate sticky groups create a file in this directory with the name
``sticky''.
.It Pa ~/.calmwm/.ignore
Any files in this directory cause
.Nm
to ignore programs by that name by not drawing borders around them.
For example the command
.Bd -literal -offset indent
$ ln -s three ~/.calmwm/.ignore/xclock
.Ed
will cause any instances of
.Xr xclock 1
to not have borders.
.It Pa ~/.calmwm/.keys
Symlinks in this directory cause the creation of keyboard shortcuts.
The default shortcuts will always be created. In case of conflict,
user-defined shortcuts take precidence.
The name of a link here is first the modifier keys, followed by a ``-''.
The following modifiers are recognised:
.Bl -tag -width Ds
.It Pa C
The Control key.
.It Pa M
The Meta key.
.It Pa S
The Shift key.
.It Pa 2
The Mod2 key.
.It Pa 3
The Mod3 key.
.It Pa 4
The Mod4 key (normally the windows key).
.El
The ``-'' should be followed by either a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h ,
or a numerical keycode value enclosed in ``[]''.
The target of the link should be either the name of a task from the
``name_to_kbfunc''
structure in
.Pa conf.c ,
or, alternatively it should be the commandline that is wished to be executed.
A special case is the ``unmap'' keyword, which causes any bindings using the
named shortcut to be removed. This can be used to remove a binding which conflicts
with an application.
For example, to cause
.Ic C-M-r
to add a label to a window:
.Bd -literal -offset indent
$ ln -s "label" ~/.calmwm/.keys/CM-r
.Ed
Launch an xterm running
.Xr top 1
with C-S-Enter:
.Bd -literal -offset indent
$ ln -s "/usr/X11R6/bin/xterm -e top" ~/.calmwm/.keys/CS-Return
.Ed
Remove a keybinding for Mod4-o
.Bd -literal -offset indent
$ ln -s "unmap" 4-o
.Ed
.El
.It Pa ~/.cwmrc
.Sh SEE ALSO
.Xr cwmrc 5
.Sh AUTHORS
.An -nosplit
.Pp

28
cwmrc Normal file
View File

@ -0,0 +1,28 @@
# $OpenBSD$
# Makes the Xft(3) font string fontname the default font
#fontname "sans-serif:pixelsize=14:bold"
# Set sticky group mode on
#sticky no
# Any entry here is shown in the application menu
#command firefox firefox
#command xmms xmms
#command top "xterm -e top"
# Autogroup definition
#autogroup 3 "aterm,XTerm"
#autogroup 3 "xterm,XTerm"
# Cause cwm to ignore programs by that name by not drawing borders around them.
#ignore XMMS
#ignore xwi
#ignore xapm
#ignore xclock
# Keys
#bind CM-r "label"
#bind CS-Return "xterm -e top"
#bind 4-o "unmap"

158
cwmrc.5 Normal file
View File

@ -0,0 +1,158 @@
.\" $OpenBSD$
.\"
.\" Copyright (c) 2004,2005 Marius Aamodt Eriksen <marius@monkey.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\" The following requests are required for all man pages.
.Dd $Mdocdate$
.Dt CWMRC 1
.Os
.Sh NAME
.Nm cwmrc
.Nd calm window manager configuration file
.Sh DESCRIPTION
The
.Xr cwm 1
window manager configuration file format.
.Sh OPTIONS
There are quite a few settings that affect the operation of
.Xr cwm 1 .
.Pp
The following options are accepted in the configuration file:
.Pp
.Bl -tag -width Ds
.It Ic fontname Ar font
Makes the
.Xr Xft 3
font string
.Ar font
the default font.
.It Ic sticky Ic yes Ns \&| Ns Ic no
Set sticky group mode on.
The default behavior for new windows is to not assign any group.
This changes the default behavior to assigning the currrently selected
group to any newly created windows.
.It Ic command Ar name Ar path
Every command entry is shown in the application menu.
When it is selected, the image is executed with
.Xr execve 2 .
.Pp
The entries
.Nm term
and
.Nm lock
have a special meaning.
When they exist they point to the terminal program and screen locking
programs used by the keybindings specified above.
The defaults for these are
.Xr xterm 1
and
.Xr xlock 1 ,
respectively.
.It Ic autogroup Ar group Dq windowclass
.It Ic autogroup Ar group Dq windowclass,windowname
Autogroups are read upon startup and control the
automatic grouping feature, which is based on the window name and class
properties.
The group is a number between 1 and 9.
.Pp
To obtain the name and class of a window, use
.Ql xprop WM_CLASS ,
then click on the window.
The first quoted string is the window name; the second one is the
window class.
.Pp
For example, to make all windows in the
.Xr xterm 1
class go to the third group:
.Bd -literal -offset indent
autogroup 3 XTerm
.Ed
.It Ic ignore Ar program
Ignore programs by that name by not drawing borders around them.
For example the command
.Bd -literal -offset indent
ignore xclock
.Ed
will cause any instances of
.Xr xclock 1
to not have borders.
.It Ic bind Ar keys Ar command
Cause the creation of keyboard shortcuts.
The default shortcuts will always be created. In case of conflict,
user-defined shortcuts take precidence.
The modifier keys come first, followed by a ``-''.
The following modifiers are recognised:
.Bl -tag -width Ds
.It Pa C
The Control key.
.It Pa M
The Meta key.
.It Pa S
The Shift key.
.It Pa 2
The Mod2 key.
.It Pa 3
The Mod3 key.
.It Pa 4
The Mod4 key (normally the windows key).
.El
The ``-'' should be followed by either a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h ,
or a numerical keycode value enclosed in ``[]''.
The command should be either the name of a task from the
``name_to_kbfunc''
structure in
.Pa conf.c ,
or, alternatively it should be the commandline that is wished to be executed.
A special case is the ``unmap'' keyword, which causes any bindings using the
named shortcut to be removed. This can be used to remove a binding which conflicts
with an application.
.Pp
For example, to cause
.Ic C-M-r
to add a label to a window:
.Bd -literal -offset indent
bind CM-r "label"
.Ed
.Pp
Launch an xterm running
.Xr top 1
with C-S-Enter:
.Bd -literal -offset indent
bind CS-Return "/usr/X11R6/bin/xterm -e top"
.Ed
.Pp
Remove a keybinding for Mod4-o
.Bd -literal -offset indent
bind 4-o "unmap"
.Ed
.El
.Sh SEE ALSO
.Xr cwm 1
.Sh AUTHORS
.An -nosplit
.Pp
.Nm
was initially written by
.An Marius Aamodt Eriksen Aq marius@monkey.org
with contributions from
.An Andy Adamson Aq dros@monkey.org ,
.An Niels Provos Aq provos@monkey.org ,
and
.An Antti Nykänen Aq aon@iki.fi .
.Sh HISTORY
.Nm
first appeared in
.Ox 4.4 .

View File

@ -31,12 +31,6 @@ char Group_name[256];
int Grouphideall = 0;
struct group_ctx_q Groupq;
static char *shortcut_to_name[] = {
"XXX", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine",
};
static void
_group_add(struct group_ctx *gc, struct client_ctx *cc)
{

View File

@ -204,7 +204,7 @@ kbfunc_menu_search(struct client_ctx *scratch, void *arg)
TAILQ_INIT(&menuq);
conf_cmd_refresh(&Conf);
conf_reload(&Conf);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
XCALLOC(mi, struct menu);
strlcpy(mi->text, cmd->label, sizeof(mi->text));
@ -249,14 +249,14 @@ kbfunc_cmdexec(struct client_ctx *cc, void *arg)
void
kbfunc_term(struct client_ctx *cc, void *arg)
{
conf_cmd_refresh(&Conf);
conf_reload(&Conf);
u_spawn(Conf.termpath);
}
void
kbfunc_lock(struct client_ctx *cc, void *arg)
{
conf_cmd_refresh(&Conf);
conf_reload(&Conf);
u_spawn(Conf.lockpath);
}
@ -423,7 +423,7 @@ kbfunc_ssh(struct client_ctx *scratch, void *arg)
if ((mi = search_start(&menuq,
search_match_exec, NULL, "ssh", 1)) != NULL) {
conf_cmd_refresh(&Conf);
conf_reload(&Conf);
l = snprintf(cmd, sizeof(cmd), "%s -e ssh %s", Conf.termpath,
mi->text);
if (l != -1 && l < sizeof(cmd))

583
parse.y Normal file
View File

@ -0,0 +1,583 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
* Copyright (c) 2001 Theo de Raadt. All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
%{
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "headers.h"
#include "calmwm.h"
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
static struct file {
TAILQ_ENTRY(file) entry;
FILE *stream;
char *name;
int lineno;
int errors;
} *file;
struct file *pushfile(const char *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...);
int kw_cmp(const void *, const void *);
int lookup(char *);
int lgetc(int);
int lungetc(int);
int findeol(void);
static struct conf *conf;
extern char *shortcut_to_name[];
typedef struct {
union {
int64_t number;
char *string;
} v;
int lineno;
} YYSTYPE;
%}
%token FONTNAME STICKY
%token AUTOGROUP BIND COMMAND IGNORE
%token YES NO
%token ERROR
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> yesno
%type <v.string> string
%%
grammar : /* empty */
| grammar '\n'
| grammar main '\n'
| grammar error '\n' { file->errors++; }
;
string : string STRING {
if (asprintf(&$$, "%s %s", $1, $2) == -1) {
free($1);
free($2);
yyerror("string: asprintf");
YYERROR;
}
free($1);
free($2);
}
| STRING
;
yesno : YES { $$ = 1; }
| NO { $$ = 0; }
;
main : FONTNAME STRING {
if (conf->DefaultFontName != NULL &&
conf->DefaultFontName != DEFAULTFONTNAME)
free(conf->DefaultFontName);
if ((conf->DefaultFontName = xstrdup($2)) == NULL) {
free($2);
yyerror("string: asprintf");
YYERROR;
}
free($2);
}
| STICKY yesno {
if ($2 == 0)
conf->flags &= ~CONF_STICKY_GROUPS;
else
conf->flags |= CONF_STICKY_GROUPS;
}
| COMMAND STRING string {
conf_cmd_add(conf, $3, $2, 0);
free($2);
free($3);
}
| AUTOGROUP NUMBER STRING {
struct autogroupwin *aw;
char *p;
if ($2 < 1 || $2 > 9) {
free($3);
yyerror("autogroup number out of range: %d", $2);
YYERROR;
}
XCALLOC(aw, struct autogroupwin);
if ((p = strchr($3, ',')) == NULL) {
aw->name = NULL;
aw->class = xstrdup($3);
} else {
*(p++) = '\0';
aw->name = xstrdup($3);
aw->class = xstrdup(p);
}
aw->group = xstrdup(shortcut_to_name[$2]);
TAILQ_INSERT_TAIL(&conf->autogroupq, aw, entry);
free($3);
}
| IGNORE STRING {
struct winmatch *wm;
XCALLOC(wm, struct winmatch);
strlcpy(wm->title, $2, sizeof(wm->title));
wm->opts |= CONF_IGNORECASE;
TAILQ_INSERT_TAIL(&conf->ignoreq, wm, entry);
free($2);
}
| BIND STRING string {
conf_bindname(conf, $2, $3);
free($2);
free($3);
}
;
%%
struct keywords {
const char *k_name;
int k_val;
};
int
yyerror(const char *fmt, ...)
{
va_list ap;
file->errors++;
va_start(ap, fmt);
fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
return (0);
}
int
kw_cmp(const void *k, const void *e)
{
return (strcmp(k, ((const struct keywords *)e)->k_name));
}
int
lookup(char *s)
{
/* this has to be sorted always */
static const struct keywords keywords[] = {
{ "autogroup", AUTOGROUP},
{ "bind", BIND},
{ "command", COMMAND},
{ "fontname", FONTNAME},
{ "ignore", IGNORE},
{ "no", NO},
{ "sticky", STICKY},
{ "yes", YES}
};
const struct keywords *p;
p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
sizeof(keywords[0]), kw_cmp);
if (p)
return (p->k_val);
else
return (STRING);
}
#define MAXPUSHBACK 128
char *parsebuf;
int parseindex;
char pushback_buffer[MAXPUSHBACK];
int pushback_index = 0;
int
lgetc(int quotec)
{
int c, next;
if (parsebuf) {
/* Read character from the parsebuffer instead of input. */
if (parseindex >= 0) {
c = parsebuf[parseindex++];
if (c != '\0')
return (c);
parsebuf = NULL;
} else
parseindex++;
}
if (pushback_index)
return (pushback_buffer[--pushback_index]);
if (quotec) {
if ((c = getc(file->stream)) == EOF) {
yyerror("reached end of file while parsing quoted string");
if (popfile() == EOF)
return (EOF);
return (quotec);
}
return (c);
}
while ((c = getc(file->stream)) == '\\') {
next = getc(file->stream);
if (next != '\n') {
c = next;
break;
}
yylval.lineno = file->lineno;
file->lineno++;
}
while (c == EOF) {
if (popfile() == EOF)
return (EOF);
c = getc(file->stream);
}
return (c);
}
int
lungetc(int c)
{
if (c == EOF)
return (EOF);
if (parsebuf) {
parseindex--;
if (parseindex >= 0)
return (c);
}
if (pushback_index < MAXPUSHBACK-1)
return (pushback_buffer[pushback_index++] = c);
else
return (EOF);
}
int
findeol(void)
{
int c;
parsebuf = NULL;
pushback_index = 0;
/* skip to either EOF or the first real EOL */
while (1) {
c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
}
if (c == EOF)
break;
}
return (ERROR);
}
int
yylex(void)
{
char buf[8096];
char *p;
int quotec, next, c;
int token;
p = buf;
while ((c = lgetc(0)) == ' ' || c == '\t')
; /* nothing */
yylval.lineno = file->lineno;
if (c == '#')
while ((c = lgetc(0)) != '\n' && c != EOF)
; /* nothing */
switch (c) {
case '\'':
case '"':
quotec = c;
while (1) {
if ((c = lgetc(quotec)) == EOF)
return (0);
if (c == '\n') {
file->lineno++;
continue;
} else if (c == '\\') {
if ((next = lgetc(quotec)) == EOF)
return (0);
if (next == quotec || c == ' ' || c == '\t')
c = next;
else if (next == '\n')
continue;
else
lungetc(next);
} else if (c == quotec) {
*p = '\0';
break;
}
if (p + 1 >= buf + sizeof(buf) - 1) {
yyerror("string too long");
return (findeol());
}
*p++ = (char)c;
}
yylval.v.string = strdup(buf);
if (yylval.v.string == NULL)
err(1, "yylex: strdup");
return (STRING);
}
#define allowed_to_end_number(x) \
(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
if (c == '-' || isdigit(c)) {
do {
*p++ = c;
if ((unsigned)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
} while ((c = lgetc(0)) != EOF && isdigit(c));
lungetc(c);
if (p == buf + 1 && buf[0] == '-')
goto nodigits;
if (c == EOF || allowed_to_end_number(c)) {
const char *errstr = NULL;
*p = '\0';
yylval.v.number = strtonum(buf, LLONG_MIN,
LLONG_MAX, &errstr);
if (errstr) {
yyerror("\"%s\" invalid number: %s",
buf, errstr);
return (findeol());
}
return (NUMBER);
} else {
nodigits:
while (p > buf + 1)
lungetc(*--p);
c = *--p;
if (c == '-')
return (c);
}
}
#define allowed_in_string(x) \
(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
x != '{' && x != '}' && x != '<' && x != '>' && \
x != '!' && x != '=' && x != '/' && x != '#' && \
x != ','))
if (isalnum(c) || c == ':' || c == '_' || c == '*') {
do {
*p++ = c;
if ((unsigned)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
lungetc(c);
*p = '\0';
if ((token = lookup(buf)) == STRING)
if ((yylval.v.string = strdup(buf)) == NULL)
err(1, "yylex: strdup");
return (token);
}
if (c == '\n') {
yylval.lineno = file->lineno;
file->lineno++;
}
if (c == EOF)
return (0);
return (c);
}
struct file *
pushfile(const char *name)
{
struct file *nfile;
if ((nfile = calloc(1, sizeof(struct file))) == NULL ||
(nfile->name = strdup(name)) == NULL) {
warn("malloc");
return (NULL);
}
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
warn("%s", nfile->name);
free(nfile->name);
free(nfile);
return (NULL);
}
nfile->lineno = 1;
TAILQ_INSERT_TAIL(&files, nfile, entry);
return (nfile);
}
int
popfile(void)
{
struct file *prev;
if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
prev->errors += file->errors;
TAILQ_REMOVE(&files, file, entry);
fclose(file->stream);
free(file->name);
free(file);
file = prev;
return (0);
}
return (EOF);
}
void
conf_clear(struct conf *c)
{
struct autogroupwin *ag, *agnext;
struct keybinding *kb, *kbnext;
struct winmatch *wm, *wmnext;
struct cmd *cmd, *cmdnext;
for (cmd = TAILQ_FIRST(&c->cmdq); cmd != NULL; cmd = cmdnext) {
cmdnext = TAILQ_NEXT(cmd, entry);
TAILQ_REMOVE(&c->cmdq, cmd, entry);
free(cmd);
}
for (kb = TAILQ_FIRST(&c->keybindingq); kb != NULL; kb = kbnext) {
kbnext = TAILQ_NEXT(kb, entry);
TAILQ_REMOVE(&c->keybindingq, kb, entry);
free(kb);
}
for (ag = TAILQ_FIRST(&c->autogroupq); ag != NULL; ag = agnext) {
agnext = TAILQ_NEXT(ag, entry);
TAILQ_REMOVE(&c->autogroupq, ag, entry);
free(ag->class);
if (ag->name)
free(ag->name);
free(ag->group);
free(ag);
}
for (wm = TAILQ_FIRST(&c->ignoreq); wm != NULL; wm = wmnext) {
wmnext = TAILQ_NEXT(wm, entry);
TAILQ_REMOVE(&c->ignoreq, wm, entry);
free(wm);
}
if (c->DefaultFontName != NULL &&
c->DefaultFontName != DEFAULTFONTNAME)
free(c->DefaultFontName);
}
int
parse_config(const char *filename, struct conf *xconf)
{
int errors = 0;
if ((conf = malloc(sizeof(struct conf))) == NULL)
return (-1);
if ((file = pushfile(filename)) == NULL) {
free(conf);
return (-1);
}
strlcpy(conf->conf_path, filename, sizeof(conf->conf_path));
conf_init(conf);
yyparse();
errors = file->errors;
file->errors = 0;
popfile();
if (errors) {
conf_clear(conf);
}
else {
struct autogroupwin *ag, *agnext;
struct keybinding *kb, *kbnext;
struct winmatch *wm, *wmnext;
struct cmd *cmd, *cmdnext;
conf_clear(xconf);
xconf->flags = conf->flags;
for (cmd = TAILQ_FIRST(&conf->cmdq); cmd != NULL; cmd = cmdnext) {
cmdnext = TAILQ_NEXT(cmd, entry);
TAILQ_REMOVE(&conf->cmdq, cmd, entry);
TAILQ_INSERT_TAIL(&xconf->cmdq, cmd, entry);
}
for (kb = TAILQ_FIRST(&conf->keybindingq); kb != NULL; kb = kbnext) {
kbnext = TAILQ_NEXT(kb, entry);
TAILQ_REMOVE(&conf->keybindingq, kb, entry);
TAILQ_INSERT_TAIL(&xconf->keybindingq, kb, entry);
}
for (ag = TAILQ_FIRST(&conf->autogroupq); ag != NULL; ag = agnext) {
agnext = TAILQ_NEXT(ag, entry);
TAILQ_REMOVE(&conf->autogroupq, ag, entry);
TAILQ_INSERT_TAIL(&xconf->autogroupq, ag, entry);
}
for (wm = TAILQ_FIRST(&conf->ignoreq); wm != NULL; wm = wmnext) {
wmnext = TAILQ_NEXT(wm, entry);
TAILQ_REMOVE(&conf->ignoreq, wm, entry);
TAILQ_INSERT_TAIL(&xconf->ignoreq, wm, entry);
}
strlcpy(xconf->termpath, conf->termpath, sizeof(xconf->termpath));
strlcpy(xconf->lockpath, conf->lockpath, sizeof(xconf->lockpath));
xconf->DefaultFontName = conf->DefaultFontName;
}
free(conf);
return (errors ? -1 : 0);
}

View File

@ -275,10 +275,7 @@ xev_handle_buttonpress(struct xevent *xev, XEvent *ee)
break;
case Button3: {
struct cmd *cmd;
if (conf_cmd_changed(Conf.menu_path)) {
conf_cmd_clear(&Conf);
conf_cmd_populate(&Conf, Conf.menu_path);
}
conf_reload(&Conf);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
XCALLOC(mi, struct menu);
strlcpy(mi->text, cmd->label, sizeof(mi->text));