diff --git a/Makefile b/Makefile index 2af7f16..fbc60e6 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/calmwm.c b/calmwm.c index 2048502..4f40107 100644 --- a/calmwm.c +++ b/calmwm.c @@ -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 diff --git a/calmwm.h b/calmwm.h index 309d2e3..5317f17 100644 --- a/calmwm.h +++ b/calmwm.h @@ -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 *); diff --git a/conf.c b/conf.c index 5371cec..7193d82 100644 --- a/conf.c +++ b/conf.c @@ -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); - -} diff --git a/cwm.1 b/cwm.1 index 5f2a01e..91509cb 100644 --- a/cwm.1 +++ b/cwm.1 @@ -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 diff --git a/cwmrc b/cwmrc new file mode 100644 index 0000000..f3c7e65 --- /dev/null +++ b/cwmrc @@ -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" + diff --git a/cwmrc.5 b/cwmrc.5 new file mode 100644 index 0000000..5695057 --- /dev/null +++ b/cwmrc.5 @@ -0,0 +1,158 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2004,2005 Marius Aamodt Eriksen +.\" +.\" 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 . diff --git a/group.c b/group.c index 744ca69..913ad2e 100644 --- a/group.c +++ b/group.c @@ -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) { diff --git a/kbfunc.c b/kbfunc.c index b71c8fd..8192cae 100644 --- a/kbfunc.c +++ b/kbfunc.c @@ -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)) diff --git a/parse.y b/parse.y new file mode 100644 index 0000000..bb5e44c --- /dev/null +++ b/parse.y @@ -0,0 +1,583 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2002, 2003, 2004 Henning Brauer + * 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 +#include +#include +#include +#include +#include +#include + +#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 STRING +%token NUMBER +%type yesno +%type 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); +} diff --git a/xevents.c b/xevents.c index 0729b49..a0f3538 100644 --- a/xevents.c +++ b/xevents.c @@ -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));