103 Commits

Author SHA1 Message Date
oga
04441482d4 fix the froggy problem.
Implement a handler for the MappingEvent, meaning that the keymap has changed.
When this happens, ungrab all bindings, update the map, and regrab.

Fixes the problem where some keybindings wouldn't work under non us or
uk keymaps (especially the .fr map, it seems). Issue noticed by
ajacoutot@, ratchov@, and a few people on misc. Based on an initial diff
from ratchov@.

ok okan.
2008-07-22 21:01:54 +00:00
oga
0df9e0673c We've been handling grabbing wrong all this time (noticed at c2k8).
add conf_grab() and conf_ungrab, and use them in the keybinding manipulation
functions to {,un}grab the binding for all screens we have defined.

the lovely little ordering problem comes in here, since when we parse
the config initially Screenq is empty, so regrab after we fill the
queue, hopefully later reordering will remove this little need and there
will be much rejoicing.

ok okan.
2008-07-22 20:51:54 +00:00
oga
436a9e5c54 split x_setup() into two. dpy_init() for setting up the display and
checking the X config, and x_setup to set up the screens.

There's an ordering problem that means that some of this init needs to
come after the config is parsed, the rest should ideally happen before
though. This is a rough split, it will be refined later. Again, needed
for an upcoming change.

ok okan.
2008-07-22 20:42:24 +00:00
oga
20052e0248 Add xu_key_ungrab() and a mirror to xu_key_ungrab(). a couple of changes
that are coming up depend on it.

ok okan.
2008-07-22 20:26:12 +00:00
oga
13b640ea29 Kill screen_init(). it's been stubbed out for a while now. I don't envision it
coming back in it's current form.

ok okan@.
2008-07-22 19:54:57 +00:00
oga
2f8639d37b kill another leftover prototype.
ok okan@
2008-07-22 19:52:54 +00:00
oga
556f7a0871 Put back the initialisation of gc in group_cycle. No cookie for okan.
Reported by Dan Harnett, thanks!
2008-07-18 15:40:52 +00:00
2798cdc9a7 move client_vertmaximize to a more sensible location, purely for readability.
"don't mind at all" oga@
2008-07-15 22:12:09 +00:00
8afabd6c55 save an X call and use what we already have stored.
ok oga@
2008-07-15 22:06:48 +00:00
96f03c8cbc don't compensate for bwidth twice.
ok oga@
2008-07-15 13:52:02 +00:00
6d268bfa4a Nm makes more sense than Xr, from Pierre Riteau - thanks! 2008-07-11 22:43:44 +00:00
oga
b6dab5ccf3 Change "a window" to "current window" in documentation. it's more correct.
While i'm here: Capital letter and full stop in descriptions in cwmrc.5

"looks good - do it" okan@.
2008-07-11 15:42:29 +00:00
a556d3fa1f add Xr for cwm(1) 2008-07-11 15:20:04 +00:00
6ea4b1bd3b no more hidden (and mysterious) config reloads and allow binding a key
to a config reload; CMS-r by default.

ok oga@
2008-07-11 15:18:29 +00:00
8be175b175 replace snprintf with strlcpy
ok oga@
2008-07-11 14:24:34 +00:00
85e6c61360 we already have ymax, so use it instead of asking X for it again.
ok oga@
2008-07-11 14:23:30 +00:00
b23fad3987 spacing, declaration lineup to be consistent throughout cwm,
readability, and a bit of knf.

ok oga@
2008-07-11 14:21:28 +00:00
734f45ab4b only cycle through visible *and* non-ignored windows.
ok oga@
2008-06-30 17:52:37 +00:00
oga
993fd4311f Allow a mouse binding to hide a window, and add a default keybinding for CMS-M3,
so it's hard to press by accident, but there if you need it.

requested (in a way) and tested by johan and todd.
2008-06-25 22:44:42 +00:00
oga
a6ec6cd9e9 Support mod4 (windows key) in mouse bindings too. 2008-06-25 22:40:27 +00:00
oga
05b17bf803 Support shift in mouse bindings. There's really no reason not to.
tested by johan@ and todd@.
2008-06-25 22:38:36 +00:00
oga
2dfd021f8b Actually grab the correct mouse buttons for a window, instead of doing the
old hardcoded ones (which now can be wrong).

tested by todd@ and johan@.
2008-06-25 22:37:29 +00:00
oga
77058c59e2 Sort flags.
From Pierre Riteau, thanks!

ok okan@.
2008-06-25 01:09:09 +00:00
oga
187e7dfad2 Stop keyboard move moving the window utterly off the screen. If that
happens there's no way to get it back.  Also, stop resize making a
windows size negative or zero. X does not like that one bit.

Diff from Martynas. Ok okan@.
2008-06-25 00:52:47 +00:00
oga
6f1ed5bfe3 Don't link "-lX11 -lXau -lXdmcp" twice.
From Martin Toft, thanks!
2008-06-19 02:21:30 +00:00
oga
e21e7680e4 No need to map/unmap the window on hide/show since we already do the
same to its parent.

"makes sense" okan@.
2008-06-18 20:42:29 +00:00
oga
2bddbe12f4 Revert previous "fix" it introduces new issues of its own.
The problem that's causing us to lose windows is that rapid hiding and
unhiding causes a backlog of X events, so we lose track of client state,
and delete cc->pwin when we should not. A proper fix will arrive when it's been
worked out.
2008-06-18 19:09:12 +00:00
0ba60f0b94 missed one mouse function
noticed by oga
2008-06-17 23:46:49 +00:00
oga
4f2d4724c9 Ignore caps lock and numlock for keyboard bindings. The way Xlib makes
you do this is ugly. Also remove mod2 (numlock) and mod3 (odd) from the
list of keybinding modifiers. They don't make much sense here.

based on a heavily modified diff from Martynas.

ok okan.
2008-06-17 23:40:33 +00:00
oga
19ba704ee3 Just rework the mouse binding calculation on event to look like the
kbfunc one.  Makes the code a lot easier to read.

Fixes a bug i introduced in the last commit here.

ok okan.
2008-06-17 20:55:48 +00:00
oga
9657664c7b The mousebinding code missing a break once it had found the correct
binding, this expose another issue that's still being debugged.
Issue pointed out by Dan Harnett, thanks!

While i'm here KNF and rework the logic to not be ass-backwards.

ok okan.
2008-06-17 20:21:17 +00:00
mk
867652c484 Make this not crash when compiled with -g.
Found by myself, analysis by kurt@, fix by me with input from otto.

``Just get some fix in...'' deraadt
2008-06-16 19:09:48 +00:00
oga
07cd0b1ac5 Rip out and burn the HASH_* stuff. We don't need a SPLAY tree for one font.
makes the code a lot simpler. While here rearrange the font handling functions
to be less shit.

ok and help okan@.
2008-06-15 02:47:46 +00:00
96d7310b4a (mostly) proper xshape event support
ok oga@
2008-06-14 22:04:11 +00:00
160228210b unbreak 2008-06-14 21:59:09 +00:00
9d9c61b8f6 slightly alter the semantics of config files:
- if no config file, continue silently and apply defaults
 - if config file, parse and move on
 - if config file specified but not found, error out

ok oga@
2008-06-14 21:51:00 +00:00
bdcbbe7f53 confable menu and window mouse bindings from rivo nurges (thanks!) with
some minor fixups, man page bits and knf.

ok oga@
2008-06-14 21:48:54 +00:00
b4ae492b7b finally document functions that can be bound, removing the need to have
name_to_kbfunc[] around.

feedback jmc@, ok oga@
2008-06-13 21:22:34 +00:00
oga
01eecac5d4 Don't client_delete() on an Unmap event, only do that on a client delete event.
found by (among others) todd@ when you have a lot of clients and do something
that maps and umaps a lot of windows fast.

Debugged with aid of gdb, todd, okan and NULL pointers in a pizza place in
edmonton while waiting an inordinately long time for food.

ok okan@, todd@
2008-06-13 03:41:58 +00:00
077173527b kill another long gone proto 2008-06-12 19:10:56 +00:00
ff9e573e1d remove old (moved) code
ok oga@
2008-06-12 18:55:35 +00:00
198bb381a9 instead of forcing the ptr in the middle everytime, be more 'calm'; keep
the ptr still unless it moves out-of-bounds, then just follow the edge.

brought up by todd@

ok oga@
2008-06-12 18:32:06 +00:00
6f1f3592d4 place the pointer in the middle of the window after resizing with grab,
just like keyboard resize.

from Edd Barrett

ok oga@
2008-06-12 05:10:24 +00:00
78c8bf08cb ignore if non-zero expose events, for we could be covered by multiple
windows; merely an optimization.

ok oga@
2008-06-12 05:01:13 +00:00
4377b5ac3f re-work client_placecalc()
- make sure new clients sit inside the current screen
- respect 'gap' placement on new clients

ok oga@
2008-06-12 04:59:51 +00:00
9037043088 Enter -> Return, to be consistant and correct.
found the hard way by johan and discovered by oga.

"do it" oga@
2008-06-05 14:29:01 +00:00
610e8e83ac prevent trying to exec a null char; could potentially happen with a
canceled or empty searchstr.

ok oga@
2008-06-05 04:01:37 +00:00
efbfc5fa42 actually honor termpath and lockpath if specified in cwmrc.
"now" oga@
2008-06-05 00:07:05 +00:00
b86d3cfae9 "Meta is a perfectly well defined concept in X (The keys bound to the
Meta_L or Meta_R keysysm).  no need to redefine it roughly there" - matthieu@
2008-06-03 21:27:47 +00:00
72bc2a295b make sure to take bwdith into account when placing a new window.
ok oga@
2008-05-23 18:57:35 +00:00
oga
48528d9ba1 Grab the keyboard when we initialise the menu. This stops the keyboard
shortcut code stealing our events in some cases.

"put 'er in" okan@.
2008-05-23 18:48:57 +00:00
oga
779cf04f05 Make menu_filter handle mouse movement too. This enables the keyboard
search dialogues to be manipulated with the mouse, too. It also allows
me to shrink the codebase further by killing grab_menu().

One known issue with highlighting the first entry in a search dialogue,
that'll be fixed soonish.

ok okan@, tested by Edd Barrett and todd@.
2008-05-21 14:11:19 +00:00
oga
1e46ba72f7 Pull out the behaviour in grab_label and search_start into one utility
function menu_filter(). The plan is to eventually merge in grab_menu too.
Shrinks the code a fair bit.

Also, change XMaskEvent for XWindowEvent to prevent getting exposes for other
windows. This is particuarly noticable on slow machines with a LOT of xterms
(todd, you're an odd man).

ok okan@, todd@.
2008-05-20 14:50:51 +00:00
oga
3bb0b451f7 General cleanup.
ok okan@.
2008-05-19 18:53:09 +00:00
53116c4ec3 stop normalizing search input; searching and matching are still
case-insensitive.  since this was the only use of normalizing input,
simplify as well.

allows one to exec with mixed case unmatched commands.

"works for me" oga@
2008-05-19 18:07:53 +00:00
oga
981c2480db Function prototypes should not have parameter names in them. These must
have been missed last time i knfed this.

ok okan.
2008-05-19 17:32:22 +00:00
6733ac217f client_cyclenext() -> client_cycle() since we now pass an arg.
removes a stray proto as well.

discussed with and ok oga@
2008-05-19 17:24:19 +00:00
oga
71f99ab78f allow an autogroup value of 0 to mean no group. This means you can set
automatically "sticky" (in the traditional sense of the word) windows in
autogroup mode.

Based on an initial diff from Andrew Fresh, thanks!

ok okan@.
2008-05-19 17:13:55 +00:00
d347aa3d9a as done with cycle/rcycle, make prev/next group switching one kbfuncs
and use a flag; adjusted to match and rename to {r,}cycle.

"ok, since i came up with the same thing" oga@
2008-05-19 17:11:19 +00:00
oga
43d6e147c2 Use the XGrabKeyboard hack in for alt-tabbing as well. This stops the
mru getting the order messed up when gvim/xpdf et all steal key events.

While i'm here, change the logic in client_cyclenext() to use break instead
of goto, it's nicer that way.

Thirdly, instead of two different kbfuncs, just use the one and a flag.

"put your cycle diff in so I can pkg_delete gvim" okan@
2008-05-19 15:17:50 +00:00
7957a470fd finally implement keyboard binding for group toggling
idea for the
    "slightly-less-abhorrent-hack-but-a-hack-nonetheless-TM" from oga@

grab and ungrab the keyboard to get around some silly X apps that like
stealing events

ok oga@
2008-05-19 12:56:58 +00:00
a94f4bbb7a send the correct x/y coordinates to XConfigureWindow()
fixes some windows that seem as if they don't fit; noticed by Edd Barrett.

ok oga@
2008-05-18 20:06:36 +00:00
5a0128bdc7 remove extra calls to client_draw_border()
ok oga@
2008-05-18 20:00:16 +00:00
5fee379cb5 fix backwards logic in example; found by oga@ 2008-05-18 19:57:43 +00:00
oga
b700be764a Fix two problems with conf_unbind():
1) it used TAILQ_FOREACH() when it's removing entrys from the list, this
   is bad.
2) We didn't free key, so there was a small memleak too.

also rework conf_bindname's logic slightly to be more simple.

ok okan@
2008-05-18 19:47:19 +00:00
oga
27b023ebcb Kill conf_get_int(), it was a silly function anyway.
Since it's only used once just put the (simplified) logic into
conf_client() instead. This  means we can kill an enum and
CONF_IGNORECASE, too.

ok okan@
2008-05-18 19:43:50 +00:00
oga
5c402536fa group_ctx->name is only used in this one function, and for now it
corresponds directly to the static list of group names. Just use the
static list and stop strdup()ing a new version for the context struct.
Since that never got freed this also fixes a small memleak.

Kill some unused variables while i'm here.

ok okan@
2008-05-18 19:38:18 +00:00
oga
a21a064a9b When we're cleaning out the lists in parse_config and conf_clear it's a
lot simpler just to do while (entry = TAILQ_FIRST(head)) than to do a
for () over the whole lot. Simpler, shorter and probably faster.

ok okan@
2008-05-18 19:34:09 +00:00
0f50af616e - re-order and mostly re-write cwmrc(5)
- merge example config file into cwmrc(5) and remove (little good in here)

feedback from jmc@ - thanks!

ok simon@
2008-05-18 19:33:36 +00:00
9a58e74401 fix cwm's current XShape support from Edd Barrett -thanks.
XShape events should be handled at some point.

ok oga@
2008-05-17 03:59:54 +00:00
oga
5034a77849 KNF, no binary change.
From Pierre Riteau. Thanks!
2008-05-15 22:18:00 +00:00
ec77265b87 tiny bit of knf
ok oga@
2008-05-15 21:56:21 +00:00
458f96936d Signal handler of SIGCHLD calls waitpid() which sets errno on error. To
avoid clubbering of errno in normal context, save_errno got introduced.

ok oga
2008-05-06 15:12:04 +00:00
oga
cd0ce46817 Rework the alt-tabbing code to be a lot simpler.
Diff mostly from Edd Barrett, with some minor changes from me.
Unfortunately the issue where apps like gvim and xpdf are stealing
keyrelease events causing the ordering to be messed up, but this is a
lot better. A fix for the aforementioned issue shall be forthcoming,
once a good one's been found.

ok okan@, also tested by todd@
2008-05-01 18:01:13 +00:00
79569a4d59 Allow slashes in unquoted strings.
From Pierre Riteau
Makes sense to oga@
2008-04-29 20:17:28 +00:00
e3971fc758 nits from Pierre Riteau - thanks!
ok oga@
2008-04-28 01:27:46 +00:00
oga
898bfff36a merge kbfunc_{ptrmove,client_{move,resize}} into one function that takes a flag,
this code was almost identical...

ok okan.
2008-04-16 13:47:29 +00:00
oga
cd5c340e01 remove infowin. It slipped out of the last commit, for some reason. 2008-04-16 13:40:34 +00:00
oga
f473dc3d12 Replace a few leftover calls to strdup and calloc with xstrdup and xcalloc
respectively.

ok okan.
2008-04-16 13:38:09 +00:00
oga
f67772be65 Remove screen_infomsg(), nothing uses it.
ok okan.
2008-04-16 13:35:37 +00:00
oga
642afbdf8c kill an unused struct member.
ok okan
2008-04-16 13:33:26 +00:00
oga
d5794a6b02 make the argument parser for commands accept quoted strings, while i'm
there make u_spawn use exec_wm (renamed to u_exec) for it's execution to
remove duplicated code.

This means constructs like this work in .cwmrc:

bind CM-t "ssh -Y 192.168.1.2 \"xterm -e top\""

or alternatively:

bind CM-t "ssh -Y 192.168.1.2 'xterm -e top'"

"in it goes" okan@.
2008-04-15 21:20:56 +00:00
oga
887a5aa65f Kill dirent_isdir() and dirent_islink() nothing used them since the new
parser went in.

ok okan.
2008-04-15 20:26:50 +00:00
oga
75182c6d9c hit it with the knf stick. 2008-04-15 20:24:41 +00:00
oga
3a94c57afc Add "gap" support to .cwmrc. The options put in here make gaps on the edge
of the screen where an application won't be {,vert}maximized over. used for
placing a statusbar or something like xclock.

Patch from Edd Barrett, with input from myself and okan. Thanks!

ok okan@.
2008-04-15 18:46:58 +00:00
4bbb472a25 - add vi keybindings to search
- allow for ctrl-h as well

discussion with and ok oga@
2008-04-15 18:33:13 +00:00
fe80d40063 malloc -> calloc
suggested by and ok oga@
2008-04-15 18:22:08 +00:00
343ec1bb4f remove alt-tab menu
discussed with a few

ok oga@
2008-04-09 18:10:47 +00:00
oga
eb77aabea1 No cookie for okan.
fix use-after-free that broke exec's path getting stuff.

``paths'' isn't used anymore, but pointers to within that array are still
used in the next loop. delay freeing it until after then.
2008-04-08 17:38:27 +00:00
9702d4cfd7 better 'quit' keybinding default (CMS-q)
feedback from oga@ and simon@

ok oga@ simon@
2008-04-08 14:12:28 +00:00
539b5c6534 Make _xev_quit "volatile sig_atomic_t" for proper correctness.
Noticed by oga@, thanks!
2008-04-08 00:09:50 +00:00
0f18223042 Add quit function, bind it per default to CM-q and change exec_wm
binding to CM-w.

Inital diff from Gleydson Soares
Feedback from oga@ and okan@

ok oga@
2008-04-07 23:47:09 +00:00
cb2cc70c3f - use $PATH before _PATH_DEFPATH, from Tim van der Molen
- plug leak, noticed by oga

feedback and ok oga@
2008-04-05 21:09:19 +00:00
ef0859de20 Sync usage() with reality and manpage.
"ok, but no cookie" oga@
2008-04-03 13:58:57 +00:00
oga
34c0a0635f Fix a couple of issues with the maximization code.
If a window is vertically maximized, then resized, before the MAXIMIZED
flag wasn't removed, now it is. so doing a resize then does the right
thing.

Also, separate flags are needed for vertical and normal maximziation,
else when you do vertical-maximize, followed by maximize, the window
returns to it's original size.

ok simon@, okan@
2008-03-26 15:45:42 +00:00
e704b57d33 Introduce bsd.xconf.mk to set default config variables used
at several places in the Xenocara build in a central place instead
of duplicating the checks all over the place. ok oga@.
2008-03-25 23:41:50 +00:00
c3aa344e78 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@
2008-03-23 15:09:21 +00:00
38ff7a904e allow autogrouping and sticky mode to work together
ok oga@
2008-03-22 21:34:07 +00:00
oga
36c1aac90f Rip out, burn, and dance around the grave of group-edit mode.
I've yet to speak to anyone who uses it, so just kill it.

You can still add/remove from groups using the mouse binding.  Groups
may get a re-work sometime soon if i have a stroke of genius.

knocks about 4k off the i386 binary for me.

ok okan@, todd@.
2008-03-22 15:09:45 +00:00
oga
cd46788d85 Remove a bunch of unused variables and incorrect comments.
"ok with me" okan@.
2008-03-22 14:09:02 +00:00
oga
fead0d511f As mentioned in my last commit, there was an issue where the switching
code would always assume that the number of windows to switch to was
three if there were more windows hidden. Check for CLIENT_HIDDEN when we
count. Now it counts correctly.

ok simon@.
2008-03-19 00:18:28 +00:00
oga
f85ba10437 client_{,r}cycle() doens't need a client. so get rid of KBFUNC_NEEDSCLIENT.
this prevents the issue where you close or hide a window, and end up
with no client selected (you're on the root window). When that happened
alt-tab failed.  This, however, exposed to me a few more issues in the
alt-tabbing code:

-the code to detect how many lines we need looks bogus (you always get
three)

-alt-tabbing when everything is hidden always reawakes the most recent
window.

Fixes for these will be forthcoming.

ok simon@.
2008-03-18 00:48:56 +00:00
a466ddaa2d typo fix. ok oga 2008-03-14 14:38:10 +00:00
25 changed files with 2904 additions and 2781 deletions

View File

@ -1,23 +1,22 @@
# $OpenBSD$
.include <bsd.own.mk>
X11BASE?= /usr/X11R6
.include <bsd.xconf.mk>
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
SRCS= calmwm.c screen.c xmalloc.c client.c grab.c menu.c \
search.c util.c xutil.c conf.c input.c xevents.c group.c \
kbfunc.c mousefunc.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
LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lXau -lXdmcp -lXext \
-lfontconfig -lexpat -lfreetype -lz
MANDIR= ${X11BASE}/man/cat
MAN= cwm.1 cwmrc.5
CLEANFILES= cwm.cat1
CLEANFILES= cwm.cat1 cwmrc.cat5
obj: _xenocara_obj

2
README
View File

@ -17,7 +17,7 @@ DESCRIPTION
base of evilwm did not accomodate well for the new features added.
So calmwm was written from scratch.
Its main goal is to be as efficient as possible, while providing
Its main goal is to be as efficient as possible, while providing
a very clean, simple & attractive aesthetic.
cwm has several novel features, including the ability to search

2
TODO
View File

@ -9,7 +9,7 @@
register handlers, with the ability to select, for example,
a window, or an event, etc. (no, maybe not...)
- ignoreq, always lower them. perhaps implement by lowering the entire
- ignoreq, always lower them. perhaps implement by lowering the entire
queue on each XLower...
- search window should try to stay inside of the screen boundaries.

169
calmwm.c
View File

@ -38,38 +38,30 @@ struct client_ctx_q Clientq;
int Doshape, Shape_ev;
int Starting;
struct conf Conf;
struct fontdesc *DefaultFont;
char *DefaultFontName;
/* From TWM */
#define gray_width 2
#define gray_height 2
static char gray_bits[] = {0x02, 0x01};
static void _sigchld_cb(int);
static void _sigchld_cb(int);
static void dpy_init(const char *);
int
main(int argc, char **argv)
{
int ch;
int conf_flags = 0;
const char *conf_file = NULL;
char *display_name = NULL;
int ch;
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':
conf_file = optarg;
break;
case 'd':
display_name = optarg;
break;
case 's':
conf_flags |= CONF_STICKY_GROUPS;
break;
case 'f':
DefaultFontName = xstrdup(optarg);
break;
default:
usage();
}
@ -78,19 +70,20 @@ main(int argc, char **argv)
argv += optind;
/* Ignore a few signals. */
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
err(1, "signal");
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
err(1, "signal");
if (signal(SIGCHLD, _sigchld_cb) == SIG_ERR)
err(1, "signal");
if (signal(SIGCHLD, _sigchld_cb) == SIG_ERR)
err(1, "signal");
group_init();
Starting = 1;
conf_setup(&Conf);
Conf.flags |= conf_flags;
dpy_init(display_name);
bzero(&Conf, sizeof(Conf));
conf_setup(&Conf, conf_file);
client_setup();
x_setup(display_name);
x_setup();
Starting = 0;
xev_init();
@ -108,6 +101,7 @@ main(int argc, char **argv)
XEV_QUICK(NULL, NULL, Expose, xev_handle_expose, NULL);
XEV_QUICK(NULL, NULL, DestroyNotify, xev_handle_destroynotify, NULL);
XEV_QUICK(NULL, NULL, ClientMessage, xev_handle_clientmessage, NULL);
XEV_QUICK(NULL, NULL, MappingNotify, xev_handle_mapping, NULL);
xev_loop();
@ -115,22 +109,28 @@ main(int argc, char **argv)
}
void
x_setup(char *display_name)
dpy_init(const char *dpyname)
{
int i;
struct screen_ctx *sc;
char *fontname;
int i;
TAILQ_INIT(&Screenq);
if ((X_Dpy = XOpenDisplay(display_name)) == NULL)
if ((X_Dpy = XOpenDisplay(dpyname)) == NULL)
errx(1, "unable to open display \"%s\"",
XDisplayName(display_name));
XDisplayName(dpyname));
XSetErrorHandler(x_errorhandler);
Doshape = XShapeQueryExtension(X_Dpy, &Shape_ev, &i);
TAILQ_INIT(&Screenq);
}
void
x_setup(void)
{
struct screen_ctx *sc;
struct keybinding *kb;
int i;
Nscreens = ScreenCount(X_Dpy);
for (i = 0; i < (int)Nscreens; i++) {
XMALLOC(sc, struct screen_ctx);
@ -138,6 +138,14 @@ x_setup(char *display_name)
TAILQ_INSERT_TAIL(&Screenq, sc, entry);
}
/*
* XXX key grabs weren't done before, since Screenq was empty,
* do them here for now (this needs changing).
*/
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_grab(&Conf, kb);
Cursor_move = XCreateFontCursor(X_Dpy, XC_fleur);
Cursor_resize = XCreateFontCursor(X_Dpy, XC_bottom_right_corner);
Cursor_select = XCreateFontCursor(X_Dpy, XC_hand1);
@ -148,17 +156,22 @@ x_setup(char *display_name)
void
x_setupscreen(struct screen_ctx *sc, u_int which)
{
XColor tmp;
XGCValues gv, gv1/* , gv2 */;
Window *wins, w0, w1;
u_int nwins, i = 0;
XWindowAttributes winattr;
XSetWindowAttributes rootattr;
struct keybinding *kb;
XColor tmp;
XGCValues gv;
Window *wins, w0, w1;
XWindowAttributes winattr;
XSetWindowAttributes rootattr;
u_int nwins, i;
Curscreen = sc;
sc->display = x_screenname(which);
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, which);
sc->xmax = DisplayWidth(X_Dpy, sc->which);
sc->ymax = DisplayHeight(X_Dpy, sc->which);
XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, which),
"black", &sc->fgcolor, &tmp);
XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, which),
@ -174,25 +187,22 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, which),
"black", &sc->blackcolor, &tmp);
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
xu_key_grab(sc->rootwin, kb->modmask, kb->keysym);
sc->blackpixl = BlackPixel(X_Dpy, sc->which);
sc->whitepixl = WhitePixel(X_Dpy, sc->which);
sc->bluepixl = sc->fccolor.pixel;
sc->redpixl = sc->redcolor.pixel;
sc->cyanpixl = sc->cyancolor.pixel;
sc->gray = XCreatePixmapFromBitmapData(X_Dpy, sc->rootwin,
gray_bits, gray_width, gray_height,
sc->blackpixl, sc->whitepixl, DefaultDepth(X_Dpy, sc->which));
sc->gray = XCreatePixmapFromBitmapData(X_Dpy, sc->rootwin,
gray_bits, gray_width, gray_height,
sc->blackpixl, sc->whitepixl, DefaultDepth(X_Dpy, sc->which));
sc->blue = XCreatePixmapFromBitmapData(X_Dpy, sc->rootwin,
gray_bits, gray_width, gray_height,
sc->bluepixl, sc->whitepixl, DefaultDepth(X_Dpy, sc->which));
sc->blue = XCreatePixmapFromBitmapData(X_Dpy, sc->rootwin,
gray_bits, gray_width, gray_height,
sc->bluepixl, sc->whitepixl, DefaultDepth(X_Dpy, sc->which));
sc->red = XCreatePixmapFromBitmapData(X_Dpy, sc->rootwin,
gray_bits, gray_width, gray_height,
sc->red = XCreatePixmapFromBitmapData(X_Dpy, sc->rootwin,
gray_bits, gray_width, gray_height,
sc->redpixl, sc->whitepixl, DefaultDepth(X_Dpy, sc->which));
gv.foreground = sc->blackpixl^sc->whitepixl;
@ -205,32 +215,16 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
GCForeground|GCBackground|GCFunction|
GCLineWidth|GCSubwindowMode, &gv);
sc->hlgc = XCreateGC(X_Dpy, sc->rootwin,
GCForeground|GCBackground|GCFunction|
GCLineWidth|GCSubwindowMode, &gv);
gv1.function = GXinvert;
gv1.subwindow_mode = IncludeInferiors;
gv1.line_width = 1;
sc->invgc = XCreateGC(X_Dpy, sc->rootwin,
GCFunction|GCSubwindowMode|GCLineWidth, &gv1);
font_init(sc);
DefaultFont = font_getx(sc, DefaultFontName);
conf_font(&Conf);
/*
* XXX - this should *really* be in screen_init(). ordering
* problem.
*/
TAILQ_INIT(&sc->mruq);
/* Initialize menu window. */
grab_menuinit(sc);
search_init(sc);
menu_init(sc);
/* Deal with existing clients. */
XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins);
XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins);
for (i = 0; i < nwins; i++) {
XGetWindowAttributes(X_Dpy, wins[i], &winattr);
@ -244,15 +238,13 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
}
XFree(wins);
Curscreen = sc; /* XXX */
screen_init();
screen_updatestackingorder();
rootattr.event_mask = ChildMask|PropertyChangeMask|EnterWindowMask|
LeaveWindowMask|ColormapChangeMask|ButtonMask;
XChangeWindowAttributes(X_Dpy, sc->rootwin,
/* CWCursor| */CWEventMask, &rootattr);
CWEventMask, &rootattr);
XSync(X_Dpy, False);
@ -262,8 +254,8 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
char *
x_screenname(int which)
{
char *cp, *dstr, *sn;
size_t snlen;
char *cp, *dstr, *sn;
size_t snlen;
if (which > 9)
errx(1, "Can't handle more than 9 screens. If you need it, "
@ -271,10 +263,10 @@ x_screenname(int which)
dstr = xstrdup(DisplayString(X_Dpy));
if ((cp = rindex(dstr, ':')) == NULL)
if ((cp = strrchr(dstr, ':')) == NULL)
return (NULL);
if ((cp = index(cp, '.')) != NULL)
if ((cp = strchr(cp, '.')) != NULL)
*cp = '\0';
snlen = strlen(dstr) + 3; /* string, dot, number, null */
@ -301,11 +293,11 @@ x_errorhandler(Display *dpy, XErrorEvent *e)
}
#endif
if (Starting &&
e->error_code == BadAccess &&
e->request_code == X_GrabKey)
if (Starting &&
e->error_code == BadAccess &&
e->request_code == X_GrabKey)
errx(1, "root window unavailable - perhaps another "
"wm is running?");
"wm is running?");
return (0);
}
@ -313,20 +305,23 @@ x_errorhandler(Display *dpy, XErrorEvent *e)
static void
_sigchld_cb(int which)
{
pid_t pid;
int status;
pid_t pid;
int save_errno = errno;
int status;
/* Collect dead children. */
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
/* Collect dead children. */
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
errno = save_errno;
}
__dead void
usage(void)
{
extern char *__progname;
extern char *__progname;
fprintf(stderr, "usage: %s [-s] [-d display] [-f fontname] \n", __progname);
fprintf(stderr, "usage: %s [-c file] [-d display]\n", __progname);
exit(1);
}

577
calmwm.h
View File

@ -23,16 +23,12 @@
#define CALMWM_MAXNAMELEN 256
#include "hash.h"
#undef MIN
#undef MAX
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
enum conftype {
CONF_BWIDTH, CONF_IGNORE, CONF_NOTIFIER,
};
#define CONFFILE ".cwmrc"
#define ChildMask (SubstructureRedirectMask|SubstructureNotifyMask)
#define ButtonMask (ButtonPressMask|ButtonReleaseMask)
@ -42,77 +38,53 @@ struct client_ctx;
TAILQ_HEAD(cycle_entry_q, client_ctx);
/* #define CYCLE_FOREACH_MRU(cy, ctx) TAILQ_FOREACH((ctx), */
struct screen_ctx;
struct fontdesc {
const char *name;
XftFont *fn;
struct screen_ctx *sc;
HASH_ENTRY(fontdesc) node;
};
int fontdesc_cmp(struct fontdesc *a, struct fontdesc *b);
HASH_HEAD(fonthash, fontdesc, 16);
HASH_PROTOTYPE(fonthash, fontdesc, node, fontdesc_cmp);
struct screen_ctx {
TAILQ_ENTRY(screen_ctx) entry;
u_int which;
Window rootwin;
Window menuwin;
Window searchwin;
Window groupwin;
Window infowin;
Colormap colormap;
GC invcg;
XColor bgcolor, fgcolor, fccolor, redcolor, cyancolor,
whitecolor, blackcolor;
char *display;
unsigned long blackpixl, whitepixl, redpixl, bluepixl, cyanpixl;
GC gc, invgc, hlgc;
GC gc;
Pixmap gray, blue, red;
int altpersist;
int maxinitialised;
int xmax;
int ymax;
FILE *notifier;
struct cycle_entry_q mruq;
struct client_ctx* cycle_client;
struct fonthash fonthash;
XftDraw *xftdraw;
XftColor xftcolor;
XftDraw *xftdraw;
XftColor xftcolor;
};
TAILQ_HEAD(screen_ctx_q, screen_ctx);
#define CLIENT_PROTO_DELETE 0x01
#define CLIENT_PROTO_TAKEFOCUS 0x02
#define CLIENT_PROTO_DELETE 0x01
#define CLIENT_PROTO_TAKEFOCUS 0x02
#define CLIENT_MAXNAMEQLEN 5
#define CLIENT_MAXNAMEQLEN 5
#define CLIENT_HIDDEN 0x01
#define CLIENT_IGNORE 0x02
#define CLIENT_INQUEUE 0x04 /* tmp used by search code */
#define CLIENT_MAXIMIZED 0x08
#define CLIENT_HIDDEN 0x01
#define CLIENT_IGNORE 0x02
#define CLIENT_DOMAXIMIZE 0x04
#define CLIENT_MAXIMIZED 0x08
#define CLIENT_DOVMAXIMIZE 0x10
#define CLIENT_VMAXIMIZED 0x20
#define CLIENT_HIGHLIGHT_BLUE 1
#define CLIENT_HIGHLIGHT_RED 2
#define CLIENT_HIGHLIGHT_BLUE 1
#define CLIENT_HIGHLIGHT_RED 2
struct winname {
TAILQ_ENTRY(winname) entry;
char *name;
TAILQ_ENTRY(winname) entry;
char *name;
};
TAILQ_HEAD(winname_q, winname);
@ -127,7 +99,7 @@ struct client_ctx {
Window win;
XSizeHints *size;
Colormap cmap;
Colormap cmap;
Window pwin;
@ -156,27 +128,31 @@ struct client_ctx {
int highlight;
char *matchname;
struct group_ctx *group;
int groupcommit;
struct group_ctx *group;
int stackingorder;
int stackingorder;
char *app_class;
char *app_name;
char *app_cliarg;
char *app_class;
char *app_name;
char *app_cliarg;
};
TAILQ_HEAD(client_ctx_q, client_ctx);
static char *shortcut_to_name[] = {
"nogroup", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine"
};
struct group_ctx {
TAILQ_ENTRY(group_ctx) entry;
struct client_ctx_q clients;
char *name;
int shortcut;
int hidden;
int nhidden;
int highstack;
};
TAILQ_ENTRY(group_ctx) entry;
struct client_ctx_q clients;
int shortcut;
int hidden;
int nhidden;
int highstack;
};
TAILQ_HEAD(group_ctx_q, group_ctx);
@ -193,80 +169,103 @@ TAILQ_HEAD(autogroupwin_q, autogroupwin);
/* NULL/0 values indicate match any. */
struct xevent {
TAILQ_ENTRY(xevent) entry;
Window *xev_win;
Window *xev_root;
int xev_type;
void (*xev_cb)(struct xevent *, XEvent *);
void *xev_arg;
TAILQ_ENTRY(xevent) entry;
Window *xev_win;
Window *xev_root;
int xev_type;
void (*xev_cb)(struct xevent *, XEvent *);
void *xev_arg;
};
TAILQ_HEAD(xevent_q, xevent);
/* Keybindings */
enum kbtype {
KB_DELETE, KB_NEWTERM0, KB_NEWTERM1, KB_HIDE,
KB_LOWER, KB_RAISE, KB_SEARCH, KB_CYCLE, KB_LABEL,
KB_GROUPSELECT, KB_VERTMAXIMIZE, KB_MAXIMIZE,
#define CWM_MOVE 0x01
#define CWM_RESIZE 0x02
#define CWM_PTRMOVE 0x04
#define CWM_BIGMOVE 0x08
#define CWM_UP 0x10
#define CWM_DOWN 0x20
#define CWM_LEFT 0x40
#define CWM_RIGHT 0x80
/* Group numbers need to be in order. */
KB_GROUP_1, KB_GROUP_2, KB_GROUP_3, KB_GROUP_4, KB_GROUP_5,
KB_GROUP_6, KB_GROUP_7, KB_GROUP_8, KB_GROUP_9, KB_NOGROUP,
KB_NEXTGROUP, KB_PREVGROUP,
KB_MOVE_WEST, KB_MOVE_EAST, KB_MOVE_NORTH, KB_MOVE_SOUTH,
KB__LAST
/*
* Match a window.
*/
#define CONF_MAX_WINTITLE 256
struct winmatch {
TAILQ_ENTRY(winmatch) entry;
char title[CONF_MAX_WINTITLE];
};
#define CWM_BIGMOVE 0x1000
enum directions {
CWM_UP=0, CWM_DOWN, CWM_LEFT, CWM_RIGHT,
};
TAILQ_HEAD(winmatch_q, winmatch);
/* for cwm_exec */
#define CWM_EXEC_PROGRAM 0x1
#define CWM_EXEC_WM 0x2
/* for alt-tab */
#define CWM_CYCLE 0x0
#define CWM_RCYCLE 0x1
/* for group cycle */
#define CWM_CYCLEGROUP 0x0
#define CWM_RCYCLEGROUP 0x1
#define KBFLAG_NEEDCLIENT 0x01
#define KBFLAG_FINDCLIENT 0x02
#define KBTOGROUP(X) ((X) - 1)
struct keybinding {
int modmask;
int keysym;
int keycode;
int flags;
void (*callback)(struct client_ctx *, void *);
void *argument;
TAILQ_ENTRY(keybinding) entry;
int modmask;
int keysym;
int keycode;
int flags;
void (*callback)(struct client_ctx *, void *);
void *argument;
TAILQ_ENTRY(keybinding) entry;
};
struct cmd {
TAILQ_ENTRY(cmd) entry;
int flags;
#define CMD_STATIC 0x01 /* static configuration in conf.c */
char image[MAXPATHLEN];
char label[256];
TAILQ_ENTRY(cmd) entry;
int flags;
char image[MAXPATHLEN];
char label[256];
/* (argv) */
};
struct mousebinding {
int modmask;
int button;
int context;
void (*callback)(struct client_ctx *, void *);
TAILQ_ENTRY(mousebinding) entry;
};
#define MOUSEBIND_CTX_ROOT 1
#define MOUSEBIND_CTX_WIN 2
TAILQ_HEAD(keybinding_q, keybinding);
TAILQ_HEAD(cmd_q, cmd);
TAILQ_HEAD(mousebinding_q, mousebinding);
/* 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;
struct mousebinding_q mousebindingq;
int flags;
#define CONF_STICKY_GROUPS 0x0001
#define CONF_STICKY_GROUPS 0x0001
int flags;
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
#define DEFAULTFONTNAME "sans-serif:pixelsize=14:bold"
char *DefaultFontName;
XftFont *DefaultFont;
u_int FontHeight;
int gap_top, gap_bottom, gap_left, gap_right;
};
/* Menu stuff */
@ -274,13 +273,13 @@ struct conf {
#define MENU_MAXENTRY 50
struct menu {
TAILQ_ENTRY(menu) entry;
TAILQ_ENTRY(menu) resultentry;
TAILQ_ENTRY(menu) entry;
TAILQ_ENTRY(menu) resultentry;
char text[MENU_MAXENTRY + 1];
char print[MENU_MAXENTRY + 1];
void *ctx;
short dummy;
char text[MENU_MAXENTRY + 1];
char print[MENU_MAXENTRY + 1];
void *ctx;
short dummy;
};
TAILQ_HEAD(menu_q, menu);
@ -294,215 +293,205 @@ enum ctltype {
/* MWM hints */
struct mwm_hints {
u_long flags;
u_long functions;
u_long decorations;
u_long flags;
u_long functions;
u_long decorations;
};
#define MWM_NUMHINTS 3
#define PROP_MWM_HINTS_ELEMENTS 3
#define MWM_HINTS_DECORATIONS (1 << 1)
#define MWM_DECOR_ALL (1 << 0)
#define MWM_DECOR_BORDER (1 << 1)
#define PROP_MWM_HINTS_ELEMENTS 3
#define MWM_HINTS_DECORATIONS (1 << 1)
#define MWM_DECOR_ALL (1 << 0)
#define MWM_DECOR_BORDER (1 << 1)
int input_keycodetrans(KeyCode, u_int, enum ctltype *, char *, int);
int input_keycodetrans(KeyCode, u_int, enum ctltype *,
char *);
int x_errorhandler(Display *, XErrorEvent *);
void x_setup(char *display_name);
char *x_screenname(int);
void x_setupscreen(struct screen_ctx *, u_int);
__dead void usage(void);
int x_errorhandler(Display *, XErrorEvent *);
void x_setup(void);
char *x_screenname(int);
void x_setupscreen(struct screen_ctx *, u_int);
__dead void usage(void);
struct client_ctx *client_find(Window);
void client_setup(void);
struct client_ctx *client_new(Window, struct screen_ctx *, int);
int client_delete(struct client_ctx *, int, int);
void client_setactive(struct client_ctx *, int);
void client_gravitate(struct client_ctx *, int);
void client_resize(struct client_ctx *);
void client_lower(struct client_ctx *);
void client_raise(struct client_ctx *);
void client_move(struct client_ctx *);
void client_leave(struct client_ctx *);
void client_send_delete(struct client_ctx *);
struct client_ctx *client_current(void);
void client_hide(struct client_ctx *);
void client_unhide(struct client_ctx *);
void client_nocurrent(void);
void client_setname(struct client_ctx *);
void client_warp(struct client_ctx *);
void client_ptrwarp(struct client_ctx *);
void client_ptrsave(struct client_ctx *);
void client_draw_border(struct client_ctx *);
void client_update(struct client_ctx *);
void client_cycle(struct client_ctx *);
void client_placecalc(struct client_ctx *);
void client_maximize(struct client_ctx *);
void client_vertmaximize(struct client_ctx *);
u_long client_bg_pixel(struct client_ctx *);
Pixmap client_bg_pixmap(struct client_ctx *);
void client_map(struct client_ctx *cc);
void client_mtf(struct client_ctx *cc);
struct client_ctx *client_cyclenext(int reverse);
void client_cycleinfo(struct client_ctx *cc);
void client_altrelease();
struct client_ctx *client_mrunext(struct client_ctx *cc);
struct client_ctx *client_mruprev(struct client_ctx *cc);
void client_gethints(struct client_ctx *cc);
void client_freehints(struct client_ctx *cc);
struct client_ctx *client_find(Window);
void client_setup(void);
struct client_ctx *client_new(Window, struct screen_ctx *, int);
int client_delete(struct client_ctx *, int, int);
void client_setactive(struct client_ctx *, int);
void client_gravitate(struct client_ctx *, int);
void client_resize(struct client_ctx *);
void client_lower(struct client_ctx *);
void client_raise(struct client_ctx *);
void client_move(struct client_ctx *);
void client_leave(struct client_ctx *);
void client_send_delete(struct client_ctx *);
struct client_ctx *client_current(void);
void client_hide(struct client_ctx *);
void client_unhide(struct client_ctx *);
void client_nocurrent(void);
void client_setname(struct client_ctx *);
void client_warp(struct client_ctx *);
void client_ptrwarp(struct client_ctx *);
void client_ptrsave(struct client_ctx *);
void client_draw_border(struct client_ctx *);
void client_update(struct client_ctx *);
void client_placecalc(struct client_ctx *);
void client_maximize(struct client_ctx *);
void client_vertmaximize(struct client_ctx *);
u_long client_bg_pixel(struct client_ctx *);
Pixmap client_bg_pixmap(struct client_ctx *);
void client_map(struct client_ctx *);
void client_mtf(struct client_ctx *);
struct client_ctx *client_cycle(int);
struct client_ctx *client_mrunext(struct client_ctx *);
struct client_ctx *client_mruprev(struct client_ctx *);
void client_gethints(struct client_ctx *);
void client_freehints(struct client_ctx *);
void client_do_shape(struct client_ctx *);
void xev_handle_maprequest(struct xevent *, XEvent *);
void xev_handle_unmapnotify(struct xevent *, XEvent *);
void xev_handle_destroynotify(struct xevent *, XEvent *);
void xev_handle_configurerequest(struct xevent *, XEvent *);
void xev_handle_propertynotify(struct xevent *, XEvent *);
void xev_handle_enternotify(struct xevent *, XEvent *);
void xev_handle_leavenotify(struct xevent *, XEvent *);
void xev_handle_buttonpress(struct xevent *, XEvent *);
void xev_handle_buttonrelease(struct xevent *, XEvent *);
void xev_handle_keypress(struct xevent *, XEvent *);
void xev_handle_keyrelease(struct xevent *, XEvent *);
void xev_handle_expose(struct xevent *, XEvent *);
void xev_handle_clientmessage(struct xevent *, XEvent *);
struct menu *menu_filter(struct menu_q *, char *, char *, int,
void (*)(struct menu_q *, struct menu_q *, char *),
void (*)(struct menu *, int));
void menu_init(struct screen_ctx *);
void xev_handle_maprequest(struct xevent *, XEvent *);
void xev_handle_unmapnotify(struct xevent *, XEvent *);
void xev_handle_destroynotify(struct xevent *, XEvent *);
void xev_handle_configurerequest(struct xevent *, XEvent *);
void xev_handle_propertynotify(struct xevent *, XEvent *);
void xev_handle_enternotify(struct xevent *, XEvent *);
void xev_handle_leavenotify(struct xevent *, XEvent *);
void xev_handle_buttonpress(struct xevent *, XEvent *);
void xev_handle_buttonrelease(struct xevent *, XEvent *);
void xev_handle_keypress(struct xevent *, XEvent *);
void xev_handle_keyrelease(struct xevent *, XEvent *);
void xev_handle_expose(struct xevent *, XEvent *);
void xev_handle_clientmessage(struct xevent *, XEvent *);
void xev_handle_shape(struct xevent *, XEvent *);
void xev_handle_mapping(struct xevent *, XEvent *);
#define XEV_QUICK(a, b, c, d, e) do { \
xev_register(xev_new(a, b, c, d, e)); \
} while (0)
void xev_reconfig(struct client_ctx *); /* XXX should be xu_ */
/* XXX should be xu_ */
void xev_reconfig(struct client_ctx *);
void xev_init(void);
struct xevent *xev_new(Window *, Window *, int, void (*)(struct xevent *, XEvent *), void *);
void xev_register(struct xevent *);
void xev_loop(void);
void xev_init(void);
struct xevent *xev_new(Window *, Window *, int,
void (*)(struct xevent *, XEvent *), void *);
void xev_register(struct xevent *);
void xev_loop(void);
int xu_ptr_grab(Window, int, Cursor);
int xu_btn_grab(Window, int, u_int);
int xu_ptr_regrab(int, Cursor);
void xu_btn_ungrab(Window, int, u_int);
void xu_ptr_ungrab(void);
void xu_ptr_setpos(Window, int, int);
void xu_ptr_getpos(Window, int *, int *);
void xu_key_grab(Window, int, int);
void xu_sendmsg(struct client_ctx *, Atom, long);
int xu_getprop(struct client_ctx *, Atom, Atom, long, u_char **);
char *xu_getstrprop(struct client_ctx *, Atom atm);
void xu_setstate(struct client_ctx *, int);
int xu_getstate(struct client_ctx *, int *);
void xu_key_grab_keycode(Window, int, int);
int xu_ptr_grab(Window, int, Cursor);
void xu_btn_grab(Window, int, u_int);
int xu_ptr_regrab(int, Cursor);
void xu_btn_ungrab(Window, int, u_int);
void xu_ptr_ungrab(void);
void xu_ptr_setpos(Window, int, int);
void xu_ptr_getpos(Window, int *, int *);
void xu_key_grab(Window, int, int);
void xu_key_ungrab(Window, int, int);
void xu_sendmsg(struct client_ctx *, Atom, long);
int xu_getprop(struct client_ctx *, Atom, Atom, long,
u_char **);
char *xu_getstrprop(struct client_ctx *, Atom atm);
void xu_setstate(struct client_ctx *, int);
int xu_getstate(struct client_ctx *, int *);
int dirent_exists(char *);
int dirent_isdir(char *);
int dirent_islink(char *);
int u_spawn(char *);
void exec_wm(char *);
int u_spawn(char *);
void u_exec(char *);
void grab_sweep(struct client_ctx *);
void grab_drag(struct client_ctx *);
void grab_menuinit(struct screen_ctx *);
void *grab_menu(XButtonEvent *, struct menu_q *);
void grab_label(struct client_ctx *);
void grab_sweep(struct client_ctx *);
void grab_drag(struct client_ctx *);
void xfree(void *);
void *xmalloc(size_t);
void *xcalloc(size_t);
char *xstrdup(const char *);
void xfree(void *);
void *xmalloc(size_t);
void *xcalloc(size_t, size_t);
char *xstrdup(const char *);
#define XMALLOC(p, t) ((p) = (t *)xmalloc(sizeof * (p)))
#define XCALLOC(p, t) ((p) = (t *)xcalloc(sizeof * (p)))
#define XCALLOC(p, t) ((p) = (t *)xcalloc(1, sizeof * (p)))
void screen_init(void);
struct screen_ctx *screen_fromroot(Window);
struct screen_ctx *screen_current(void);
void screen_updatestackingorder(void);
void screen_infomsg(char *);
struct screen_ctx *screen_fromroot(Window);
struct screen_ctx *screen_current(void);
void screen_updatestackingorder(void);
void conf_setup(struct conf *);
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);
char *conf_get_str(struct client_ctx *, enum conftype);
void conf_setup(struct conf *, const char *);
void conf_client(struct client_ctx *);
void conf_grab(struct conf *, struct keybinding *);
void conf_ungrab(struct conf *, struct keybinding *);
void conf_bindname(struct conf *, char *, char *);
void conf_unbind(struct conf *, struct keybinding *);
void conf_mousebind(struct conf *, char *, char *);
void conf_mouseunbind(struct conf *, struct mousebinding *);
void conf_grab_mouse(struct client_ctx *);
void conf_reload(struct conf *);
void conf_font(struct conf *);
void kbfunc_client_lower(struct client_ctx *, void *);
void kbfunc_client_raise(struct client_ctx *, void *);
void kbfunc_client_search(struct client_ctx *, void *);
void kbfunc_client_hide(struct client_ctx *, void *);
void kbfunc_client_cycle(struct client_ctx *, void *);
void kbfunc_client_rcycle(struct client_ctx *cc, void *arg);
void kbfunc_cmdexec(struct client_ctx *, void *);
void kbfunc_client_label(struct client_ctx *, void *);
void kbfunc_client_delete(struct client_ctx *, void *);
void kbfunc_client_groupselect(struct client_ctx *, void *);
void kbfunc_client_group(struct client_ctx *, void *);
void kbfunc_client_nextgroup(struct client_ctx *, void *);
void kbfunc_client_prevgroup(struct client_ctx *, void *);
void kbfunc_client_nogroup(struct client_ctx *, void *);
void kbfunc_client_maximize(struct client_ctx *, void *);
void kbfunc_client_vmaximize(struct client_ctx *, void *);
void kbfunc_client_move(struct client_ctx *, void *);
void kbfunc_client_resize(struct client_ctx *, void *);
void kbfunc_menu_search(struct client_ctx *, void *);
void kbfunc_exec(struct client_ctx *, void *);
void kbfunc_ptrmove(struct client_ctx *, void *);
void kbfunc_ssh(struct client_ctx *, void *);
void kbfunc_term(struct client_ctx *cc, void *arg);
void kbfunc_lock(struct client_ctx *cc, void *arg);
void kbfunc_client_lower(struct client_ctx *, void *);
void kbfunc_client_raise(struct client_ctx *, void *);
void kbfunc_client_search(struct client_ctx *, void *);
void kbfunc_client_hide(struct client_ctx *, void *);
void kbfunc_client_cycle(struct client_ctx *, void *);
void kbfunc_client_rcycle(struct client_ctx *, void *);
void kbfunc_cmdexec(struct client_ctx *, void *);
void kbfunc_client_label(struct client_ctx *, void *);
void kbfunc_client_delete(struct client_ctx *, void *);
void kbfunc_client_group(struct client_ctx *, void *);
void kbfunc_client_cyclegroup(struct client_ctx *, void *);
void kbfunc_client_nogroup(struct client_ctx *, void *);
void kbfunc_client_grouptoggle(struct client_ctx *, void *);
void kbfunc_client_maximize(struct client_ctx *, void *);
void kbfunc_client_vmaximize(struct client_ctx *, void *);
void kbfunc_reload(struct client_ctx *, void *);
void kbfunc_quit_wm(struct client_ctx *, void *);
void kbfunc_moveresize(struct client_ctx *, void *);
void kbfunc_menu_search(struct client_ctx *, void *);
void kbfunc_exec(struct client_ctx *, void *);
void kbfunc_ssh(struct client_ctx *, void *);
void kbfunc_term(struct client_ctx *, void *);
void kbfunc_lock(struct client_ctx *, void *);
void search_init(struct screen_ctx *);
struct menu *search_start(struct menu_q *menuq,
void (*match)(struct menu_q *, struct menu_q *, char *),
void (*rank)(struct menu_q *, char *),
void (*print)(struct menu *mi, int),
char *, int);
void search_match_client(struct menu_q *, struct menu_q *, char *);
void search_print_client(struct menu *mi, int list);
void search_match_text(struct menu_q *, struct menu_q *, char *);
void search_match_exec(struct menu_q *, struct menu_q *, char *);
void search_rank_text(struct menu_q *, char *);
void mousefunc_window_resize(struct client_ctx *, void *);
void mousefunc_window_move(struct client_ctx *, void *);
void mousefunc_window_grouptoggle(struct client_ctx *,
void *);
void mousefunc_window_lower(struct client_ctx *, void *);
void mousefunc_window_hide(struct client_ctx *, void *);
void mousefunc_menu_group(struct client_ctx *, void *);
void mousefunc_menu_unhide(struct client_ctx *, void *);
void mousefunc_menu_cmd(struct client_ctx *, void *);
void group_init(void);
void group_select(int);
void group_enter(void);
void group_exit(int);
void group_click(struct client_ctx *);
void group_display_init(struct screen_ctx *);
void group_display_draw(struct screen_ctx *);
void group_display_keypress(KeyCode);
void group_hidetoggle(int);
void group_slide(int);
void group_sticky(struct client_ctx *);
void group_client_delete(struct client_ctx *);
void group_menu(XButtonEvent *);
void group_namemode(void);
void group_alltoggle(void);
void group_deletecurrent(void);
void group_done(void);
void group_sticky_toggle_enter(struct client_ctx *);
void group_sticky_toggle_exit(struct client_ctx *);
void group_autogroup(struct client_ctx *);
void search_match_client(struct menu_q *, struct menu_q *,
char *);
void search_print_client(struct menu *, int);
void search_match_text(struct menu_q *, struct menu_q *,
char *);
void search_match_exec(struct menu_q *, struct menu_q *,
char *);
void notification_init(struct screen_ctx *);
void group_init(void);
void group_hidetoggle(int);
void group_cycle(int);
void group_sticky(struct client_ctx *);
void group_client_delete(struct client_ctx *);
void group_menu(XButtonEvent *);
void group_alltoggle(void);
void group_sticky_toggle_enter(struct client_ctx *);
void group_sticky_toggle_exit(struct client_ctx *);
void group_autogroup(struct client_ctx *);
void font_init(struct screen_ctx *sc);
struct fontdesc *font_get(struct screen_ctx *sc, const char *name);
int font_width(struct fontdesc *fdp, const char *text, int len);
void font_draw(struct fontdesc *fdp, const char *text, int len,
Drawable d, int x, int y);
int font_ascent(struct fontdesc *fdp);
int font_descent(struct fontdesc *fdp);
struct fontdesc *font_getx(struct screen_ctx *sc, const char *name);
void font_init(struct screen_ctx *);
int font_width(const char *, int);
void font_draw(struct screen_ctx *, const char *, int,
Drawable, int, int);
XftFont *font_make(struct screen_ctx *, const char *);
#define font_ascent() Conf.DefaultFont->ascent
#define font_descent() Conf.DefaultFont->descent
#define font_height() Conf.FontHeight
#define CCTOSC(cc) (cc->sc)
@ -525,8 +514,4 @@ extern struct client_ctx_q Clientq;
extern int Doshape, Shape_ev;
extern struct conf Conf;
extern int Groupmode;
extern struct fontdesc *DefaultFont;
#endif /* _CALMWM_H_ */

514
client.c
View File

@ -21,13 +21,11 @@
#include "headers.h"
#include "calmwm.h"
static struct client_ctx *client__cycle(struct client_ctx *cc,
struct client_ctx *(*iter)(struct client_ctx *));
int _inwindowbounds(struct client_ctx *, int, int);
int _inwindowbounds(struct client_ctx *, int, int);
static char emptystring[] = "";
static char emptystring[] = "";
struct client_ctx *_curcc = NULL;
struct client_ctx *_curcc = NULL;
void
client_setup(void)
@ -38,7 +36,7 @@ client_setup(void)
struct client_ctx *
client_find(Window win)
{
struct client_ctx *cc;
struct client_ctx *cc;
TAILQ_FOREACH(cc, &Clientq, entry)
if (cc->pwin == win || cc->win == win)
@ -50,12 +48,12 @@ client_find(Window win)
struct client_ctx *
client_new(Window win, struct screen_ctx *sc, int mapped)
{
struct client_ctx *cc;
long tmp;
XSetWindowAttributes pxattr;
XWindowAttributes wattr;
int x, y, height, width, state;
XWMHints *wmhints;
struct client_ctx *cc;
XSetWindowAttributes pxattr;
XWindowAttributes wattr;
XWMHints *wmhints;
long tmp;
int x, y, height, width, state;
if (win == None)
return (NULL);
@ -67,7 +65,7 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
cc->state = mapped ? NormalState : IconicState;
cc->sc = sc;
cc->win = win;
cc->size= XAllocSizeHints();
cc->size = XAllocSizeHints();
if (cc->size->width_inc == 0)
cc->size->width_inc = 1;
if (cc->size->height_inc == 0)
@ -117,8 +115,8 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
if (xu_getstate(cc, &state) < 0)
state = NormalState;
XSelectInput(X_Dpy, cc->win,
ColormapChangeMask|EnterWindowMask|PropertyChangeMask|KeyReleaseMask);
XSelectInput(X_Dpy, cc->win, ColormapChangeMask | EnterWindowMask |
PropertyChangeMask | KeyReleaseMask);
x = cc->geom.x - cc->bwidth;
y = cc->geom.y - cc->bwidth;
@ -129,18 +127,10 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
width += (cc->bwidth)*2;
height += (cc->bwidth)*2;
}
pxattr.override_redirect = True;
pxattr.background_pixel = sc->bgcolor.pixel;
pxattr.event_mask =
ChildMask|ButtonPressMask|ButtonReleaseMask|
ExposureMask|EnterWindowMask;
/* pxattr.border_pixel = sc->blackpix; */
/* pxattr.background_pixel = sc->whitepix; */
/* cc->pwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, */
/* x, y, width, height, 1, sc->blackpix, sc->whitepix); */
pxattr.event_mask = ChildMask | ButtonPressMask | ButtonReleaseMask |
ExposureMask | EnterWindowMask;
cc->pwin = XCreateWindow(X_Dpy, sc->rootwin, x, y,
width, height, 0, /* XXX */
@ -148,22 +138,7 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
DefaultVisual(X_Dpy, sc->which),
CWOverrideRedirect | CWBackPixel | CWEventMask, &pxattr);
if (Doshape) {
XRectangle *r;
int n, tmp;
XShapeSelectInput(X_Dpy, cc->win, ShapeNotifyMask);
r = XShapeGetRectangles(X_Dpy, cc->win, ShapeBounding, &n, &tmp);
if (n > 1)
XShapeCombineShape(X_Dpy, cc->pwin, ShapeBounding,
0, 0, /* XXX border */
cc->win, ShapeBounding, ShapeSet);
XFree(r);
}
cc->active = 0;
client_draw_border(cc);
XAddToSaveSet(X_Dpy, cc->win);
XSetWindowBorderWidth(X_Dpy, cc->win, 0);
@ -189,29 +164,46 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
client_gethints(cc);
client_update(cc);
if (mapped) {
if (Conf.flags & CONF_STICKY_GROUPS)
group_sticky(cc);
else
group_autogroup(cc);
}
if (mapped)
group_autogroup(cc);
return (cc);
}
void
client_do_shape(struct client_ctx *cc)
{
/* Windows not rectangular require more effort */
XRectangle *r;
int n, tmp;
if (Doshape) {
XShapeSelectInput(X_Dpy, cc->win, ShapeNotifyMask);
r = XShapeGetRectangles(X_Dpy, cc->win, ShapeBounding,
&n, &tmp);
if (n > 1)
XShapeCombineShape(X_Dpy, cc->pwin, ShapeBounding,
cc->bwidth, cc->bwidth, cc->win, ShapeBounding,
ShapeSet);
XFree(r);
}
}
int
client_delete(struct client_ctx *cc, int sendevent, int ignorewindow)
{
struct screen_ctx *sc = CCTOSC(cc);
struct winname *wn;
struct screen_ctx *sc = CCTOSC(cc);
struct winname *wn;
if (cc->state == IconicState && !sendevent)
return (1);
group_client_delete(cc);
XGrabServer(X_Dpy);
XGrabServer(X_Dpy);
xu_setstate(cc, WithdrawnState);
XRemoveFromSaveSet(X_Dpy, cc->win);
@ -233,9 +225,6 @@ client_delete(struct client_ctx *cc, int sendevent, int ignorewindow)
if (_curcc == cc)
_curcc = NULL;
if (sc->cycle_client == cc)
sc->cycle_client = NULL;
XFree(cc->size);
while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) {
@ -255,21 +244,21 @@ client_delete(struct client_ctx *cc, int sendevent, int ignorewindow)
void
client_leave(struct client_ctx *cc)
{
struct screen_ctx *sc;
struct screen_ctx *sc;
if (cc == NULL)
cc = _curcc;
if (cc == NULL)
return;
sc = CCTOSC(cc);
sc = CCTOSC(cc);
xu_btn_ungrab(sc->rootwin, AnyModifier, Button1);
}
void
client_setactive(struct client_ctx *cc, int fg)
{
struct screen_ctx* sc;
struct screen_ctx *sc;
if (cc == NULL)
cc = _curcc;
@ -282,8 +271,7 @@ client_setactive(struct client_ctx *cc, int fg)
XInstallColormap(X_Dpy, cc->cmap);
XSetInputFocus(X_Dpy, cc->win,
RevertToPointerRoot, CurrentTime);
xu_btn_grab(cc->pwin, Mod1Mask, AnyButton);
xu_btn_grab(cc->pwin, ControlMask|Mod1Mask, Button1);
conf_grab_mouse(cc);
/*
* If we're in the middle of alt-tabbing, don't change
* the order please.
@ -311,11 +299,11 @@ client_current(void)
void
client_gravitate(struct client_ctx *cc, int yes)
{
int dx = 0, dy = 0, mult = yes ? 1 : -1;
int gravity = (cc->size->flags & PWinGravity) ?
cc->size->win_gravity : NorthWestGravity;
int dx = 0, dy = 0, mult = yes ? 1 : -1;
int gravity = (cc->size->flags & PWinGravity) ?
cc->size->win_gravity : NorthWestGravity;
switch (gravity) {
switch (gravity) {
case NorthWestGravity:
case SouthWestGravity:
case NorthEastGravity:
@ -324,29 +312,46 @@ client_gravitate(struct client_ctx *cc, int yes)
case NorthGravity:
dy = cc->bwidth;
break;
}
}
cc->geom.x += mult*dx;
cc->geom.y += mult*dy;
cc->geom.x += mult * dx;
cc->geom.y += mult * dy;
}
void
client_maximize(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
if (cc->flags & CLIENT_MAXIMIZED) {
cc->flags &= ~CLIENT_MAXIMIZED;
cc->geom = cc->savegeom;
} else {
XWindowAttributes rootwin_geom;
struct screen_ctx *sc = CCTOSC(cc);
if (!(cc->flags & CLIENT_VMAXIMIZED))
cc->savegeom = cc->geom;
cc->geom.x = Conf.gap_left;
cc->geom.y = Conf.gap_top;
cc->geom.height = sc->ymax - (Conf.gap_top + Conf.gap_bottom);
cc->geom.width = sc->xmax - (Conf.gap_left + Conf.gap_right);
cc->flags |= CLIENT_DOMAXIMIZE;
}
XGetWindowAttributes(X_Dpy, sc->rootwin, &rootwin_geom);
cc->savegeom = cc->geom;
cc->geom.x = 0;
cc->geom.y = 0;
cc->geom.height = rootwin_geom.height;
cc->geom.width = rootwin_geom.width;
cc->flags |= CLIENT_MAXIMIZED;
client_resize(cc);
}
void
client_vertmaximize(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
if (cc->flags & CLIENT_VMAXIMIZED) {
cc->geom = cc->savegeom;
} else {
if (!(cc->flags & CLIENT_MAXIMIZED))
cc->savegeom = cc->geom;
cc->geom.y = cc->bwidth + Conf.gap_top;
cc->geom.height = (sc->ymax - cc->bwidth * 2) -
(Conf.gap_top + Conf.gap_bottom);
cc->flags |= CLIENT_DOVMAXIMIZE;
}
client_resize(cc);
@ -355,13 +360,23 @@ client_maximize(struct client_ctx *cc)
void
client_resize(struct client_ctx *cc)
{
if (cc->flags & (CLIENT_MAXIMIZED | CLIENT_VMAXIMIZED))
cc->flags &= ~(CLIENT_MAXIMIZED | CLIENT_VMAXIMIZED);
if (cc->flags & CLIENT_DOMAXIMIZE) {
cc->flags &= ~CLIENT_DOMAXIMIZE;
cc->flags |= CLIENT_MAXIMIZED;
} else if (cc->flags & CLIENT_DOVMAXIMIZE) {
cc->flags &= ~CLIENT_DOVMAXIMIZE;
cc->flags |= CLIENT_VMAXIMIZED;
}
XMoveResizeWindow(X_Dpy, cc->pwin, cc->geom.x - cc->bwidth,
cc->geom.y - cc->bwidth, cc->geom.width + cc->bwidth*2,
cc->geom.height + cc->bwidth*2);
XMoveResizeWindow(X_Dpy, cc->win, cc->bwidth, cc->bwidth,
cc->geom.width, cc->geom.height);
xev_reconfig(cc);
client_draw_border(cc);
}
void
@ -382,13 +397,12 @@ void
client_raise(struct client_ctx *cc)
{
XRaiseWindow(X_Dpy, cc->pwin);
client_draw_border(cc);
}
void
client_ptrwarp(struct client_ctx *cc)
{
int x = cc->ptr.x, y = cc->ptr.y;
int x = cc->ptr.x, y = cc->ptr.y;
if (x == -1 || y == -1) {
x = cc->geom.width / 2;
@ -406,13 +420,13 @@ client_ptrwarp(struct client_ctx *cc)
void
client_ptrsave(struct client_ctx *cc)
{
int x, y;
int x, y;
xu_ptr_getpos(cc->pwin, &x, &y);
if (_inwindowbounds(cc, x, y)) {
if (_inwindowbounds(cc, x, y)) {
cc->ptr.x = x;
cc->ptr.y = y;
}
}
}
void
@ -420,7 +434,6 @@ client_hide(struct client_ctx *cc)
{
/* XXX - add wm_state stuff */
XUnmapWindow(X_Dpy, cc->pwin);
XUnmapWindow(X_Dpy, cc->win);
cc->active = 0;
cc->flags |= CLIENT_HIDDEN;
@ -433,9 +446,9 @@ client_hide(struct client_ctx *cc)
void
client_unhide(struct client_ctx *cc)
{
XMapWindow(X_Dpy, cc->win);
XMapRaised(X_Dpy, cc->pwin);
cc->highlight = 0;
cc->flags &= ~CLIENT_HIDDEN;
xu_setstate(cc, NormalState);
}
@ -443,7 +456,7 @@ client_unhide(struct client_ctx *cc)
void
client_draw_border(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
struct screen_ctx *sc = CCTOSC(cc);
if (cc->active) {
XSetWindowBackground(X_Dpy, cc->pwin, client_bg_pixel(cc));
@ -465,8 +478,8 @@ client_draw_border(struct client_ctx *cc)
u_long
client_bg_pixel(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
u_long pixl;
struct screen_ctx *sc = CCTOSC(cc);
u_long pixl;
switch (cc->highlight) {
case CLIENT_HIGHLIGHT_BLUE:
@ -486,8 +499,8 @@ client_bg_pixel(struct client_ctx *cc)
Pixmap
client_bg_pixmap(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
Pixmap pix;
struct screen_ctx *sc = CCTOSC(cc);
Pixmap pix;
switch (cc->highlight) {
case CLIENT_HIGHLIGHT_BLUE:
@ -507,9 +520,9 @@ client_bg_pixmap(struct client_ctx *cc)
void
client_update(struct client_ctx *cc)
{
Atom *p, wm_delete, wm_protocols, wm_take_focus;
int i;
long n;
Atom *p, wm_delete, wm_protocols, wm_take_focus;
int i;
long n;
/* XXX cache these. */
wm_delete = XInternAtom(X_Dpy, "WM_DELETE_WINDOW", False);
@ -532,7 +545,7 @@ client_update(struct client_ctx *cc)
void
client_send_delete(struct client_ctx *cc)
{
Atom wm_delete, wm_protocols;
Atom wm_delete, wm_protocols;
/* XXX - cache */
wm_delete = XInternAtom(X_Dpy, "WM_DELETE_WINDOW", False);
@ -547,8 +560,8 @@ client_send_delete(struct client_ctx *cc)
void
client_setname(struct client_ctx *cc)
{
char *newname;
struct winname *wn;
struct winname *wn;
char *newname;
XFetchName(X_Dpy, cc->win, &newname);
if (newname == NULL)
@ -581,141 +594,60 @@ match:
cc->nameqlen--;
}
return;
return;
}
/*
* TODO: seems to have some issues still on the first invocation
* (globally the first)
*/
struct client_ctx *
client_cyclenext(int reverse)
client_cycle(int reverse)
{
struct screen_ctx *sc;
struct client_ctx *cc;
struct client_ctx *(*iter)(struct client_ctx *) =
reverse ? &client_mruprev : &client_mrunext;
struct client_ctx *oldcc, *newcc;
struct screen_ctx *sc;
int again = 1;
/* TODO: maybe this should just be a CIRCLEQ. */
oldcc = client_current();
sc = screen_current();
if (!(cc = _curcc)) {
if (TAILQ_EMPTY(&Clientq))
return(NULL);
cc = TAILQ_FIRST(&Clientq);
}
sc = CCTOSC(cc);
/* if altheld; then reset the iterator to the beginning */
if (!sc->altpersist || sc->cycle_client == NULL)
sc->cycle_client = TAILQ_FIRST(&sc->mruq);
if (sc->cycle_client == NULL)
/* If no windows then you cant cycle */
if (TAILQ_EMPTY(&sc->mruq))
return (NULL);
/*
* INVARIANT: as long as sc->cycle_client != NULL here, we
* won't exit with sc->cycle_client == NULL
*/
if (oldcc == NULL)
oldcc = (reverse ? TAILQ_LAST(&sc->mruq, cycle_entry_q) :
TAILQ_FIRST(&sc->mruq));
if ((sc->cycle_client = client__cycle(cc, iter)) == NULL)
sc->cycle_client = cc;
newcc = oldcc;
while (again) {
again = 0;
/* Do the actual warp. */
client_ptrsave(cc);
client_ptrwarp(sc->cycle_client);
sc->altpersist = 1; /* This is reset when alt is let go... */
newcc = (reverse ? client_mruprev(newcc) :
client_mrunext(newcc));
/* Draw window. */
client_cycleinfo(sc->cycle_client);
/* Only cycle visible and non-ignored windows. */
if (newcc->flags & (CLIENT_HIDDEN|CLIENT_IGNORE))
again = 1;
return (sc->cycle_client);
}
/* Is oldcc the only non-hidden window? */
if (newcc == oldcc) {
if (again)
return (NULL); /* No windows visible. */
/*
* XXX - have to have proper exposure handling here. we will probably
* have to do this by registering with the event loop a function to
* redraw, then match that on windows.
*/
void
client_cycleinfo(struct client_ctx *cc)
{
int w, h, nlines, i, n, oneh, curn = -1, x, y, diff;
struct client_ctx *ccc, *list[3];
struct screen_ctx *sc = CCTOSC(cc);
struct fontdesc *font = DefaultFont;
memset(list, 0, sizeof(list));
nlines = 0;
TAILQ_FOREACH(ccc, &sc->mruq, mru_entry)
nlines++;
nlines = MIN(nlines, 3);
oneh = font_ascent(font) + font_descent(font) + 1;
h = nlines*oneh;
list[1] = cc;
if (nlines > 1)
list[2] = client__cycle(cc, &client_mrunext);
if (nlines > 2)
list[0] = client__cycle(cc, &client_mruprev);
w = 0;
for (i = 0; i < sizeof(list)/sizeof(list[0]); i++) {
if ((ccc = list[i]) == NULL)
continue;
w = MAX(w, font_width(font, ccc->name, strlen(ccc->name)));
break;
}
}
w += 4;
/* reset when alt is released. XXX I hate this hack */
sc->altpersist = 1;
client_ptrsave(oldcc);
client_ptrwarp(newcc);
/* try to fit. */
if ((x = cc->ptr.x) < 0 || (y = cc->ptr.y) < 0) {
x = cc->geom.width / 2;
y = cc->geom.height / 2;
}
if ((diff = cc->geom.width - (x + w)) < 0)
x += diff;
if ((diff = cc->geom.height - (y + h)) < 0)
y += diff;
/* Don't hide the beginning of the window names */
if (x < 0)
x = 0;
XReparentWindow(X_Dpy, sc->infowin, cc->win, 0, 0);
XMoveResizeWindow(X_Dpy, sc->infowin, x, y, w, h);
XMapRaised(X_Dpy, sc->infowin);
XClearWindow(X_Dpy, sc->infowin);
for (i = 0, n = 0; i < sizeof(list)/sizeof(list[0]); i++) {
if ((ccc = list[i]) == NULL)
continue;
font_draw(font, ccc->name, strlen(ccc->name), sc->infowin,
2, n*oneh + font_ascent(font) + 1);
if (i == 1)
curn = n;
n++;
}
assert(curn != -1);
/* Highlight the current entry. */
XFillRectangle(X_Dpy, sc->infowin, sc->hlgc, 0, curn*oneh, w, oneh);
return (newcc);
}
struct client_ctx *
client_mrunext(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
struct client_ctx *ccc;
struct screen_ctx *sc = CCTOSC(cc);
struct client_ctx *ccc;
return ((ccc = TAILQ_NEXT(cc, mru_entry)) != NULL ?
ccc : TAILQ_FIRST(&sc->mruq));
@ -724,136 +656,65 @@ client_mrunext(struct client_ctx *cc)
struct client_ctx *
client_mruprev(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
struct client_ctx *ccc;
struct screen_ctx *sc = CCTOSC(cc);
struct client_ctx *ccc;
return ((ccc = TAILQ_PREV(cc, cycle_entry_q, mru_entry)) != NULL ?
ccc : TAILQ_LAST(&sc->mruq, cycle_entry_q));
}
static struct client_ctx *
client__cycle(struct client_ctx *cc,
struct client_ctx *(*iter)(struct client_ctx *))
{
struct client_ctx *save = cc;
do {
if (!((cc = (*iter)(cc))->flags & CLIENT_HIDDEN))
break;
} while (cc != save);
return cc != save ? cc : NULL;
}
void
client_altrelease()
{
struct client_ctx *cc = _curcc;
struct screen_ctx *sc;
if (cc == NULL)
return;
sc = CCTOSC(cc);
XUnmapWindow(X_Dpy, sc->infowin);
XReparentWindow(X_Dpy, sc->infowin, sc->rootwin, 0, 0);
}
void
client_placecalc(struct client_ctx *cc)
{
struct screen_ctx *sc = CCTOSC(cc);
int yslack, xslack;
int x, y, height, width, ymax, xmax, mousex, mousey;
struct screen_ctx *sc = CCTOSC(cc);
int yslack, xslack, xmouse, ymouse;
y = cc->geom.y;
x = cc->geom.x;
yslack = sc->ymax - cc->geom.height - cc->bwidth;
xslack = sc->xmax - cc->geom.width - cc->bwidth;
height = cc->geom.height;
width = cc->geom.width;
xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
ymax = DisplayHeight(X_Dpy, sc->which) - cc->bwidth;
xmax = DisplayWidth(X_Dpy, sc->which) - cc->bwidth;
xmouse = MAX(xmouse, cc->bwidth) - cc->geom.width / 2;
ymouse = MAX(ymouse, cc->bwidth) - cc->geom.height / 2;
yslack = ymax - cc->geom.height;
xslack = xmax - cc->geom.width;
xu_ptr_getpos(sc->rootwin, &mousex, &mousey);
mousex = MAX(mousex, cc->bwidth) - cc->geom.width/2;
mousey = MAX(mousey, cc->bwidth) - cc->geom.height/2;
mousex = MAX(mousex, (int)cc->bwidth);
mousey = MAX(mousey, (int)cc->bwidth);
xmouse = MAX(xmouse, (int)cc->bwidth);
ymouse = MAX(ymouse, (int)cc->bwidth);
if (cc->size->flags & USPosition) {
if (cc->size->x > 0)
x = cc->size->x;
if (x < cc->bwidth)
x = cc->bwidth;
else if (x > xslack)
x = xslack;
if (cc->size->y > 0)
y = cc->size->y;
if (y < cc->bwidth)
y = cc->bwidth;
else if (y > yslack)
y = yslack;
} else {
if (yslack < 0) {
y = cc->bwidth;
height = ymax;
} else {
if (y == 0 || y > yslack)
y = MIN(mousey, yslack);
height = cc->geom.height;
}
if (xslack < 0) {
x = cc->bwidth;
width = xmax;
} else {
if (x == 0 || x > xslack)
x = MIN(mousex, xslack);
width = cc->geom.width;
}
}
cc->geom.y = y;
cc->geom.x = x;
cc->geom.height = height;
cc->geom.width = width;
}
void
client_vertmaximize(struct client_ctx *cc)
{
if (cc->flags & CLIENT_MAXIMIZED) {
cc->flags &= ~CLIENT_MAXIMIZED;
cc->geom = cc->savegeom;
} else {
struct screen_ctx *sc = CCTOSC(cc);
int display_height = DisplayHeight(X_Dpy, sc->which) -
cc->bwidth*2;
cc->savegeom = cc->geom;
cc->geom.y = cc->bwidth;
if (cc->geom.min_dx == 0)
cc->geom.height = display_height;
if (cc->size->x >= 0)
cc->geom.x = MAX(MIN(cc->size->x, xslack), cc->bwidth);
else
cc->geom.height = display_height -
(display_height % cc->geom.min_dx);
cc->flags |= CLIENT_MAXIMIZED;
cc->geom.x = cc->bwidth;
if (cc->size->y >= 0)
cc->geom.y = MAX(MIN(cc->size->y, yslack), cc->bwidth);
else
cc->geom.y = cc->bwidth;
} else {
if (xslack >= 0) {
cc->geom.x = MAX(MIN(xmouse, xslack),
Conf.gap_left + cc->bwidth);
if (cc->geom.x > (xslack - Conf.gap_right))
cc->geom.x -= Conf.gap_right;
} else {
cc->geom.x = cc->bwidth + Conf.gap_left;
cc->geom.width = sc->xmax - Conf.gap_left;
}
if (yslack >= 0) {
cc->geom.y = MAX(MIN(ymouse, yslack),
Conf.gap_top + cc->bwidth);
if (cc->geom.y > (yslack - Conf.gap_bottom))
cc->geom.y -= Conf.gap_bottom;
} else {
cc->geom.y = cc->bwidth + Conf.gap_top;
cc->geom.height = sc->ymax - Conf.gap_top;
}
}
client_resize(cc);
}
void
client_mtf(struct client_ctx *cc)
{
struct screen_ctx *sc;
struct screen_ctx *sc;
if (cc == NULL)
cc = _curcc;
@ -870,11 +731,11 @@ client_mtf(struct client_ctx *cc)
void
client_gethints(struct client_ctx *cc)
{
XClassHint xch;
int argc;
char **argv;
Atom mha;
struct mwm_hints *mwmh;
XClassHint xch;
int argc;
char **argv;
Atom mha;
struct mwm_hints *mwmh;
if (XGetClassHint(X_Dpy, cc->win, &xch)) {
if (xch.res_name != NULL)
@ -885,7 +746,7 @@ client_gethints(struct client_ctx *cc)
mha = XInternAtom(X_Dpy, "_MOTIF_WM_HINTS", False);
if (xu_getprop(cc, mha, mha, PROP_MWM_HINTS_ELEMENTS,
(u_char **)&mwmh) == MWM_NUMHINTS)
(u_char **)&mwmh) == MWM_NUMHINTS)
if (mwmh->flags & MWM_HINTS_DECORATIONS &&
!(mwmh->decorations & MWM_DECOR_ALL) &&
!(mwmh->decorations & MWM_DECOR_BORDER))
@ -893,9 +754,8 @@ client_gethints(struct client_ctx *cc)
if (XGetCommand(X_Dpy, cc->win, &argv, &argc)) {
#define MAX_ARGLEN 512
#define ARG_SEP_ " "
int len = MAX_ARGLEN;
int i, o;
char *buf;
int i, o, len = MAX_ARGLEN;
char *buf;
buf = xmalloc(len);
buf[0] = '\0';
@ -905,7 +765,7 @@ client_gethints(struct client_ctx *cc)
break;
strlcat(buf, argv[i], len);
o += strlen(buf);
strlcat(buf, ARG_SEP_, len);
strlcat(buf, ARG_SEP_, len);
o += strlen(ARG_SEP_);
}

656
conf.c
View File

@ -23,65 +23,12 @@
#ifndef timespeccmp
#define timespeccmp(tsp, usp, cmp) \
(((tsp)->tv_sec == (usp)->tv_sec) ? \
((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
((tsp)->tv_sec cmp (usp)->tv_sec))
(((tsp)->tv_sec == (usp)->tv_sec) ? \
((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
((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)
/* #define SYSTR_PRE "systrace -C -g /usr/local/bin/notification -d /usr/home/marius/policy/X11 " */
/* 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
@ -89,11 +36,11 @@ conf_cmd_add(struct conf *c, char *image, char *label, int flags)
{
/* "term" and "lock" have special meanings. */
if (strcmp(label, "term") == 0) {
strlcpy(Conf.termpath, image, sizeof(Conf.termpath));
} else if (strcmp(label, "lock") == 0) {
strlcpy(Conf.lockpath, image, sizeof(Conf.lockpath));
} else {
if (strcmp(label, "term") == 0)
strlcpy(c->termpath, image, sizeof(c->termpath));
else if (strcmp(label, "lock") == 0)
strlcpy(c->lockpath, image, sizeof(c->lockpath));
else {
struct cmd *cmd;
XMALLOC(cmd, struct cmd);
cmd->flags = flags;
@ -103,100 +50,43 @@ conf_cmd_add(struct conf *c, char *image, char *label, int flags)
}
}
int
conf_cmd_changed(char *path)
void
conf_font(struct conf *c)
{
#ifdef __OpenBSD__
static struct timespec old_ts;
#else
static time_t old_time;
#endif
struct stat sb;
int changed;
struct screen_ctx *sc;
/* If the directory does not exist we pretend that nothing changed */
if (stat(path, &sb) == -1 || !(sb.st_mode & S_IFDIR))
return (0);
sc = screen_current();
#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);
c->DefaultFont = font_make(sc, Conf.DefaultFontName);
c->FontHeight = font_ascent() + font_descent() + 1;
}
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 (parse_config(c->conf_path, c) == -1) {
warnx("config file %s has errors, not reloading", c->conf_path);
return;
}
conf_cmd_clear(c);
conf_cmd_populate(c, c->menu_path);
conf_font(c);
}
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");
c->flags = 0;
if (home == NULL)
errx(1, "No HOME directory.");
snprintf(c->menu_path, sizeof(c->menu_path), "%s/.calmwm", home);
conf_cmd_init(c);
TAILQ_INIT(&c->keybindingq);
TAILQ_INIT(&c->ignoreq);
TAILQ_INIT(&c->cmdq);
TAILQ_INIT(&c->keybindingq);
TAILQ_INIT(&c->autogroupq);
TAILQ_INIT(&c->mousebindingq);
conf_bindname(c, "CM-Return", "terminal");
conf_bindname(c, "CM-Delete", "lock");
conf_bindname(c, "M-question", "exec");
conf_bindname(c, "CM-q", "exec_wm");
conf_bindname(c, "CM-w", "exec_wm");
conf_bindname(c, "M-period", "ssh");
conf_bindname(c, "M-Return", "hide");
conf_bindname(c, "M-Down", "lower");
@ -207,7 +97,6 @@ conf_setup(struct conf *c)
conf_bindname(c, "MS-Tab", "rcycle");
conf_bindname(c, "CM-n", "label");
conf_bindname(c, "CM-x", "delete");
conf_bindname(c, "CM-Escape", "groupselect");
conf_bindname(c, "CM-0", "nogroup");
conf_bindname(c, "CM-1", "group1");
conf_bindname(c, "CM-2", "group2");
@ -218,10 +107,13 @@ conf_setup(struct conf *c)
conf_bindname(c, "CM-7", "group7");
conf_bindname(c, "CM-8", "group8");
conf_bindname(c, "CM-9", "group9");
conf_bindname(c, "M-Right", "nextgroup");
conf_bindname(c, "M-Left", "prevgroup");
conf_bindname(c, "M-Right", "cyclegroup");
conf_bindname(c, "M-Left", "rcyclegroup");
conf_bindname(c, "CM-g", "grouptoggle");
conf_bindname(c, "CM-f", "maximize");
conf_bindname(c, "CM-equal", "vmaximize");
conf_bindname(c, "CMS-r", "reload");
conf_bindname(c, "CMS-q", "quit");
conf_bindname(c, "M-h", "moveleft");
conf_bindname(c, "M-j", "movedown");
@ -250,88 +142,67 @@ 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;
conf_mousebind(c, "1", "menu_unhide");
conf_mousebind(c, "2", "menu_group");
conf_mousebind(c, "3", "menu_cmd");
conf_mousebind(c, "M-1", "window_move");
conf_mousebind(c, "CM-1", "window_grouptoggle");
conf_mousebind(c, "M-2", "window_resize");
conf_mousebind(c, "M-3", "window_lower");
conf_mousebind(c, "CMS-3", "window_hide");
/* 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 = xstrdup(DEFAULTFONTNAME);
}
int
conf_get_int(struct client_ctx *cc, enum conftype ctype)
void
conf_setup(struct conf *c, const char *conf_file)
{
int val = -1, ignore = 0;
char *wname;
struct winmatch *wm;
struct stat sb;
wname = cc->name;
if (conf_file == NULL) {
char *home = getenv("HOME");
/* Can wname be NULL? */
if (home == NULL)
errx(1, "No HOME directory.");
if (wname != NULL) {
TAILQ_FOREACH(wm, &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) {
ignore = 1;
break;
}
}
snprintf(c->conf_path, sizeof(c->conf_path), "%s/%s", home,
CONFFILE);
} else
ignore = 1;
if (stat(conf_file, &sb) == -1 || !(sb.st_mode & S_IFREG))
errx(1, "%s: %s", conf_file, strerror(errno));
else
strlcpy(c->conf_path, conf_file, sizeof(c->conf_path));
switch (ctype) {
case CONF_BWIDTH:
/*
* XXX this will be a list, specified in the
* configuration file.
*/
val = ignore ? 0 : 3;
break;
case CONF_IGNORE:
val = ignore;
break;
default:
break;
}
conf_init(c);
return (val);
(void)parse_config(c->conf_path, c);
}
void
conf_client(struct client_ctx *cc)
{
cc->bwidth = conf_get_int(cc, CONF_BWIDTH);
cc->flags |= conf_get_int(cc, CONF_IGNORE) ? CLIENT_IGNORE : 0;
struct winmatch *wm;
char *wname = cc->name;
int ignore = 0;
/* Can wname be NULL? */
if (wname != NULL) {
TAILQ_FOREACH(wm, &Conf.ignoreq, entry) {
if (strncasecmp(wm->title, wname, strlen(wm->title))
== 0) {
ignore = 1;
break;
}
}
} else
ignore = 1;
cc->bwidth = ignore ? 0 : 3;
cc->flags |= ignore ? CLIENT_IGNORE : 0;
}
struct {
@ -345,19 +216,10 @@ struct {
{ "search", kbfunc_client_search, 0, 0 },
{ "menusearch", kbfunc_menu_search, 0, 0 },
{ "hide", kbfunc_client_hide, KBFLAG_NEEDCLIENT, 0 },
{ "cycle", kbfunc_client_cycle, KBFLAG_NEEDCLIENT, 0 },
{ "rcycle", kbfunc_client_rcycle, KBFLAG_NEEDCLIENT, 0 },
{ "cycle", kbfunc_client_cycle, 0, (void *)CWM_CYCLE },
{ "rcycle", kbfunc_client_cycle, 0, (void *)CWM_RCYCLE },
{ "label", kbfunc_client_label, KBFLAG_NEEDCLIENT, 0 },
{ "delete", kbfunc_client_delete, KBFLAG_NEEDCLIENT, 0 },
{ "ptrmoveup", kbfunc_ptrmove, 0, (void *)CWM_UP },
{ "ptrmovedown", kbfunc_ptrmove, 0, (void *)CWM_DOWN },
{ "ptrmoveleft", kbfunc_ptrmove, 0, (void *)CWM_LEFT },
{ "ptrmoveright", kbfunc_ptrmove, 0, (void *)CWM_RIGHT },
{ "bigptrmoveup", kbfunc_ptrmove, 0, (void *)(CWM_UP|CWM_BIGMOVE) },
{ "bigptrmovedown", kbfunc_ptrmove, 0, (void *)(CWM_DOWN|CWM_BIGMOVE) },
{ "bigptrmoveleft", kbfunc_ptrmove, 0, (void *)(CWM_LEFT|CWM_BIGMOVE) },
{ "bigptrmoveright", kbfunc_ptrmove, 0, (void *)(CWM_RIGHT|CWM_BIGMOVE) },
{ "groupselect", kbfunc_client_groupselect, 0, 0 },
{ "group1", kbfunc_client_group, 0, (void *)1 },
{ "group2", kbfunc_client_group, 0, (void *)2 },
{ "group3", kbfunc_client_group, 0, (void *)3 },
@ -368,71 +230,104 @@ struct {
{ "group8", kbfunc_client_group, 0, (void *)8 },
{ "group9", kbfunc_client_group, 0, (void *)9 },
{ "nogroup", kbfunc_client_nogroup, 0, 0 },
{ "nextgroup", kbfunc_client_nextgroup, 0, 0 },
{ "prevgroup", kbfunc_client_prevgroup, 0, 0 },
{ "cyclegroup", kbfunc_client_cyclegroup, 0, (void *)CWM_CYCLEGROUP },
{ "rcyclegroup", kbfunc_client_cyclegroup, 0, (void *)CWM_RCYCLEGROUP },
{ "grouptoggle", kbfunc_client_grouptoggle, KBFLAG_NEEDCLIENT, 0},
{ "maximize", kbfunc_client_maximize, KBFLAG_NEEDCLIENT, 0 },
{ "vmaximize", kbfunc_client_vmaximize, KBFLAG_NEEDCLIENT, 0 },
{ "reload", kbfunc_reload, 0, 0 },
{ "quit", kbfunc_quit_wm, 0, 0 },
{ "exec", kbfunc_exec, 0, (void *)CWM_EXEC_PROGRAM },
{ "exec_wm", kbfunc_exec, 0, (void *)CWM_EXEC_WM },
{ "ssh", kbfunc_ssh, 0, 0 },
{ "terminal", kbfunc_term, 0, 0 },
{ "lock", kbfunc_lock, 0, 0 },
{ "moveup", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)CWM_UP },
{ "movedown", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)CWM_DOWN },
{ "moveright", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)CWM_RIGHT },
{ "moveleft", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)CWM_LEFT },
{ "bigmoveup", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)(CWM_UP|CWM_BIGMOVE) },
{ "bigmovedown", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)(CWM_DOWN|CWM_BIGMOVE) },
{ "bigmoveright", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)(CWM_RIGHT|CWM_BIGMOVE) },
{ "bigmoveleft", kbfunc_client_move, KBFLAG_NEEDCLIENT, (void *)(CWM_LEFT|CWM_BIGMOVE) },
{ "resizeup", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)(CWM_UP) },
{ "resizedown", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)CWM_DOWN },
{ "resizeright", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)CWM_RIGHT },
{ "resizeleft", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)CWM_LEFT },
{ "bigresizeup", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)(CWM_UP|CWM_BIGMOVE) },
{ "bigresizedown", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)(CWM_DOWN|CWM_BIGMOVE) },
{ "bigresizeright", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)(CWM_RIGHT|CWM_BIGMOVE) },
{ "bigresizeleft", kbfunc_client_resize, KBFLAG_NEEDCLIENT, (void *)(CWM_LEFT|CWM_BIGMOVE) },
{ "moveup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_UP|CWM_MOVE) },
{ "movedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_DOWN|CWM_MOVE) },
{ "moveright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_RIGHT|CWM_MOVE) },
{ "moveleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_LEFT|CWM_MOVE) },
{ "bigmoveup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_UP|CWM_MOVE|CWM_BIGMOVE) },
{ "bigmovedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_DOWN|CWM_MOVE|CWM_BIGMOVE) },
{ "bigmoveright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_RIGHT|CWM_MOVE|CWM_BIGMOVE) },
{ "bigmoveleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_LEFT|CWM_MOVE|CWM_BIGMOVE) },
{ "resizeup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_UP|CWM_RESIZE) },
{ "resizedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_DOWN|CWM_RESIZE) },
{ "resizeright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_RIGHT|CWM_RESIZE) },
{ "resizeleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_LEFT|CWM_RESIZE) },
{ "bigresizeup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_UP|CWM_RESIZE|CWM_BIGMOVE) },
{ "bigresizedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_DOWN|CWM_RESIZE|CWM_BIGMOVE) },
{ "bigresizeright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_RIGHT|CWM_RESIZE|CWM_BIGMOVE) },
{ "bigresizeleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
(void *)(CWM_LEFT|CWM_RESIZE|CWM_BIGMOVE) },
{ "ptrmoveup", kbfunc_moveresize, 0, (void *)(CWM_UP|CWM_PTRMOVE) },
{ "ptrmovedown", kbfunc_moveresize, 0, (void *)(CWM_DOWN|CWM_PTRMOVE) },
{ "ptrmoveleft", kbfunc_moveresize, 0, (void *)(CWM_LEFT|CWM_PTRMOVE) },
{ "ptrmoveright", kbfunc_moveresize, 0,
(void *)(CWM_RIGHT|CWM_PTRMOVE) },
{ "bigptrmoveup", kbfunc_moveresize, 0,
(void *)(CWM_UP|CWM_PTRMOVE|CWM_BIGMOVE) },
{ "bigptrmovedown", kbfunc_moveresize, 0,
(void *)(CWM_DOWN|CWM_PTRMOVE|CWM_BIGMOVE) },
{ "bigptrmoveleft", kbfunc_moveresize, 0,
(void *)(CWM_LEFT|CWM_PTRMOVE|CWM_BIGMOVE) },
{ "bigptrmoveright", kbfunc_moveresize, 0,
(void *)(CWM_RIGHT|CWM_PTRMOVE|CWM_BIGMOVE) },
{ NULL, NULL, 0, 0},
};
/*
* The following two functions are used when grabbing and ungrabbing keys for
* bindings
*/
/*
* Grab key combination on all screens and add to the global queue
*/
void
conf_parsekeys(struct conf *c, char *filename)
conf_grab(struct conf *c, struct keybinding *kb)
{
DIR *dir;
struct dirent *ent;
char buffer[MAXPATHLEN];
char current_file[MAXPATHLEN];
extern struct screen_ctx_q Screenq;
struct screen_ctx *sc;
dir = opendir(filename);
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
TAILQ_FOREACH(sc, &Screenq, entry)
xu_key_grab(sc->rootwin, kb->modmask, kb->keysym);
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;
}
/*
* Ungrab key combination from all screens and remove from global queue
*/
void
conf_ungrab(struct conf *c, struct keybinding *kb)
{
extern struct screen_ctx_q Screenq;
struct screen_ctx *sc;
memset(buffer, 0, MAXPATHLEN);
if (readlink(current_file, buffer, MAXPATHLEN) < 0)
continue;
conf_bindname(c, ent->d_name, buffer);
}
closedir(dir);
TAILQ_FOREACH(sc, &Screenq, entry)
xu_key_ungrab(sc->rootwin, kb->modmask, kb->keysym);
}
void
conf_bindname(struct conf *c, char *name, char *binding)
{
int iter;
struct keybinding *current_binding;
char *substring;
struct keybinding *current_binding;
char *substring;
int iter;
XCALLOC(current_binding, struct keybinding);
@ -441,23 +336,15 @@ conf_bindname(struct conf *c, char *name, char *binding)
current_binding->modmask |= ControlMask;
if (strchr(name, 'M') != NULL &&
strchr(name, 'M') < strchr(name, '-'))
strchr(name, 'M') < strchr(name, '-'))
current_binding->modmask |= Mod1Mask;
if (strchr(name, '2') != NULL &&
strchr(name, '2') < strchr(name, '-'))
current_binding->modmask |= Mod2Mask;
if (strchr(name, '3') != NULL &&
strchr(name, '3') < strchr(name, '-'))
current_binding->modmask |= Mod3Mask;
if (strchr(name, '4') != NULL &&
strchr(name, '4') < strchr(name, '-'))
strchr(name, '4') < strchr(name, '-'))
current_binding->modmask |= Mod4Mask;
if (strchr(name, 'S') != NULL &&
strchr(name, 'S') < strchr(name, '-'))
strchr(name, 'S') < strchr(name, '-'))
current_binding->modmask |= ShiftMask;
substring = strchr(name, '-') + 1;
@ -476,15 +363,15 @@ conf_bindname(struct conf *c, char *name, char *binding)
}
if (current_binding->keysym == NoSymbol &&
current_binding->keycode == 0 ) {
current_binding->keycode == 0) {
xfree(current_binding);
return;
}
/* We now have the correct binding, remove duplicates. */
conf_unbind(c, current_binding);
conf_unbind(c, current_binding);
if (strcmp("unmap",binding) == 0)
if (strcmp("unmap", binding) == 0)
return;
for (iter = 0; name_to_kbfunc[iter].tag != NULL; iter++) {
@ -494,107 +381,154 @@ conf_bindname(struct conf *c, char *name, char *binding)
current_binding->callback = name_to_kbfunc[iter].handler;
current_binding->flags = name_to_kbfunc[iter].flags;
current_binding->argument = name_to_kbfunc[iter].argument;
conf_grab(c, current_binding);
TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
break;
return;
}
if (name_to_kbfunc[iter].tag != NULL)
return;
current_binding->callback = kbfunc_cmdexec;
current_binding->argument = strdup(binding);
current_binding->argument = xstrdup(binding);
current_binding->flags = 0;
conf_grab(c, current_binding);
TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
return;
}
void conf_unbind(struct conf *c, struct keybinding *unbind)
void
conf_unbind(struct conf *c, struct keybinding *unbind)
{
struct keybinding *key = NULL;
struct keybinding *key = NULL, *keynxt;
for (key = TAILQ_FIRST(&c->keybindingq);
key != TAILQ_END(&c->keybindingq); key = keynxt) {
keynxt = TAILQ_NEXT(key, entry);
TAILQ_FOREACH(key, &c->keybindingq, entry) {
if (key->modmask != unbind->modmask)
continue;
if ((key->keycode != 0 && key->keysym == NoSymbol &&
key->keycode == unbind->keycode) ||
key->keysym == unbind->keysym)
key->keycode == unbind->keycode) ||
key->keysym == unbind->keysym) {
conf_ungrab(c, key);
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);
xfree(key);
}
aw->group = xstrdup(group);
TAILQ_INSERT_TAIL(&c->autogroupq, aw, entry);
}
closedir(dir);
}
struct {
char *tag;
void (*handler)(struct client_ctx *, void *);
int context;
} name_to_mousefunc[] = {
{ "window_move", mousefunc_window_move, MOUSEBIND_CTX_WIN },
{ "window_resize", mousefunc_window_resize, MOUSEBIND_CTX_WIN },
{ "window_grouptoggle", mousefunc_window_grouptoggle,
MOUSEBIND_CTX_WIN },
{ "window_lower", mousefunc_window_lower, MOUSEBIND_CTX_WIN },
{ "window_hide", mousefunc_window_hide, MOUSEBIND_CTX_WIN },
{ "menu_group", mousefunc_menu_group, MOUSEBIND_CTX_ROOT },
{ "menu_unhide", mousefunc_menu_unhide, MOUSEBIND_CTX_ROOT },
{ "menu_cmd", mousefunc_menu_cmd, MOUSEBIND_CTX_ROOT },
{ NULL, NULL, 0 },
};
void
conf_mousebind(struct conf *c, char *name, char *binding)
{
struct mousebinding *current_binding;
char *substring;
const char *errstr;
int iter;
XCALLOC(current_binding, struct mousebinding);
if (strchr(name, 'C') != NULL &&
strchr(name, 'C') < strchr(name, '-'))
current_binding->modmask |= ControlMask;
if (strchr(name, 'M') != NULL &&
strchr(name, 'M') < strchr(name, '-'))
current_binding->modmask |= Mod1Mask;
if (strchr(name, 'S') != NULL &&
strchr(name, 'S') < strchr(name, '-'))
current_binding->modmask |= ShiftMask;
if (strchr(name, '4') != NULL &&
strchr(name, '4') < strchr(name, '-'))
current_binding->modmask |= Mod4Mask;
substring = strchr(name, '-') + 1;
if (strchr(name, '-') == NULL)
substring = name;
current_binding->button = strtonum(substring, 1, 3, &errstr);
if (errstr)
warnx("number of buttons is %s: %s", errstr, substring);
conf_mouseunbind(c, current_binding);
if (strcmp("unmap", binding) == 0)
return;
for (iter = 0; name_to_mousefunc[iter].tag != NULL; iter++) {
if (strcmp(name_to_mousefunc[iter].tag, binding) != 0)
continue;
current_binding->context = name_to_mousefunc[iter].context;
current_binding->callback = name_to_mousefunc[iter].handler;
TAILQ_INSERT_TAIL(&c->mousebindingq, current_binding, entry);
return;
}
}
void
conf_mouseunbind(struct conf *c, struct mousebinding *unbind)
{
struct mousebinding *mb = NULL, *mbnxt;
for (mb = TAILQ_FIRST(&c->mousebindingq);
mb != TAILQ_END(&c->mousebindingq); mb = mbnxt) {
mbnxt = TAILQ_NEXT(mb, entry);
if (mb->modmask != unbind->modmask)
continue;
if (mb->button == unbind->button) {
TAILQ_REMOVE(&c->mousebindingq, mb, entry);
xfree(mb);
}
}
}
/*
* Grab the mouse buttons that we need for bindings for this client
*/
void
conf_grab_mouse(struct client_ctx *cc)
{
struct mousebinding *mb;
int button;
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
if (mb->context != MOUSEBIND_CTX_WIN)
continue;
switch(mb->button) {
case 1:
button = Button1;
break;
case 2:
button = Button2;
break;
case 3:
button = Button3;
break;
default:
warnx("strange button in mousebinding\n");
}
xu_btn_grab(cc->pwin, mb->modmask, button);
}
}

189
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: July 11 2008 $
.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 c Ar file
.Op Fl d Ar display
.Op Fl f Ar fontname
.Sh DESCRIPTION
.Nm
is a window manager for X11 which contains many features that
@ -40,7 +39,7 @@ The following notation is used throughout this page:
.It Ic C
Control
.It Ic M
Meta (Alt on PCs)
Meta
.It Ic S
Shift
.It Ic M1
@ -58,11 +57,11 @@ The current keybindings are described below;
their functionality is described in more detail later.
.Pp
.Bl -tag -width "C-M-EscapeXXX" -offset indent -compact
.It Ic C-M-Enter
.It Ic C-M-Return
Spawn a new terminal.
.It Ic C-M-Delete
Lock the screen.
.It Ic M-Enter
.It Ic M-Return
Hide current window.
.It Ic M-Down
Lower current window.
@ -80,20 +79,20 @@ Cycle through currently visible windows.
Reverse cycle through currently visible windows.
.It Ic C-M-x
Delete current window.
.It Ic C-M-Escape
Enter group edit mode.
.It Ic C-M-[n]
Select group n, where n is 1-9.
.It Ic C-M-0
Select all groups.
.It Ic C-M-g
Toggle group membership of current window.
.It Ic M-Right
Switch to next group.
Cycle through active groups.
.It Ic M-Left
Switch to previous group.
Reverse cycle through active groups.
.It Ic C-M-f
Toggle full-screen size of window.
Toggle full-screen size of current window.
.It Ic C-M-=
Toggle vertical maximization of window.
Toggle vertical maximization of current window.
.It Ic M-?
Spawn
.Dq Exec program
@ -107,46 +106,43 @@ This parses
to provide host auto-completion.
.Xr ssh 1
will be executed via the configured terminal emulator.
.It Ic C-M-q
.It Ic C-M-w
Spawn
.Dq Exec WindowManager
dialog; allows you to switch from
.Nm
to another window manager without restarting the X server.
.It Ic C-M-S-r
Reload configuration.
.It Ic C-M-S-q
Quit
.Nm .
.El
.Pp
The mouse bindings are also important, they are:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It M-M1
Move a window.
Move current window.
.It C-M-M1
Toggle a window's membership in the current group.
A blue highlight indicates the window has been added to the group;
a red highlight indicates it has been removed.
Toggle group membership of current window.
.It M-M2
Resize a window/select a window.
Resize current window
.It M-M3
Lower a window.
Lower current window.
.It CMS-M3
Hide current window.
.El
.Pp
The options for
.Nm
are as follows:
.Bl -tag -width Ds
.It Fl c Ar file
Specify the config file to use. Defaults to
.Pa ~/.cwmrc .
.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 currrently selected
group to any newly created windows.
.El
.Sh POINTER MOVEMENT
The pointer can be moved with the use of the keyboard through bindings.
@ -188,17 +184,19 @@ The window is hidden.
.Pp
The following keybindings may be used to navigate the result list:
.Pp
.Bl -tag -width "[Down] or C-sXXX" -offset indent -compact
.It Ic [Down] No or Ic C-s
.Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact
.It Ic [Down], C-s No or Ic M-j
Select the next window in the list.
.It Ic [Up] No or Ic C-r
.It Ic [Up], C-r No or Ic M-k
Select the previous window in the list.
.It Ic [Backspace] No or Ic C-h
Backspace.
.It Ic C-u
Clear the input.
.It Ic [Enter]
.It Ic [Return]
Focus the selected window.
.It Ic [Esc]
Quit.
Cancel.
.It Ic C-a
Whenever there are no matching windows, list every window.
.El
@ -209,14 +207,13 @@ 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, enter the group edit mode, and select/unselect the
groups with the group selection mouse click.
A blue border will be shown on the currently selected windows.
The group selection keyboard shortcuts can also be used to change
which group to edit.
To edit groups, use the group selection commands to toggle membership
of a group.
A blue border will be shown briefly on windows added to the current group,
and a red border will be shown on those just removed.
.Sh MENUS
Menus are recalled by clicking the mouse on the root window:
.Pp
@ -229,7 +226,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
@ -242,111 +239,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

343
cwmrc.5 Normal file
View File

@ -0,0 +1,343 @@
.\" $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: July 11 2008 $
.Dt CWMRC 5
.Os
.Sh NAME
.Nm cwmrc
.Nd calm window manager configuration file
.Sh DESCRIPTION
This manual page describes the
.Xr cwm 1
configuration file.
The following options are accepted in the configuration file:
.Pp
.Bl -tag -width Ds -compact
.It Ic autogroup Ar group Dq windowclass
.It Ic autogroup Ar group Dq windowclass,windowname
Control automatic window grouping, based on the class and/or name
properties, where
.Ar group
is a number between 0 and 9.
If the group number is 0, then the window will not be grouped; this to
allow for
.Dq sticky
windows in sticky group mode.
.Pp
The class and name of a window may be obtained using
.Xr xprop 1 .
.Pp
.It Ic bind Ar keys Ar command
Cause the creation of a keybinding, or replacement of a default
keybinding.
The modifier keys come first, followed by a
.Sq - .
.Pp
The following modifiers are recognised:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It C
The Control key.
.It M
The Meta key.
.It S
The Shift key.
.It 4
The Mod4 key (normally the windows key).
.El
.Pp
The
.Sq -
should be followed by either a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h ,
or a numerical keycode value enclosed in
.Dq [] .
The
.Ar command
may either be one from the
.Sx BIND COMMAND LIST
(see below) or the command line that is to be executed.
.Pp
A special
.Ar command
keyword
.Dq unmap
can be used to remove the named keybinding.
This can be used to remove a binding which conflicts with an
application.
.Pp
.It Ic command Ar name Ar path
Every
.Ar name
entry is shown in the application menu.
When selected, the defined
.Ar path
is executed with
.Xr execve 2 .
.Pp
The
.Ar name
entries
.Nm term
and
.Nm lock
have a special meaning.
They point to the terminal and screen locking programs specified by
keybindings.
The defaults are
.Xr xterm 1
and
.Xr xlock 1 ,
respectively.
.Pp
.It Ic fontname Ar font
Change the default
.Ar font
for
.Xr Xft 3 .
.Pp
.It Ic gap Ar top bottom left right
Define a
.Dq gap
in pixels at the edge of the screen, so that when a
window is maximized it will not overlap this area.
This
.Dq gap
can be used for applications such as
.Xr xclock 1 ,
where the user may wish to remain visible.
.Pp
.It Ic ignore Ar windowname
Ignore drawing borders around a window with the name
.Ar windowname .
.Pp
.It Ic mousebind Ar buttons Ar command
Cause the creation of a mouse binding, or replacement of a default
mouse binding.
The modifier keys come first, followed by a
.Sq - .
.Pb
The following modifiers are recognised:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It C
The Control key.
.It M
The Meta key.
.It S
The Shift key.
.It 4
The Mod4 key (normally the windows key).
.El
.Pp
The
.Sq -
should be followed by number:
.Pb
.Bl -tag -width Ds -offset indent -compact
.Pp
.It 1
Left mouse button.
.It 2
Right mouse button.
.It 3
Middle mouse button.
.El
.Pp
The
.Ar command
may be taken from the
.Sx MOUSEBIND COMMAND LIST
(see below).
.Pp
.It Ic sticky Ic yes Ns \&| Ns Ic no
Toggle sticky group mode.
The default behavior for new windows is to not assign any group.
By enabling sticky group mode,
.Xr cwm 1
will assign new windows to the currently selected group.
.El
.Sh EXAMPLE CONFIGURATION
.Bd -literal
# Set default Xft(3) font
fontname "sans-serif:pixelsize=14:bold"
# Turn on sticky-group mode
sticky yes
# Any entry here is shown in the application menu
command firefox firefox
command xmms xmms
command top "xterm -e top"
# Autogroup definitions
autogroup 3 "aterm,XTerm"
autogroup 3 "xterm,XTerm"
# Ignore programs by that name by not drawing borders around them.
ignore XMMS
ignore xwi
ignore xapm
ignore xclock
# Keybindings
bind CM-r label
bind CS-Return "xterm -e top"
bind 4-o unmap
# Mousebindings
mousebind M-2 window_lower
mousebind M-3 window_resize
.Ed
.Sh BIND COMMAND LIST
.Bl -tag -width 18n -compact
.It reload
Reload configuration.
.It quit
Quit
.Xr cwm 1 .
.It terminal
Spawn a new terminal.
.It lock
Lock the screen.
.It search
Launch window search menu.
.It menusearch
Launch application search menu.
.It exec
Launch
.Dq exec program
menu.
.It exec_wm
Launch
.Dq exec WindowManager
menu.
.It ssh
Launch
.Dq ssh
menu.
.It group[n]
Select group n, where n is 1-9.
.It nogroup
Select all groups.
.It grouptoggle
Toggle group membership of current window.
.It cyclegroup
Forward cycle through groups.
.It rcyclegroup
Reverse cycle through groups.
.It cycle
Forward cycle through windows.
.It rcycle
Reverse cycle through windows.
.It delete
Delete current window.
.It hide
Hide current window.
.It lower
Lower current window.
.It raise
Raise current window.
.It label
Label current window.
.It maximize
Maximize current window full-screen.
.It vmaximize
Maximize current window vertically.
.It moveup
Move window 1 pixel up.
.It movedown
Move window 1 pixel down.
.It moveright
Move window 1 pixel right.
.It moveleft
Move window 1 pixel left.
.It bigmoveup
Move window 10 pixels up.
.It bigmovedown
Move window 10 pixels down.
.It bigmoveright
Move window 10 pixels right.
.It bigmoveleft
Move window 10 pixels left.
.It resizeup
Resize window 1 pixel up.
.It resizedown
Resize window 1 pixel down.
.It resizeright
Resize window 1 pixel right.
.It resizeleft
Resize window 1 pixel left.
.It bigresizeup
Resize window 10 pixels up.
.It bigresizedown
Resize window 10 pixels down.
.It bigresizeright
Resize window 10 pixels right.
.It bigresizeleft
Resize window 10 pixels left.
.It ptrmoveup
Move pointer 1 pixel up.
.It ptrmovedown
Move pointer 1 pixel down.
.It ptrmoveright
Move pointer 1 pixel right.
.It ptrmoveleft
Move pointer 1 pixel left.
.It bigptrmoveup
Move pointer 10 pixels up.
.It bigptrmovedown
Move pointer 10 pixels down.
.It bigptrmoveright
Move pointer 10 pixels right.
.It bigptrmoveleft
Move pointer 10 pixels left.
.El
.Sh MOUSEBIND COMMAND LIST
.Bl -tag -width 18n -compact
.It window_move
Move current window.
.It window_resize
Resize current window.
.It window_lower
Lower current window.
.It window_hide
Hide current window.
.It window_grouptoggle
Toggle group membership of current window.
.It menu_group
Launch group list.
.It menu_unhide
Launch group list.
.It menu_cmd
Launch command list.
.El
.Sh FILES
.Bl -tag -width "~/.cwmrcXXX" -compact
.It Pa ~/.cwmrc
default
.Xr cwm 1
configuration file
.El
.Sh SEE ALSO
.Xr cwm 1
.Sh HISTORY
The
.Nm
file format first appeared in
.Ox 4.4 .

113
font.c
View File

@ -16,58 +16,21 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "hash.h"
#include "headers.h"
#include "calmwm.h"
static XftFont *_make_font(struct screen_ctx *sc, struct fontdesc *fdp);
HASH_GENERATE(fonthash, fontdesc, node, fontdesc_cmp);
int
fontdesc_cmp(struct fontdesc *a, struct fontdesc *b)
{
return strcmp(a->name, b->name);
}
/*
* Fowler/Noll/Vo hash
* http://www.isthe.com/chongo/tech/comp/fnv/
*/
#define FNV_P_32 ((unsigned int)0x01000193) /* 16777619 */
#define FNV_1_32 ((unsigned int)0x811c9dc5) /* 2166136261 */
unsigned int
fontdesc_hash(struct fontdesc *fdp)
{
const unsigned char *p, *end, *start;
unsigned int hash = FNV_1_32;
start = fdp->name;
end = (const unsigned char *)fdp->name + strlen(fdp->name);
for (p = start; p < end; p++) {
hash *= FNV_P_32;
hash ^= (unsigned int)*p;
}
return (hash);
}
void
font_init(struct screen_ctx *sc)
{
XColor xcolor, tmp;
XColor xcolor, tmp;
HASH_INIT(&sc->fonthash, fontdesc_hash);
sc->xftdraw = XftDrawCreate(X_Dpy, sc->rootwin,
DefaultVisual(X_Dpy, sc->which), DefaultColormap(X_Dpy, sc->which));
if (sc->xftdraw == NULL)
errx(1, "XftDrawCreate");
if (!XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, sc->which),
"black", &xcolor, &tmp))
"black", &xcolor, &tmp))
errx(1, "XAllocNamedColor");
sc->xftcolor.color.red = xcolor.red;
@ -77,76 +40,35 @@ font_init(struct screen_ctx *sc)
sc->xftcolor.pixel = xcolor.pixel;
}
struct fontdesc *
font_getx(struct screen_ctx *sc, const char *name)
{
struct fontdesc *fdp;
if ((fdp = font_get(sc, name)) == NULL)
errx(1, "font_get()");
return (fdp);
}
struct fontdesc *
font_get(struct screen_ctx *sc, const char *name)
{
struct fontdesc fd, *fdp;
XftFont *fn;
fd.name = name;
if ((fdp = HASH_FIND(fonthash, &sc->fonthash, &fd)) == NULL
&& (fn = _make_font(sc, &fd)) != NULL) {
fdp = xmalloc(sizeof(*fdp));
fdp->name = xstrdup(fd.name);
fdp->fn = fn;
fdp->sc = sc;
HASH_INSERT(fonthash, &sc->fonthash, fdp);
}
return (fdp);
}
int
font_width(struct fontdesc *fdp, const char *text, int len)
font_width(const char *text, int len)
{
XGlyphInfo extents;
XftTextExtents8(X_Dpy, fdp->fn, (const XftChar8*)text, len, &extents);
XGlyphInfo extents;
return (extents.xOff);
XftTextExtents8(X_Dpy, Conf.DefaultFont, (const XftChar8*)text,
len, &extents);
return (extents.xOff);
}
void
font_draw(struct fontdesc *fdp, const char *text, int len,
font_draw(struct screen_ctx *sc, const char *text, int len,
Drawable d, int x, int y)
{
XftDrawChange(fdp->sc->xftdraw, d);
XftDrawChange(sc->xftdraw, d);
/* Really needs to be UTF8'd. */
XftDrawString8(fdp->sc->xftdraw, &fdp->sc->xftcolor, fdp->fn, x, y,
XftDrawString8(sc->xftdraw, &sc->xftcolor, Conf.DefaultFont, x, y,
(const FcChar8*)text, len);
}
int
font_ascent(struct fontdesc *fdp)
XftFont *
font_make(struct screen_ctx *sc, const char *name)
{
return fdp->fn->ascent;
}
XftFont *fn = NULL;
FcPattern *pat, *patx;
XftResult res;
int
font_descent(struct fontdesc *fdp)
{
return fdp->fn->descent;
}
static XftFont *
_make_font(struct screen_ctx *sc, struct fontdesc *fdp)
{
XftFont *fn = NULL;
FcPattern *pat, *patx;
XftResult res;
if ((pat = FcNameParse(fdp->name)) == NULL)
if ((pat = FcNameParse(name)) == NULL)
return (NULL);
if ((patx = XftFontMatch(X_Dpy, sc->which, pat, &res)) != NULL)
@ -156,4 +78,3 @@ _make_font(struct screen_ctx *sc, struct fontdesc *fdp)
return (fn);
}

289
grab.c
View File

@ -22,7 +22,6 @@
#include "calmwm.h"
static int _sweepcalc(struct client_ctx *, int, int, int, int);
static int menu_calc_entry(int, int, int, int, int);
#define ADJUST_HEIGHT(cc, dy) ((cc->geom.height - cc->geom.min_dy)/ dy)
#define ADJUST_WIDTH(cc, dx) ((cc->geom.width - cc->geom.min_dx)/ dx)
@ -30,36 +29,34 @@ static int menu_calc_entry(int, int, int, int, int);
void
grab_sweep_draw(struct client_ctx *cc, int dx, int dy)
{
struct screen_ctx *sc = CCTOSC(cc);
int x0 = cc->geom.x, y0 = cc->geom.y;
char asize[10]; /* fits "nnnnxnnnn\0" */
int wide, height, wide_size, wide_name;
struct fontdesc *font = DefaultFont;
struct screen_ctx *sc = CCTOSC(cc);
char asize[10]; /* fits "nnnnxnnnn\0" */
int wide, height, wide_size, wide_name;
int x = cc->geom.x, y = cc->geom.y;
snprintf(asize, sizeof(asize), "%dx%d",
ADJUST_WIDTH(cc, dx), ADJUST_HEIGHT(cc, dy));
wide_size = font_width(font, asize, strlen(asize)) + 4;
wide_name = font_width(font, cc->name, strlen(cc->name)) + 4;
wide_size = font_width(asize, strlen(asize)) + 4;
wide_name = font_width(cc->name, strlen(cc->name)) + 4;
wide = MAX(wide_size, wide_name);
height = font_ascent(font) + font_descent(font) + 1;
height = font_ascent() + font_descent() + 1;
XMoveResizeWindow(X_Dpy, sc->menuwin, x0, y0, wide, height * 2);
XMoveResizeWindow(X_Dpy, sc->menuwin, x, y, wide, height * 2);
XMapWindow(X_Dpy, sc->menuwin);
XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0);
XClearWindow(X_Dpy, sc->menuwin);
font_draw(font, cc->name, strlen(cc->name), sc->menuwin,
2, font_ascent(font) + 1);
font_draw(font, asize, strlen(asize), sc->menuwin,
wide/2 - wide_size/2, height + font_ascent(font) + 1);
font_draw(sc, cc->name, strlen(cc->name), sc->menuwin,
2, font_ascent() + 1);
font_draw(sc, asize, strlen(asize), sc->menuwin,
wide / 2 - wide_size / 2, height + font_ascent() + 1);
}
void
grab_sweep(struct client_ctx *cc)
{
XEvent ev;
struct screen_ctx *sc = CCTOSC(cc);
int x0 = cc->geom.x, y0 = cc->geom.y;
int dx, dy;
XEvent ev;
struct screen_ctx *sc = CCTOSC(cc);
int x = cc->geom.x, y = cc->geom.y, dx, dy;
dx = MAX(1, cc->size->width_inc);
dy = MAX(1, cc->size->height_inc);
@ -82,8 +79,8 @@ grab_sweep(struct client_ctx *cc)
client_draw_border(cc);
break;
case MotionNotify:
if (_sweepcalc(cc, x0, y0, ev.xmotion.x, ev.xmotion.y))
/* Recompute window output */
if (_sweepcalc(cc, x, y, ev.xmotion.x, ev.xmotion.y))
/* Recompute window output */
grab_sweep_draw(cc, dx, dy);
XMoveResizeWindow(X_Dpy, cc->pwin,
@ -95,12 +92,20 @@ grab_sweep(struct client_ctx *cc)
cc->bwidth, cc->bwidth,
cc->geom.width, cc->geom.height);
client_do_shape(cc);
break;
case ButtonRelease:
XUnmapWindow(X_Dpy, sc->menuwin);
XReparentWindow(X_Dpy, sc->menuwin, sc->rootwin, 0, 0);
xu_ptr_ungrab();
/* Make sure the pointer stays within the window. */
if (cc->ptr.x > cc->geom.width)
cc->ptr.x = cc->geom.width - cc->bwidth;
if (cc->ptr.y > cc->geom.height)
cc->ptr.y = cc->geom.height - cc->bwidth;
client_ptrwarp(cc);
return;
}
}
@ -110,9 +115,9 @@ grab_sweep(struct client_ctx *cc)
void
grab_drag(struct client_ctx *cc)
{
int x0 = cc->geom.x, y0 = cc->geom.y, xm, ym;
struct screen_ctx *sc = CCTOSC(cc);
XEvent ev;
XEvent ev;
struct screen_ctx *sc = CCTOSC(cc);
int x = cc->geom.x, y = cc->geom.y, xm, ym;
client_raise(cc);
@ -129,8 +134,8 @@ grab_drag(struct client_ctx *cc)
client_draw_border(cc);
break;
case MotionNotify:
cc->geom.x = x0 + (ev.xmotion.x - xm);
cc->geom.y = y0 + (ev.xmotion.y - ym);
cc->geom.x = x + (ev.xmotion.x - xm);
cc->geom.y = y + (ev.xmotion.y - ym);
XMoveWindow(X_Dpy, cc->pwin,
cc->geom.x - cc->bwidth, cc->geom.y - cc->bwidth);
@ -144,225 +149,16 @@ grab_drag(struct client_ctx *cc)
/* NOTREACHED */
}
#define MenuMask (ButtonMask|ButtonMotionMask|ExposureMask)
#define MenuGrabMask (ButtonMask|ButtonMotionMask|StructureNotifyMask)
#define AllButtonMask (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
void *
grab_menu(XButtonEvent *e, struct menu_q *menuq)
{
struct screen_ctx *sc;
struct menu *mi;
XEvent event;
struct fontdesc *font = DefaultFont;
int x, y, width, height, tothigh, i, no, entry, prev;
int fx, fy;
no = i = width = 0;
if ((sc = screen_fromroot(e->root)) == NULL || e->window == sc->menuwin)
return (NULL);
TAILQ_FOREACH(mi, menuq, entry) {
i = font_width(font, mi->text, strlen(mi->text)) + 4;
if (i > width)
width = i;
no++;
}
if (!sc->maxinitialised) {
sc->xmax = DisplayWidth(X_Dpy, sc->which);
sc->ymax = DisplayHeight(X_Dpy, sc->which);
}
height = font_ascent(font) + font_descent(font) + 1;
tothigh = height * no;
x = e->x - width/2;
y = e->y - height/2;
/* does it fit on the screen? */
if (x < 0)
x = 0;
else if (x+width >= sc->xmax)
x = sc->xmax - width;
if (y < 0)
y = 0;
else if (y+tothigh >= sc->ymax)
y = sc->ymax - tothigh;
xu_ptr_setpos(e->root, x + width/2, y + height/2);
XMoveResizeWindow(X_Dpy, sc->menuwin, x, y, width, tothigh);
XSelectInput(X_Dpy, sc->menuwin, MenuMask);
XMapRaised(X_Dpy, sc->menuwin);
if (xu_ptr_grab(sc->menuwin, MenuGrabMask, Cursor_select) < 0) {
XUnmapWindow(X_Dpy, sc->menuwin);
return (NULL);
}
entry = prev = -1;
for (;;) {
XMaskEvent(X_Dpy, MenuMask, &event);
switch (event.type) {
case Expose:
XClearWindow(X_Dpy, sc->menuwin);
i = 0;
TAILQ_FOREACH(mi, menuq, entry) {
fx = (width - font_width(font, mi->text,
strlen(mi->text)))/2;
fy = height*i + font_ascent(font) + 1;
font_draw(font, mi->text, strlen(mi->text),
sc->menuwin, fx, fy);
i++;
}
/* FALLTHROUGH */
case MotionNotify:
prev = entry;
entry = menu_calc_entry(event.xbutton.x,
event.xbutton.y, width, height, no);
if (prev != -1)
XFillRectangle(X_Dpy, sc->menuwin, sc->hlgc,
0, height*prev, width, height);
if (entry != -1) {
xu_ptr_regrab(MenuGrabMask, Cursor_select);
XFillRectangle(X_Dpy, sc->menuwin, sc->hlgc,
0, height*entry, width, height);
} else
xu_ptr_regrab(MenuGrabMask, Cursor_default);
break;
case ButtonRelease:
if (event.xbutton.button != e->button)
break;
entry = menu_calc_entry(event.xbutton.x,
event.xbutton.y, width, height, no);
xu_ptr_ungrab();
XUnmapWindow(X_Dpy, sc->menuwin);
i = 0;
TAILQ_FOREACH(mi, menuq, entry)
if (entry == i++)
break;
return (mi);
default:
break;
}
}
}
void
grab_menuinit(struct screen_ctx *sc)
{
sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0,
1, 1, 1, sc->blackpixl, sc->whitepixl);
}
#define LABEL_MAXLEN 256
#define LabelMask (KeyPressMask|ExposureMask)
void
grab_label(struct client_ctx *cc)
{
struct screen_ctx *sc = screen_current();
int x, y, dx, dy, fontheight, focusrevert;
XEvent e;
char labelstr[LABEL_MAXLEN];
char dispstr[LABEL_MAXLEN + sizeof("label>") - 1];
Window focuswin;
char chr;
enum ctltype ctl;
size_t len;
struct fontdesc *font = DefaultFont;
if (cc->label != NULL)
strlcpy(labelstr, cc->label, sizeof(labelstr));
else
labelstr[0] = '\0';
xu_ptr_getpos(sc->rootwin, &x, &y);
dy = fontheight = font_ascent(font) + font_descent(font) + 1;
dx = font_width(font, "label>", 6);
XMoveResizeWindow(X_Dpy, sc->searchwin, x, y, dx, dy);
XSelectInput(X_Dpy, sc->searchwin, LabelMask);
XMapRaised(X_Dpy, sc->searchwin);
XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
XSetInputFocus(X_Dpy, sc->searchwin,
RevertToPointerRoot, CurrentTime);
for (;;) {
XMaskEvent(X_Dpy, LabelMask, &e);
switch (e.type) {
case KeyPress:
if (input_keycodetrans(e.xkey.keycode, e.xkey.state,
&ctl, &chr, 0) < 0)
continue;
switch (ctl) {
case CTL_ERASEONE:
if ((len = strlen(labelstr)) > 0)
labelstr[len - 1] = '\0';
break;
case CTL_RETURN:
/* Done */
if (strlen(labelstr) == 0)
goto out;
if (cc->label != NULL)
xfree(cc->label);
cc->label = xstrdup(labelstr);
/* FALLTHROUGH */
case CTL_ABORT:
goto out;
default:
break;
}
if (chr != '\0') {
char str[2];
str[0] = chr;
str[1] = '\0';
strlcat(labelstr, str, sizeof(labelstr));
}
case Expose:
snprintf(dispstr, sizeof(dispstr), "label>%s", labelstr);
dx = font_width(font, dispstr, strlen(dispstr));
dy = fontheight;
XClearWindow(X_Dpy, sc->searchwin);
XResizeWindow(X_Dpy, sc->searchwin, dx, dy);
font_draw(font, dispstr, strlen(dispstr),
sc->searchwin, 0, font_ascent(font) + 1);
break;
}
}
out:
XSetInputFocus(X_Dpy, focuswin,
focusrevert, CurrentTime);
XUnmapWindow(X_Dpy, sc->searchwin);
}
static int
_sweepcalc(struct client_ctx *cc, int x0, int y0, int motionx, int motiony)
_sweepcalc(struct client_ctx *cc, int x, int y, int motionx, int motiony)
{
int width, height;
int width, height;
width = cc->geom.width;
height = cc->geom.height;
cc->geom.width = abs(x0 - motionx);
cc->geom.height = abs(y0 - motiony);
cc->geom.width = abs(x - motionx);
cc->geom.height = abs(y - motiony);
if (cc->size->flags & PResizeInc) {
cc->geom.width -=
@ -381,21 +177,8 @@ _sweepcalc(struct client_ctx *cc, int x0, int y0, int motionx, int motiony)
cc->geom.height = MIN(cc->geom.height, cc->size->max_height);
}
cc->geom.x = x0 <= motionx ? x0 : x0 - cc->geom.width;
cc->geom.y = y0 <= motiony ? y0 : y0 - cc->geom.height;
cc->geom.x = x <= motionx ? x : x - cc->geom.width;
cc->geom.y = y <= motiony ? y : y - cc->geom.height;
return (width != cc->geom.width || height != cc->geom.height);
}
static int
menu_calc_entry(int x, int y, int width, int height, int noentries)
{
int entry = y/height;
/* in bounds? */
if (x < 0 || x > width || y < 0 || y > height*noentries ||
entry < 0 || entry >= noentries)
entry = -1;
return entry;
}

386
group.c
View File

@ -24,31 +24,16 @@
#define CALMWM_NGROUPS 9
int Groupmode = 0;
int Groupnamemode = 0;
struct group_ctx *Group_active = NULL;
struct group_ctx *Group_current = NULL;
struct group_ctx Groups[CALMWM_NGROUPS];
char Group_name[256];
int Groupfocusset = 0;
Window Groupfocuswin;
int Groupfocusrevert;
int Grouphideall = 0;
struct group_ctx_q Groupq;
#define GroupMask (KeyPressMask|ExposureMask)
static char *shortcut_to_name[] = {
"XXX", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine",
};
struct group_ctx *Group_active = NULL;
struct group_ctx Groups[CALMWM_NGROUPS];
int Grouphideall = 0;
struct group_ctx_q Groupq;
static void
_group_add(struct group_ctx *gc, struct client_ctx *cc)
{
if (cc == NULL || gc == NULL)
errx(1, "_group_add: a ctx is NULL");
errx(1, "_group_add: a ctx is NULL");
if (cc->group == gc)
return;
@ -58,52 +43,22 @@ _group_add(struct group_ctx *gc, struct client_ctx *cc)
TAILQ_INSERT_TAIL(&gc->clients, cc, group_entry);
cc->group = gc;
cc->groupcommit = 0;
}
static void
_group_remove(struct client_ctx *cc)
{
if (cc == NULL || cc->group == NULL)
errx(1, "_group_remove: a ctx is NULL");
errx(1, "_group_remove: a ctx is NULL");
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
cc->group = NULL;
cc->groupcommit = 0;
cc->highlight = 0;
client_draw_border(cc);
}
static void
_group_commit(struct group_ctx *gc)
{
struct client_ctx *cc;
if (gc == NULL)
errx(1, "_group_commit: ctx is null");
TAILQ_FOREACH(cc, &gc->clients, group_entry)
cc->groupcommit = 1;
}
static void
_group_purge(struct group_ctx *gc)
{
struct client_ctx *cc;
if (gc == NULL)
errx(1, "_group_purge: ctx is null");
TAILQ_FOREACH(cc, &gc->clients, group_entry)
if (cc->groupcommit == 0)
_group_remove(cc);
}
static void
_group_hide(struct group_ctx *gc)
{
struct client_ctx *cc;
struct client_ctx *cc;
screen_updatestackingorder();
@ -121,12 +76,12 @@ _group_hide(struct group_ctx *gc)
static void
_group_show(struct group_ctx *gc)
{
struct client_ctx *cc;
Window *winlist;
u_int i;
int lastempty = -1;
struct client_ctx *cc;
Window *winlist;
u_int i;
int lastempty = -1;
winlist = (Window *) xcalloc(sizeof(*winlist) * (gc->highstack + 1));
winlist = (Window *) xcalloc(sizeof(*winlist), (gc->highstack + 1));
/*
* Invert the stacking order as XRestackWindows() expects them
@ -155,31 +110,10 @@ _group_show(struct group_ctx *gc)
Group_active = gc;
}
static void
_group_destroy(struct group_ctx *gc)
{
struct client_ctx *cc;
if (gc->name != NULL) {
xfree(gc->name);
gc->name = NULL;
}
while ((cc = TAILQ_FIRST(&gc->clients)) != NULL) {
TAILQ_REMOVE(&gc->clients, cc, group_entry);
cc->group = NULL;
cc->groupcommit = 0;
cc->highlight = 0;
client_draw_border(cc);
}
}
void
group_init(void)
{
int i;
int i;
TAILQ_INIT(&Groupq);
@ -190,114 +124,18 @@ group_init(void)
TAILQ_INSERT_TAIL(&Groupq, &Groups[i], entry);
}
Group_current = Group_active = &Groups[0];
Group_active = &Groups[0];
}
/*
* manipulate the 'current group'
/*
* Colouring for groups upon add/remove.
*/
/* change the current group */
void
group_select(int idx)
{
struct group_ctx *gc = Group_current;
struct client_ctx *cc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
return;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
cc->highlight = 0;
client_draw_border(cc);
}
_group_commit(gc);
Group_current = &Groups[idx];
group_display_draw(screen_current());
return;
}
/* enter group mode */
void
group_enter(void)
{
if (Groupmode != 0)
errx(1, "group_enter called twice");
if (Group_current == NULL)
Group_current = &Groups[0];
/* setup input buffer */
Group_name[0] = '\0';
Groupmode = 1;
group_display_init(screen_current());
group_display_draw(screen_current());
}
/* exit group mode */
void
group_exit(int commit)
{
struct group_ctx *gc = Group_current;
struct client_ctx *cc;
if (Groupmode != 1)
errx(1, "group_exit called twice");
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
cc->highlight = 0;
client_draw_border(cc);
}
if (commit) {
_group_commit(gc);
} else {
/* abort */
_group_purge(gc);
if (!TAILQ_EMPTY(&gc->clients))
_group_destroy(gc);
}
XUnmapWindow(X_Dpy, screen_current()->groupwin);
if (Groupnamemode) {
XSetInputFocus(X_Dpy, Groupfocuswin, Groupfocusrevert,
CurrentTime);
Groupfocusset = 0;
}
Groupmode = Groupnamemode = 0;
}
void
group_click(struct client_ctx *cc)
{
struct group_ctx *gc = Group_current;
if (gc == cc->group)
_group_remove(cc);
else
_group_add(gc, cc);
group_display_draw(screen_current());
}
/* Used to add a newly mapped window to the active group */
void
group_sticky(struct client_ctx *cc)
{
_group_add(Group_active, cc);
}
void
group_sticky_toggle_enter(struct client_ctx *cc)
{
struct group_ctx *gc = Group_active;
struct group_ctx *gc;
gc = Group_active;
if (gc == cc->group) {
_group_remove(cc);
@ -318,95 +156,16 @@ group_sticky_toggle_exit(struct client_ctx *cc)
}
/*
* selection list display
* selection list display
*/
void
group_display_init(struct screen_ctx *sc)
{
sc->groupwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0,
1, 1, 1, sc->blackpixl, sc->whitepixl);
}
void
group_display_draw(struct screen_ctx *sc)
{
struct group_ctx *gc = Group_current;
int x, y, dx, dy, fontheight;
struct client_ctx *cc;
char titlebuf[1024];
struct fontdesc *font = DefaultFont;
snprintf(titlebuf, sizeof(titlebuf), "Editing group %d", gc->shortcut);
x = y = 0;
fontheight = font_ascent(font) + font_descent(font) + 1;
dx = font_width(font, titlebuf, strlen(titlebuf));
dy = fontheight;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
cc->highlight = CLIENT_HIGHLIGHT_BLUE;
client_draw_border(cc);
}
XMoveResizeWindow(X_Dpy, sc->groupwin, x, y, dx, dy);
/* XXX */
XSelectInput(X_Dpy, sc->groupwin, GroupMask);
XMapRaised(X_Dpy, sc->groupwin);
XClearWindow(X_Dpy, sc->groupwin);
font_draw(font, titlebuf, strlen(titlebuf), sc->groupwin,
0, font_ascent(font) + 1);
}
void
group_display_keypress(KeyCode k)
{
struct group_ctx * gc = Group_current;
char chr;
enum ctltype ctl;
int len;
if (!Groupnamemode)
return;
if (input_keycodetrans(k, 0, &ctl, &chr, 1) < 0)
goto out;
switch (ctl) {
case CTL_ERASEONE:
if ((len = strlen(Group_name)) > 0)
Group_name[len - 1] = '\0';
break;
case CTL_RETURN:
if (gc->name != NULL)
xfree(gc->name);
gc->name = xstrdup(Group_name);
group_exit(1);
return;
default:
break;
}
if (chr != '\0')
snprintf(Group_name, sizeof(Group_name), "%s%c",
Group_name, chr);
out:
group_display_draw(screen_current());
}
/* if group_hidetoggle would produce no effect, toggle the group's hidden state
*/
void
_group_fix_hidden_state(struct group_ctx *gc)
{
struct client_ctx *cc;
int same = 0;
struct client_ctx *cc;
int same = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
if (gc->hidden == ((cc->flags & CLIENT_HIDDEN) ? 1 : 0))
@ -420,10 +179,7 @@ _group_fix_hidden_state(struct group_ctx *gc)
void
group_hidetoggle(int idx)
{
struct group_ctx *gc;
#ifdef notyet
char buf[128];
#endif
struct group_ctx *gc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_hidetoggle: index out of range (%d)", idx);
@ -439,33 +195,25 @@ group_hidetoggle(int idx)
if (TAILQ_EMPTY(&gc->clients))
Group_active = gc;
}
#ifdef notyet
snprintf(buf, sizeof(buf), "Group %d", idx + 1);
screen_infomsg(buf);
#endif
}
#define GROUP_NEXT(gc, fwd) (fwd) ? \
TAILQ_NEXT(gc, entry) : TAILQ_PREV(gc, group_ctx_q, entry)
/*
* Jump to the next/previous active group. If none exist, then just
* stay put.
* Cycle through active groups. If none exist, then just stay put.
*/
void
group_slide(int fwd)
group_cycle(int reverse)
{
struct group_ctx *gc, *showgroup = NULL;
struct group_ctx *gc, *showgroup = NULL;
assert(Group_active != NULL);
gc = Group_active;
for (;;) {
gc = GROUP_NEXT(gc, fwd);
gc = reverse ? TAILQ_PREV(gc, group_ctx_q, entry) :
TAILQ_NEXT(gc, entry);
if (gc == NULL)
gc = fwd ? TAILQ_FIRST(&Groupq) :
TAILQ_LAST(&Groupq, group_ctx_q);
gc = reverse ? TAILQ_LAST(&Groupq, group_ctx_q) :
TAILQ_FIRST(&Groupq);
if (gc == Group_active)
break;
@ -491,20 +239,19 @@ void
group_client_delete(struct client_ctx *cc)
{
if (cc->group == NULL)
return;
return;
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
cc->group = NULL; /* he he */
cc->groupcommit = 0;
}
void
group_menu(XButtonEvent *e)
{
struct menu_q menuq;
struct menu *mi;
int i;
struct group_ctx *gc;
struct group_ctx *gc;
struct menu *mi;
struct menu_q menuq;
int i;
TAILQ_INIT(&menuq);
@ -514,16 +261,13 @@ group_menu(XButtonEvent *e)
if (TAILQ_EMPTY(&gc->clients))
continue;
if (gc->name == NULL)
gc->name = xstrdup(shortcut_to_name[gc->shortcut]);
XCALLOC(mi, struct menu);
if (gc->hidden)
snprintf(mi->text, sizeof(mi->text), "%d: [%s]",
gc->shortcut, gc->name);
gc->shortcut, shortcut_to_name[gc->shortcut]);
else
snprintf(mi->text, sizeof(mi->text), "%d: %s",
gc->shortcut, gc->name);
gc->shortcut, shortcut_to_name[gc->shortcut]);
mi->ctx = gc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
@ -531,7 +275,7 @@ group_menu(XButtonEvent *e)
if (TAILQ_EMPTY(&menuq))
return;
mi = (struct menu *)grab_menu(e, &menuq);
mi = menu_filter(&menuq, NULL, NULL, 0, NULL, NULL);
if (mi == NULL || mi->ctx == NULL)
goto cleanup;
@ -543,31 +287,23 @@ group_menu(XButtonEvent *e)
else
_group_hide(gc);
cleanup:
cleanup:
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
void
group_namemode(void)
{
Groupnamemode = 1;
group_display_draw(screen_current());
}
void
group_alltoggle(void)
{
int i;
int i;
for (i=0; i < CALMWM_NGROUPS; i++) {
for (i = 0; i < CALMWM_NGROUPS; i++) {
if (Grouphideall)
_group_show(&Groups[i]);
else
_group_hide(&Groups[i]);
_group_hide(&Groups[i]);
}
if (Grouphideall)
@ -576,34 +312,12 @@ group_alltoggle(void)
Grouphideall = 1;
}
void
group_deletecurrent(void)
{
_group_destroy(Group_current);
XUnmapWindow(X_Dpy, screen_current()->groupwin);
Groupmode = Groupnamemode = 0;
}
void
group_done(void)
{
struct group_ctx *gc = Group_current;
if (gc->name != NULL)
xfree(gc->name);
gc->name = xstrdup(shortcut_to_name[gc->shortcut]);
group_exit(1);
}
void
group_autogroup(struct client_ctx *cc)
{
struct autogroupwin *aw;
struct group_ctx *gc;
char group[CALMWM_MAXNAMELEN];
struct autogroupwin *aw;
struct group_ctx *gc;
char group[CALMWM_MAXNAMELEN];
if (cc->app_class == NULL || cc->app_name == NULL)
return;
@ -616,9 +330,17 @@ group_autogroup(struct client_ctx *cc)
}
}
if (strncmp("nogroup", group, 7) == 0)
return;
TAILQ_FOREACH(gc, &Groupq, entry) {
if (strcmp(shortcut_to_name[gc->shortcut], group) == 0)
if (strcmp(shortcut_to_name[gc->shortcut], group) == 0) {
_group_add(gc, cc);
return;
}
}
if (Conf.flags & CONF_STICKY_GROUPS)
_group_add(Group_active, cc);
}

68
hash.h
View File

@ -1,68 +0,0 @@
/*
* hash.h - generic hash template, akin to queue.h & tree.h
*
* Copyright (c) 2005 Marius 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.
*/
#ifndef _HASH_H_ /* Possibly this is too generic. */
#define _HASH_H_
#include <sys/tree.h>
#define HASH_ENTRY SPLAY_ENTRY
#define HASH_HEAD(name, type, nbuckets) \
SPLAY_HEAD(name##_HASH_TREE, type); \
struct name { \
struct name##_HASH_TREE buckets[nbuckets]; \
unsigned int (*hashfn)(struct type *elm); \
};
#define HASH_NBUCKETS(head) \
(sizeof((head)->buckets)/sizeof((head)->buckets[0]))
#define HASH_INIT(head, fn) do { \
int i; \
for (i = 0; i < HASH_NBUCKETS(head); i++) { \
SPLAY_INIT(&(head)->buckets[i]); \
} \
(head)->hashfn = fn; \
} while (0)
#define HASH_PROTOTYPE(name, type, field, cmp) \
SPLAY_PROTOTYPE(name##_HASH_TREE, type, field, cmp) \
struct type *name##_HASH_TREE_FIND(struct name *head, struct type *find); \
void name##_HASH_TREE_INSERT(struct name *head, struct type *insert);
#define HASH_GENERATE(name, type, field, cmp) \
SPLAY_GENERATE(name##_HASH_TREE, type, field, cmp) \
struct type *name##_HASH_TREE_FIND(struct name *head, struct type *find) \
{ \
struct name##_HASH_TREE *bucket = \
&head->buckets[(*head->hashfn)(find) % HASH_NBUCKETS(head)]; \
return SPLAY_FIND(name##_HASH_TREE, bucket, find); \
} \
void name##_HASH_TREE_INSERT(struct name *head, struct type *insert) \
{ \
struct name##_HASH_TREE *bucket = \
&head->buckets[(*head->hashfn)(insert) % HASH_NBUCKETS(head)]; \
\
SPLAY_INSERT(name##_HASH_TREE, bucket, insert); \
}
#define HASH_FIND(name, head, find) name##_HASH_TREE_FIND((head), (find))
#define HASH_INSERT(name, head, insert) name##_HASH_TREE_INSERT((head), (insert))
#endif /* _HASH_H_ */

View File

@ -41,7 +41,7 @@
#include <X11/cursorfont.h>
#include <X11/extensions/shape.h>
#include <X11/Xlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>

26
input.c
View File

@ -22,10 +22,9 @@
#include "calmwm.h"
int
input_keycodetrans(KeyCode kc, u_int state,
enum ctltype *ctl, char *chr, int normalize)
input_keycodetrans(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
{
int ks;
int ks;
*ctl = CTL_NONE;
*chr = '\0';
@ -70,6 +69,10 @@ input_keycodetrans(KeyCode kc, u_int state,
case XK_U:
*ctl = CTL_WIPE;
break;
case XK_h:
case XK_H:
*ctl = CTL_ERASEONE;
break;
case XK_a:
case XK_A:
*ctl = CTL_ALL;
@ -77,6 +80,21 @@ input_keycodetrans(KeyCode kc, u_int state,
}
}
if (*ctl == CTL_NONE && (state & Mod1Mask)) {
switch (ks) {
case XK_j:
case XK_J:
/* Vi "down" */
*ctl = CTL_DOWN;
break;
case XK_k:
case XK_K:
/* Vi "up" */
*ctl = CTL_UP;
break;
}
}
if (*ctl != CTL_NONE)
return (0);
@ -88,8 +106,6 @@ input_keycodetrans(KeyCode kc, u_int state,
return (-1);
*chr = (char)ks;
if (normalize)
*chr = tolower(*chr);
return (0);
}

345
kbfunc.c
View File

@ -1,6 +1,6 @@
/*
* calmwm - the calm window manager
*
*
* Copyright (c) 2004 Martin Murray <mmurray@monkey.org>
*
* Permission to use, copy, modify, and distribute this software for any
@ -23,9 +23,11 @@
#include "headers.h"
#include "calmwm.h"
#define KNOWN_HOSTS ".ssh/known_hosts"
#define HASH_MARKER "|1|"
#define MOVE_AMOUNT 1
#define KNOWN_HOSTS ".ssh/known_hosts"
#define HASH_MARKER "|1|"
#define MOVE_AMOUNT 1
extern int _xev_quit;
void
kbfunc_client_lower(struct client_ctx *cc, void *arg)
@ -39,109 +41,31 @@ kbfunc_client_raise(struct client_ctx *cc, void *arg)
client_raise(cc);
}
#define typemask (CWM_MOVE | CWM_RESIZE | CWM_PTRMOVE)
#define movemask (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)
void
kbfunc_client_move(struct client_ctx *cc, void *arg)
kbfunc_moveresize(struct client_ctx *cc, void *arg)
{
int x,y,flags,amt;
u_int mx,my;
struct screen_ctx *sc;
int x, y, flags, amt;
u_int mx, my;
sc = screen_current();
mx = my = 0;
flags = (int)arg;
amt = MOVE_AMOUNT;
if (flags & CWM_BIGMOVE) {
flags -= CWM_BIGMOVE;
amt = amt*10;
}
switch(flags) {
case CWM_UP:
my -= amt;
break;
case CWM_DOWN:
my += amt;
break;
case CWM_RIGHT:
mx += amt;
break;
case CWM_LEFT:
mx -= amt;
break;
}
cc->geom.y += my;
cc->geom.x += mx;
client_move(cc);
xu_ptr_getpos(cc->pwin, &x, &y);
cc->ptr.y = y + my;
cc->ptr.x = x + mx;
client_ptrwarp(cc);
}
void
kbfunc_client_resize(struct client_ctx *cc, void *arg)
{
int flags,mx,my;
u_int amt;
mx = my = 0;
flags = (int)arg;
amt = MOVE_AMOUNT;
if (flags & CWM_BIGMOVE) {
flags -= CWM_BIGMOVE;
amt = amt*10;
}
switch(flags) {
case CWM_UP:
my -= amt;
break;
case CWM_DOWN:
my += amt;
break;
case CWM_RIGHT:
mx += amt;
break;
case CWM_LEFT:
mx -= amt;
break;
}
cc->geom.height += my;
cc->geom.width += mx;
client_resize(cc);
/*
* Moving the cursor while resizing is problematic. Just place
* it in the middle of the window.
*/
cc->ptr.x = -1;
cc->ptr.y = -1;
client_ptrwarp(cc);
}
void
kbfunc_ptrmove(struct client_ctx *cc, void *arg)
{
int px,py,mx,my,flags,amt;
struct screen_ctx *sc = screen_current();
my = mx = 0;
flags = (int)arg;
amt = MOVE_AMOUNT;
if (flags & CWM_BIGMOVE) {
flags -= CWM_BIGMOVE;
amt = amt * 10;
}
switch(flags) {
switch (flags & movemask) {
case CWM_UP:
my -= amt;
break;
case CWM_DOWN:
case CWM_DOWN:
my += amt;
break;
case CWM_RIGHT:
@ -151,25 +75,66 @@ kbfunc_ptrmove(struct client_ctx *cc, void *arg)
mx -= amt;
break;
}
switch (flags & typemask) {
case CWM_MOVE:
cc->geom.y += my;
if (cc->geom.y + cc->geom.height < 0)
cc->geom.y = -cc->geom.height;
if (cc->geom.y > cc->sc->ymax)
cc->geom.y = cc->sc->ymax;
if (cc) {
xu_ptr_getpos(cc->pwin, &px, &py);
xu_ptr_setpos(cc->pwin, px + mx, py + my);
} else {
xu_ptr_getpos(sc->rootwin, &px, &py);
xu_ptr_setpos(sc->rootwin, px + mx, py + my);
cc->geom.x += mx;
if (cc->geom.x + cc->geom.width < 0)
cc->geom.x = -cc->geom.width;
if (cc->geom.x > cc->sc->xmax)
cc->geom.x = cc->sc->xmax;
client_move(cc);
xu_ptr_getpos(cc->pwin, &x, &y);
cc->ptr.y = y + my;
cc->ptr.x = x + mx;
client_ptrwarp(cc);
break;
case CWM_RESIZE:
if ((cc->geom.height += my) < 1)
cc->geom.height = 1;
if ((cc->geom.width += mx) < 1)
cc->geom.width = 1;
client_resize(cc);
/* Make sure the pointer stays within the window. */
xu_ptr_getpos(cc->pwin, &cc->ptr.x, &cc->ptr.y);
if (cc->ptr.x > cc->geom.width)
cc->ptr.x = cc->geom.width - cc->bwidth;
if (cc->ptr.y > cc->geom.height)
cc->ptr.y = cc->geom.height - cc->bwidth;
client_ptrwarp(cc);
break;
case CWM_PTRMOVE:
if (cc) {
xu_ptr_getpos(cc->pwin, &x, &y);
xu_ptr_setpos(cc->pwin, x + mx, y + my);
} else {
xu_ptr_getpos(sc->rootwin, &x, &y);
xu_ptr_setpos(sc->rootwin, x + mx, y + my);
}
break;
default:
warnx("invalid flags passed to kbfunc_client_moveresize");
}
}
void
kbfunc_client_search(struct client_ctx *scratch, void *arg)
{
struct menu_q menuq;
struct client_ctx *cc, *old_cc = client_current();
struct menu *mi;
struct client_ctx *cc, *old_cc;
struct menu *mi;
struct menu_q menuq;
old_cc = client_current();
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &Clientq, entry) {
XCALLOC(mi, struct menu);
strlcpy(mi->text, cc->name, sizeof(mi->text));
@ -177,9 +142,8 @@ kbfunc_client_search(struct client_ctx *scratch, void *arg)
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if ((mi = search_start(&menuq,
search_match_client, NULL,
search_print_client, "window", 0)) != NULL) {
if ((mi = menu_filter(&menuq, "window", NULL, 0,
search_match_client, search_print_client)) != NULL) {
cc = (struct client_ctx *)mi->ctx;
if (cc->flags & CLIENT_HIDDEN)
client_unhide(cc);
@ -198,13 +162,12 @@ kbfunc_client_search(struct client_ctx *scratch, void *arg)
void
kbfunc_menu_search(struct client_ctx *scratch, void *arg)
{
struct menu_q menuq;
struct menu *mi;
struct cmd *cmd;
struct cmd *cmd;
struct menu *mi;
struct menu_q menuq;
TAILQ_INIT(&menuq);
conf_cmd_refresh(&Conf);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
XCALLOC(mi, struct menu);
strlcpy(mi->text, cmd->label, sizeof(mi->text));
@ -212,8 +175,8 @@ kbfunc_menu_search(struct client_ctx *scratch, void *arg)
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if ((mi = search_start(&menuq,
search_match_text, NULL, NULL, "application", 0)) != NULL)
if ((mi = menu_filter(&menuq, "application", NULL, 0,
search_match_text, NULL)) != NULL)
u_spawn(((struct cmd *)mi->ctx)->image);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
@ -225,13 +188,15 @@ kbfunc_menu_search(struct client_ctx *scratch, void *arg)
void
kbfunc_client_cycle(struct client_ctx *scratch, void *arg)
{
client_cyclenext(0);
}
struct screen_ctx *sc;
void
kbfunc_client_rcycle(struct client_ctx *scratch, void *arg)
{
client_cyclenext(1);
sc = screen_current();
/* XXX for X apps that ignore events */
XGrabKeyboard(X_Dpy, sc->rootwin, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
client_cycle((int)arg);
}
void
@ -249,14 +214,12 @@ kbfunc_cmdexec(struct client_ctx *cc, void *arg)
void
kbfunc_term(struct client_ctx *cc, void *arg)
{
conf_cmd_refresh(&Conf);
u_spawn(Conf.termpath);
}
void
kbfunc_lock(struct client_ctx *cc, void *arg)
{
conf_cmd_refresh(&Conf);
u_spawn(Conf.lockpath);
}
@ -264,19 +227,19 @@ void
kbfunc_exec(struct client_ctx *scratch, void *arg)
{
#define NPATHS 256
char **ap, *paths[NPATHS], *path, tpath[MAXPATHLEN];
int l, i, j, ngroups;
gid_t mygroups[NGROUPS_MAX];
uid_t ruid, euid, suid;
DIR *dirp;
struct dirent *dp;
struct stat sb;
struct menu_q menuq;
struct menu *mi;
char *label;
char **ap, *paths[NPATHS], *path, *pathcpy, *label;
char tpath[MAXPATHLEN];
int l, i, j, ngroups;
gid_t mygroups[NGROUPS_MAX];
uid_t ruid, euid, suid;
DIR *dirp;
struct dirent *dp;
struct menu *mi;
struct menu_q menuq;
struct stat sb;
int cmd = (int)arg;
switch(cmd) {
switch (cmd) {
case CWM_EXEC_PROGRAM:
label = "exec";
break;
@ -294,10 +257,13 @@ kbfunc_exec(struct client_ctx *scratch, void *arg)
err(1, "getresuid failure");
TAILQ_INIT(&menuq);
/* just use default path until we have config to set this */
path = xstrdup(_PATH_DEFPATH);
if ((path = getenv("PATH")) == NULL)
path = _PATH_DEFPATH;
pathcpy = path = xstrdup(path);
for (ap = paths; ap < &paths[NPATHS - 1] &&
(*ap = strsep(&path, ":")) != NULL;) {
(*ap = strsep(&pathcpy, ":")) != NULL;) {
if (**ap != '\0')
ap++;
}
@ -320,17 +286,20 @@ kbfunc_exec(struct client_ctx *scratch, void *arg)
if (stat(tpath, &sb) == -1)
continue;
/* may we execute this file? */
if (euid == sb.st_uid)
if (euid == sb.st_uid) {
if (sb.st_mode & S_IXUSR)
goto executable;
else
continue;
for (j = 0; j < ngroups; j++)
if (mygroups[j] == sb.st_gid)
}
for (j = 0; j < ngroups; j++) {
if (mygroups[j] == sb.st_gid) {
if (sb.st_mode & S_IXGRP)
goto executable;
else
continue;
}
}
if (sb.st_mode & S_IXOTH)
goto executable;
continue;
@ -340,17 +309,19 @@ kbfunc_exec(struct client_ctx *scratch, void *arg)
strlcpy(mi->text, dp->d_name, sizeof(mi->text));
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
(void) closedir(dirp);
(void)closedir(dirp);
}
xfree(path);
if ((mi = search_start(&menuq,
search_match_exec, NULL, NULL, label, 1)) != NULL) {
if ((mi = menu_filter(&menuq, label, NULL, 1,
search_match_exec, NULL)) != NULL) {
switch (cmd) {
case CWM_EXEC_PROGRAM:
u_spawn(mi->text);
break;
case CWM_EXEC_WM:
exec_wm(mi->text);
u_exec(mi->text);
warn("%s", mi->text);
break;
default:
err(1, "kb_func: egad, cmd changed value!");
@ -364,19 +335,19 @@ kbfunc_exec(struct client_ctx *scratch, void *arg)
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
xfree(path);
}
void
kbfunc_ssh(struct client_ctx *scratch, void *arg)
{
struct menu_q menuq;
struct menu *mi;
FILE *fp;
size_t len;
char *buf, *lbuf, *p, *home;
char hostbuf[MAXHOSTNAMELEN], filename[MAXPATHLEN], cmd[256];
int l;
struct menu *mi;
struct menu_q menuq;
FILE *fp;
char *buf, *lbuf, *p, *home;
char hostbuf[MAXHOSTNAMELEN], filename[MAXPATHLEN];
char cmd[256];
int l;
size_t len;
if ((home = getenv("HOME")) == NULL)
return;
@ -417,10 +388,8 @@ kbfunc_ssh(struct client_ctx *scratch, void *arg)
xfree(lbuf);
fclose(fp);
if ((mi = search_start(&menuq,
search_match_exec, NULL, NULL, "ssh", 1)) != NULL) {
conf_cmd_refresh(&Conf);
if ((mi = menu_filter(&menuq, "ssh", NULL, 1,
search_match_exec, NULL)) != NULL) {
l = snprintf(cmd, sizeof(cmd), "%s -e ssh %s", Conf.termpath,
mi->text);
if (l != -1 && l < sizeof(cmd))
@ -438,7 +407,24 @@ kbfunc_ssh(struct client_ctx *scratch, void *arg)
void
kbfunc_client_label(struct client_ctx *cc, void *arg)
{
grab_label(cc);
struct menu *mi;
struct menu_q menuq;
char *current;
TAILQ_INIT(&menuq);
if (cc->label != NULL)
current = cc->label;
else
current = NULL;
if ((mi = menu_filter(&menuq, "label", current, 1,
search_match_text, NULL)) != NULL) {
if (cc->label != NULL)
xfree(cc->label);
cc->label = xstrdup(mi->text);
xfree(mi);
}
}
void
@ -447,43 +433,32 @@ kbfunc_client_delete(struct client_ctx *cc, void *arg)
client_send_delete(cc);
}
void
kbfunc_client_groupselect(struct client_ctx *cc, void *arg)
{
if (Groupmode)
group_done();
else
group_enter();
}
void
kbfunc_client_group(struct client_ctx *cc, void *arg)
{
if (Groupmode)
group_select(KBTOGROUP((int)arg));
else
group_hidetoggle(KBTOGROUP((int)arg));
group_hidetoggle(KBTOGROUP((int)arg));
}
void
kbfunc_client_nextgroup(struct client_ctx *cc, void *arg)
kbfunc_client_cyclegroup(struct client_ctx *cc, void *arg)
{
group_slide(1);
}
void
kbfunc_client_prevgroup(struct client_ctx *cc, void *arg)
{
group_slide(0);
group_cycle((int)arg);
}
void
kbfunc_client_nogroup(struct client_ctx *cc, void *arg)
{
if (Groupmode)
group_deletecurrent();
else
group_alltoggle();
group_alltoggle();
}
void
kbfunc_client_grouptoggle(struct client_ctx *cc, void *arg)
{
/* XXX for stupid X apps like xpdf and gvim */
XGrabKeyboard(X_Dpy, cc->pwin, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
group_sticky_toggle_enter(cc);
}
void
@ -497,3 +472,15 @@ kbfunc_client_vmaximize(struct client_ctx *cc, void *arg)
{
client_vertmaximize(cc);
}
void
kbfunc_quit_wm(struct client_ctx *cc, void *arg)
{
_xev_quit = 1;
}
void
kbfunc_reload(struct client_ctx *cc, void *arg)
{
conf_reload(&Conf);
}

403
menu.c Normal file
View File

@ -0,0 +1,403 @@
/*
* Copyright (c) 2008 Owain G. Ainsworth <oga@openbsd.org>
* Copyright (c) 2004 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.
*/
#include "headers.h"
#include "calmwm.h"
#define KeyMask (KeyPressMask|ExposureMask)
#define MenuMask (ButtonMask|ButtonMotionMask|ExposureMask| \
PointerMotionMask)
#define MenuGrabMask (ButtonMask|ButtonMotionMask|StructureNotifyMask|\
PointerMotionMask)
#define PROMPT_SCHAR '<27>'
#define PROMPT_ECHAR '<27>'
struct menu_ctx {
char searchstr[MENU_MAXENTRY + 1];
char dispstr[MENU_MAXENTRY*2 + 1];
char promptstr[MENU_MAXENTRY + 1];
int hasprompt;
int list;
int listing;
int changed;
int noresult;
int prev;
int entry;
int width;
int num;
int x;
int y;
void (*match)(struct menu_q *, struct menu_q *, char *);
void (*print)(struct menu *, int);
};
static struct menu *menu_handle_key(XEvent *, struct menu_ctx *,
struct menu_q *, struct menu_q *);
static void menu_handle_move(XEvent *, struct menu_ctx *,
struct screen_ctx *);
struct menu *menu_handle_release(XEvent *, struct menu_ctx *,
struct screen_ctx *, struct menu_q *);
static void menu_draw(struct screen_ctx *, struct menu_ctx *,
struct menu_q *, struct menu_q *);
static int menu_calc_entry(struct screen_ctx *, struct menu_ctx *,
int, int);
void
menu_init(struct screen_ctx *sc)
{
sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0,
1, 1, 1, sc->blackpixl, sc->whitepixl);
}
struct menu *
menu_filter(struct menu_q *menuq, char *prompt, char *initial, int dummy,
void (*match)(struct menu_q *, struct menu_q *, char *),
void (*print)(struct menu *, int))
{
struct screen_ctx *sc;
struct menu_ctx mc;
struct menu_q resultq;
struct menu *mi = NULL;
XEvent e;
Window focuswin;
int Mask, focusrevert;
sc = screen_current();
TAILQ_INIT(&resultq);
bzero(&mc, sizeof(mc));
xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y);
if (prompt == NULL) {
Mask = MenuMask;
mc.promptstr[0] = '\0';
mc.list = 1;
} else {
Mask = MenuMask | KeyMask; /* only accept keys if prompt */
snprintf(mc.promptstr, sizeof(mc.promptstr), "%s%c", prompt,
PROMPT_SCHAR);
snprintf(mc.dispstr, sizeof(mc.dispstr), "%s%s%c", mc.promptstr,
mc.searchstr, PROMPT_ECHAR);
mc.width = font_width(mc.dispstr, strlen(mc.dispstr));
mc.hasprompt = 1;
}
if (initial != NULL)
strlcpy(mc.searchstr, initial, sizeof(mc.searchstr));
else
mc.searchstr[0] = '\0';
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
XMoveResizeWindow(X_Dpy, sc->menuwin, mc.x, mc.y, mc.width,
font_height());
XSelectInput(X_Dpy, sc->menuwin, Mask);
XMapRaised(X_Dpy, sc->menuwin);
if (xu_ptr_grab(sc->menuwin, MenuGrabMask, Cursor_question) < 0) {
XUnmapWindow(X_Dpy, sc->menuwin);
return (NULL);
}
XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
XSetInputFocus(X_Dpy, sc->menuwin, RevertToPointerRoot, CurrentTime);
/* make sure keybindings don't remove keys from the menu stream */
XGrabKeyboard(X_Dpy, sc->menuwin, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
for (;;) {
mc.changed = 0;
XWindowEvent(X_Dpy, sc->menuwin, Mask, &e);
switch (e.type) {
default:
break;
case KeyPress:
if ((mi = menu_handle_key(&e, &mc, menuq, &resultq))
!= NULL)
goto out;
/* FALLTHROUGH */
case Expose:
menu_draw(sc, &mc, menuq, &resultq);
break;
case MotionNotify:
menu_handle_move(&e, &mc, sc);
break;
case ButtonRelease:
if ((mi = menu_handle_release(&e, &mc, sc, &resultq))
!= NULL)
goto out;
break;
}
}
out:
if ((dummy == 0 && mi->dummy) || (mi->text[0] == '\0')) { /* no match */
xfree (mi);
mi = NULL;
xu_ptr_ungrab();
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
}
XUnmapWindow(X_Dpy, sc->menuwin);
XUngrabKeyboard(X_Dpy, CurrentTime);
return (mi);
}
static struct menu *
menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
struct menu_q *resultq)
{
struct menu *mi;
enum ctltype ctl;
char chr;
size_t len;
if (input_keycodetrans(e->xkey.keycode, e->xkey.state,
&ctl, &chr) < 0)
return (NULL);
switch (ctl) {
case CTL_ERASEONE:
if ((len = strlen(mc->searchstr)) > 0) {
mc->searchstr[len - 1] = '\0';
mc->changed = 1;
}
break;
case CTL_UP:
mi = TAILQ_LAST(resultq, menu_q);
if (mi == NULL)
break;
TAILQ_REMOVE(resultq, mi, resultentry);
TAILQ_INSERT_HEAD(resultq, mi, resultentry);
break;
case CTL_DOWN:
mi = TAILQ_FIRST(resultq);
if (mi == NULL)
break;
TAILQ_REMOVE(resultq, mi, resultentry);
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
break;
case CTL_RETURN:
/*
* Return whatever the cursor is currently on. Else
* even if dummy is zero, we need to return something.
*/
if ((mi = TAILQ_FIRST(resultq)) == NULL) {
mi = xmalloc(sizeof *mi);
(void)strlcpy(mi->text,
mc->searchstr, sizeof(mi->text));
mi->dummy = 1;
}
return (mi);
case CTL_WIPE:
mc->searchstr[0] = '\0';
mc->changed = 1;
break;
case CTL_ALL:
mc->list = !mc->list;
break;
case CTL_ABORT:
mi = xmalloc(sizeof *mi);
mi->text[0] = '\0';
mi->dummy = 1;
return (mi);
default:
break;
}
if (chr != '\0') {
char str[2];
str[0] = chr;
str[1] = '\0';
mc->changed = 1;
strlcat(mc->searchstr, str, sizeof(mc->searchstr));
}
mc->noresult = 0;
if (mc->changed && strlen(mc->searchstr) > 0) {
(*mc->match)(menuq, resultq, mc->searchstr);
/* If menuq is empty, never show we've failed */
mc->noresult = TAILQ_EMPTY(resultq) && !TAILQ_EMPTY(menuq);
} else if (mc->changed)
TAILQ_INIT(resultq);
if (!mc->list && mc->listing && !mc->changed) {
TAILQ_INIT(resultq);
mc->listing = 0;
}
return (NULL);
}
static void
menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
struct menu_q *resultq)
{
struct menu *mi;
int n, dy, xsave, ysave;
if (mc->list) {
if (TAILQ_EMPTY(resultq) && mc->list) {
/* Copy them all over. */
TAILQ_FOREACH(mi, menuq, entry)
TAILQ_INSERT_TAIL(resultq, mi,
resultentry);
mc->listing = 1;
} else if (mc->changed)
mc->listing = 0;
}
mc->num = 0;
mc->width = 0;
dy = 0;
if (mc->hasprompt) {
snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%c",
mc->promptstr, mc->searchstr, PROMPT_ECHAR);
mc->width = font_width(mc->dispstr, strlen(mc->dispstr));
dy = font_height();
mc->num = 1;
}
TAILQ_FOREACH(mi, resultq, resultentry) {
char *text;
if (mc->print != NULL) {
(*mc->print)(mi, mc->listing);
text = mi->print;
} else {
mi->print[0] = '\0';
text = mi->text;
}
mc->width = MAX(mc->width, font_width(text,
MIN(strlen(text), MENU_MAXENTRY)));
dy += font_height();
mc->num++;
}
xsave = mc->x;
ysave = mc->y;
if (mc->x < 0)
mc->x = 0;
else if (mc->x + mc->width >= sc->xmax)
mc->x = sc->xmax - mc->width;
if (mc->y + dy >= sc->ymax)
mc->y = sc->ymax - dy;
/* never hide the top of the menu */
if (mc->y < 0)
mc->y = 0;
if (mc->x != xsave || mc->y != ysave)
xu_ptr_setpos(sc->rootwin, mc->x, mc->y);
XClearWindow(X_Dpy, sc->menuwin);
XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y, mc->width, dy);
if (mc->hasprompt) {
font_draw(sc, mc->dispstr, strlen(mc->dispstr), sc->menuwin,
0, font_ascent() + 1);
n = 1;
} else
n = 0;
TAILQ_FOREACH(mi, resultq, resultentry) {
char *text = mi->print[0] != '\0' ?
mi->print : mi->text;
font_draw(sc, text, MIN(strlen(text), MENU_MAXENTRY),
sc->menuwin, 0, n*font_height() + font_ascent() + 1);
n++;
}
if (mc->hasprompt && n > 1)
XFillRectangle(X_Dpy, sc->menuwin, sc->gc,
0, font_height(), mc->width, font_height());
if (mc->noresult)
XFillRectangle(X_Dpy, sc->menuwin, sc->gc,
0, 0, mc->width, font_height());
}
void
menu_handle_move(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc)
{
mc->prev = mc->entry;
mc->entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
if (mc->prev != -1)
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height() * mc->prev, mc->width, font_height());
if (mc->entry != -1) {
xu_ptr_regrab(MenuGrabMask, Cursor_select);
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height() * mc->entry, mc->width, font_height());
} else
xu_ptr_regrab(MenuGrabMask, Cursor_default);
}
struct menu *
menu_handle_release(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
struct menu_q *resultq)
{
struct menu *mi;
int entry, i = 0;
entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
xu_ptr_ungrab();
if (mc->hasprompt)
i = 1;
TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++)
break;
if (mi == NULL) {
XMALLOC(mi, struct menu);
mi->text[0] = '\0';
mi->dummy = 1;
}
return (mi);
}
static int
menu_calc_entry(struct screen_ctx *sc, struct menu_ctx *mc, int x, int y)
{
int entry;
entry = y / font_height();
/* in bounds? */
if (x < 0 || x > mc->width || y < 0 || y > font_height() * mc->num ||
entry < 0 || entry >= mc->num)
entry = -1;
if (mc->hasprompt && entry == 0)
entry = -1;
return (entry);
}

134
mousefunc.c Normal file
View File

@ -0,0 +1,134 @@
/*
* calmwm - the calm window manager
*
* Copyright (c) 2008 rivo nurges <rix@estpak.ee>
*
* 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.
*
* $Id$
*/
#include "headers.h"
#include "calmwm.h"
void
mousefunc_window_resize(struct client_ctx *cc, void *arg)
{
grab_sweep(cc);
client_resize(cc);
}
void
mousefunc_window_move(struct client_ctx *cc, void *arg)
{
grab_drag(cc);
client_move(cc);
}
void
mousefunc_window_grouptoggle(struct client_ctx *cc, void *arg)
{
group_sticky_toggle_enter(cc);
}
void
mousefunc_window_lower(struct client_ctx *cc, void *arg)
{
client_ptrsave(cc);
client_lower(cc);
}
void
mousefunc_window_hide(struct client_ctx *cc, void *arg)
{
client_hide(cc);
}
void
mousefunc_menu_group(struct client_ctx *cc, void *arg)
{
group_menu(arg);
}
void
mousefunc_menu_unhide(struct client_ctx *cc, void *arg)
{
struct client_ctx *old_cc;
struct menu *mi;
struct menu_q menuq;
char *wname;
old_cc = client_current();
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &Clientq, entry)
if (cc->flags & CLIENT_HIDDEN) {
if (cc->label != NULL)
wname = cc->label;
else
wname = cc->name;
if (wname == NULL)
continue;
XCALLOC(mi, struct menu);
strlcpy(mi->text, wname, sizeof(mi->text));
mi->ctx = cc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(&menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL) {
cc = (struct client_ctx *)mi->ctx;
client_unhide(cc);
if (old_cc != NULL)
client_ptrsave(old_cc);
client_ptrwarp(cc);
} else {
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
}
void
mousefunc_menu_cmd(struct client_ctx *cc, void *arg)
{
struct menu *mi;
struct menu_q menuq;
struct cmd *cmd;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
XCALLOC(mi, struct menu);
strlcpy(mi->text, cmd->label, sizeof(mi->text));
mi->ctx = cmd;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(&menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL)
u_spawn(((struct cmd *)mi->ctx)->image);
else
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}

582
parse.y Normal file
View File

@ -0,0 +1,582 @@
/* $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 GAP MOUSEBIND
%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 {
free(conf->DefaultFontName);
conf->DefaultFontName = $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 < 0 || $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));
TAILQ_INSERT_TAIL(&conf->ignoreq, wm, entry);
free($2);
}
| BIND STRING string {
conf_bindname(conf, $2, $3);
free($2);
free($3);
}
| GAP NUMBER NUMBER NUMBER NUMBER {
conf->gap_top = $2;
conf->gap_bottom = $3;
conf->gap_left = $4;
conf->gap_right = $5;
}
| MOUSEBIND STRING string {
conf_mousebind(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},
{ "gap", GAP},
{ "ignore", IGNORE},
{ "mousebind", MOUSEBIND},
{ "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 = xstrdup(buf);
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 != ','))
if (isalnum(c) || 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)
yylval.v.string = xstrdup(buf);
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;
nfile = xcalloc(1, sizeof(struct file));
nfile->name = xstrdup(name);
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
if (errno != ENOENT)
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;
struct keybinding *kb;
struct winmatch *wm;
struct cmd *cmd;
struct mousebinding *mb;
while (cmd = TAILQ_FIRST(&c->cmdq)) {
TAILQ_REMOVE(&c->cmdq, cmd, entry);
free(cmd);
}
while (kb = TAILQ_FIRST(&c->keybindingq)) {
TAILQ_REMOVE(&c->keybindingq, kb, entry);
free(kb);
}
while (ag = TAILQ_FIRST(&c->autogroupq)) {
TAILQ_REMOVE(&c->autogroupq, ag, entry);
free(ag->class);
if (ag->name)
free(ag->name);
free(ag->group);
free(ag);
}
while (wm = TAILQ_FIRST(&c->ignoreq)) {
TAILQ_REMOVE(&c->ignoreq, wm, entry);
free(wm);
}
while (mb = TAILQ_FIRST(&c->mousebindingq)) {
TAILQ_REMOVE(&c->mousebindingq, mb, entry);
free(mb);
}
if (c->DefaultFontName != NULL &&
c->DefaultFontName != DEFAULTFONTNAME)
free(c->DefaultFontName);
}
int
parse_config(const char *filename, struct conf *xconf)
{
int errors = 0;
XCALLOC(conf, struct conf);
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;
struct keybinding *kb;
struct winmatch *wm;
struct cmd *cmd;
struct mousebinding *mb;
conf_clear(xconf);
xconf->flags = conf->flags;
while (cmd = TAILQ_FIRST(&conf->cmdq)) {
TAILQ_REMOVE(&conf->cmdq, cmd, entry);
TAILQ_INSERT_TAIL(&xconf->cmdq, cmd, entry);
}
while (kb = TAILQ_FIRST(&conf->keybindingq)) {
TAILQ_REMOVE(&conf->keybindingq, kb, entry);
TAILQ_INSERT_TAIL(&xconf->keybindingq, kb, entry);
}
while (ag = TAILQ_FIRST(&conf->autogroupq)) {
TAILQ_REMOVE(&conf->autogroupq, ag, entry);
TAILQ_INSERT_TAIL(&xconf->autogroupq, ag, entry);
}
while (wm = TAILQ_FIRST(&conf->ignoreq)) {
TAILQ_REMOVE(&conf->ignoreq, wm, entry);
TAILQ_INSERT_TAIL(&xconf->ignoreq, wm, entry);
}
while (mb = TAILQ_FIRST(&conf->mousebindingq)) {
TAILQ_REMOVE(&conf->mousebindingq, mb, entry);
TAILQ_INSERT_TAIL(&xconf->mousebindingq, mb, entry);
}
strlcpy(xconf->termpath, conf->termpath,
sizeof(xconf->termpath));
strlcpy(xconf->lockpath, conf->lockpath,
sizeof(xconf->lockpath));
xconf->DefaultFontName = conf->DefaultFontName;
bcopy(&(conf->gap_top), &(xconf->gap_top), sizeof(int) * 4);
}
free(conf);
return (errors ? -1 : 0);
}

View File

@ -21,20 +21,13 @@
#include "headers.h"
#include "calmwm.h"
extern struct screen_ctx_q Screenq;
extern struct screen_ctx *Curscreen;
static void
_clearwindow_cb(int sig)
{
struct screen_ctx *sc = screen_current();
XUnmapWindow(X_Dpy, sc->infowin);
}
extern struct screen_ctx_q Screenq;
extern struct screen_ctx *Curscreen;
struct screen_ctx *
screen_fromroot(Window rootwin)
{
struct screen_ctx *sc;
struct screen_ctx *sc;
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == rootwin)
@ -53,10 +46,12 @@ screen_current(void)
void
screen_updatestackingorder(void)
{
Window *wins, w0, w1;
struct screen_ctx *sc = screen_current();
u_int nwins, i, s;
struct client_ctx *cc;
Window *wins, w0, w1;
struct screen_ctx *sc;
struct client_ctx *cc;
u_int nwins, i, s;
sc = screen_current();
if (!XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins))
return;
@ -72,43 +67,3 @@ screen_updatestackingorder(void)
XFree(wins);
}
void
screen_init(void)
{
struct screen_ctx *sc = screen_current();
sc->cycle_client = NULL;
sc->infowin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0,
1, 1, 1, sc->blackpixl, sc->whitepixl);
/* XXX - marius. */
if (signal(SIGALRM, _clearwindow_cb) == SIG_ERR)
err(1, "signal");
}
void
screen_infomsg(char *msg)
{
struct screen_ctx *sc = screen_current();
char buf[1024];
int dy, dx;
struct fontdesc *font = DefaultFont;
XUnmapWindow(X_Dpy, sc->infowin);
alarm(0);
snprintf(buf, sizeof(buf), ">%s", msg);
dy = font_ascent(font) + font_descent(font) + 1;
dx = font_width(font, buf, strlen(buf));
XMoveResizeWindow(X_Dpy, sc->infowin, 0, 0, dx, dy);
XMapRaised(X_Dpy, sc->infowin);
font_draw(font, buf, strlen(buf), sc->infowin,
0, font_ascent(font) + 1);
alarm(1);
}

324
search.c
View File

@ -22,286 +22,7 @@
#define SearchMask (KeyPressMask|ExposureMask)
static int _strsubmatch(char *, char *, int);
void
search_init(struct screen_ctx *sc)
{
sc->searchwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0,
1, 1, 1, sc->blackpixl, sc->whitepixl);
}
/*
* ranking. each rank type is assigned a weight. multiply this by
* the rank given. add them up. simple linear combination.
*/
/*
* Input: list of items,
* Output: choose one
* so, exactly like menus
*/
struct menu *
search_start(struct menu_q *menuq,
void (*match)(struct menu_q *, struct menu_q *, char *),
void (*rank)(struct menu_q *resultq, char *search),
void (*print)(struct menu *mi, int print),
char *prompt, int dummy)
{
struct screen_ctx *sc = screen_current();
int x, y, dx, dy, fontheight,
focusrevert, mutated, xmax, ymax, warp, added, beobnoxious = 0;
XEvent e;
char searchstr[MENU_MAXENTRY + 1];
char dispstr[MENU_MAXENTRY*2 + 1];
char promptstr[MENU_MAXENTRY + 1];
Window focuswin;
struct menu *mi = NULL, *dummy_mi = NULL;
struct menu_q resultq;
char chr;
enum ctltype ctl;
size_t len;
u_int n;
static int list = 0;
int listing = 0;
char endchar = '<EFBFBD>';
struct fontdesc *font = DefaultFont;
if (prompt == NULL)
prompt = "search";
TAILQ_INIT(&resultq);
xmax = DisplayWidth(X_Dpy, sc->which);
ymax = DisplayHeight(X_Dpy, sc->which);
xu_ptr_getpos(sc->rootwin, &x, &y);
searchstr[0] = '\0';
snprintf(promptstr, sizeof(promptstr), "%s <20>", prompt);
dy = fontheight = font_ascent(font) + font_descent(font) + 1;
snprintf(dispstr, sizeof(dispstr), "%s%c", promptstr, endchar);
dx = font_width(font, dispstr, strlen(dispstr));
XMoveResizeWindow(X_Dpy, sc->searchwin, x, y, dx, dy);
XSelectInput(X_Dpy, sc->searchwin, SearchMask);
XMapRaised(X_Dpy, sc->searchwin);
/*
* TODO: eventually, the mouse should be able to select
* results as well. Right now we grab it only to set a fancy
* cursor.
*/
if (xu_ptr_grab(sc->searchwin, 0, Cursor_question) < 0) {
XUnmapWindow(X_Dpy, sc->searchwin);
return (NULL);
}
XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
XSetInputFocus(X_Dpy, sc->searchwin, RevertToPointerRoot, CurrentTime);
for (;;) {
added = mutated = 0;
XMaskEvent(X_Dpy, SearchMask, &e);
switch (e.type) {
case KeyPress:
/*
* XXX - C-s & C-r for next and prev.
*/
if (input_keycodetrans(e.xkey.keycode, e.xkey.state,
&ctl, &chr, 1) < 0)
continue;
switch (ctl) {
case CTL_ERASEONE:
if ((len = strlen(searchstr)) > 0) {
searchstr[len - 1] = '\0';
mutated = 1;
}
break;
case CTL_UP:
mi = TAILQ_LAST(&resultq, menu_q);
if (mi == NULL)
break;
TAILQ_REMOVE(&resultq, mi, resultentry);
TAILQ_INSERT_HEAD(&resultq, mi, resultentry);
break;
case CTL_DOWN:
mi = TAILQ_FIRST(&resultq);
if (mi == NULL)
break;
TAILQ_REMOVE(&resultq, mi, resultentry);
TAILQ_INSERT_TAIL(&resultq, mi, resultentry);
break;
case CTL_RETURN:
/* This is just picking the match the
* cursor is over. */
if ((mi = TAILQ_FIRST(&resultq)) != NULL) {
goto found;
} else if (dummy) {
dummy_mi = xmalloc(sizeof *dummy_mi);
(void) strlcpy(dummy_mi->text,
searchstr, sizeof(dummy_mi->text));
dummy_mi->dummy = 1;
goto found;
}
goto out;
case CTL_WIPE:
searchstr[0] = '\0';
mutated = 1;
break;
case CTL_ALL:
list = !list;
break;
case CTL_ABORT:
goto out;
default:
break;
}
if (chr != '\0') {
char str[2];
str[0] = chr;
str[1] = '\0';
mutated = 1;
added =
strlcat(searchstr, str, sizeof(searchstr));
}
beobnoxious = 0;
if (mutated && strlen(searchstr) > 0) {
(*match)(menuq, &resultq, searchstr);
beobnoxious = TAILQ_EMPTY(&resultq);
if (!beobnoxious && rank != NULL)
(*rank)(&resultq, searchstr);
} else if (mutated)
TAILQ_INIT(&resultq);
if (!list && listing && !mutated) {
TAILQ_INIT(&resultq);
listing = 0;
}
case Expose:
if (list) {
if (TAILQ_EMPTY(&resultq) && list) {
/* Copy them over and rank them. */
TAILQ_FOREACH(mi, menuq, entry)
TAILQ_INSERT_TAIL(&resultq, mi,
resultentry);
if (rank != NULL)
(*rank)(&resultq, searchstr);
listing = 1;
} else if (mutated)
listing = 0;
}
snprintf(dispstr, sizeof(dispstr), "%s%s%c",
promptstr, searchstr, endchar);
dx = font_width(font, dispstr, strlen(dispstr));
dy = fontheight;
TAILQ_FOREACH(mi, &resultq, resultentry) {
char *text;
if (print != NULL) {
(*print)(mi, listing);
text = mi->print;
} else {
mi->print[0] = '\0';
text = mi->text;
}
dx = MAX(dx, font_width(font, text,
MIN(strlen(text), MENU_MAXENTRY)));
dy += fontheight;
}
/*
* Calculate new geometry.
*
* XXX - put this into a util function -- it's
* used elsewhere, too.
*/
warp = 0;
if (x < 0) {
x = 0;
warp = 1;
}
if (x + dx >= xmax) {
x = xmax - dx;
warp = 1;
}
if (y < 0) {
y = 0;
warp = 1;
}
if (y + dy >= ymax) {
y = ymax - dy;
/* If the menu is too high, never hide the
* top of the menu.
*/
if (y < 0)
y = 0;
warp = 1;
}
if (warp)
xu_ptr_setpos(sc->rootwin, x, y);
XClearWindow(X_Dpy, sc->searchwin);
XMoveResizeWindow(X_Dpy, sc->searchwin, x, y, dx, dy);
font_draw(font, dispstr, strlen(dispstr), sc->searchwin,
0, font_ascent(font) + 1);
n = 1;
TAILQ_FOREACH(mi, &resultq, resultentry) {
char *text = mi->print[0] != '\0' ?
mi->print : mi->text;
font_draw(font, text,
MIN(strlen(text), MENU_MAXENTRY),
sc->searchwin,
0, n*fontheight + font_ascent(font) + 1);
n++;
}
if (n > 1)
XFillRectangle(X_Dpy, sc->searchwin, sc->gc,
0, fontheight, dx, fontheight);
if (beobnoxious)
XFillRectangle(X_Dpy, sc->searchwin, sc->gc,
0, 0, dx, fontheight);
break;
}
}
out:
/* (if no match) */
xu_ptr_ungrab();
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
found:
XUnmapWindow(X_Dpy, sc->searchwin);
if (dummy && dummy_mi != NULL)
return (dummy_mi);
return (mi);
}
static int _strsubmatch(char *, char *, int);
/*
* Match: label, title, class.
@ -310,9 +31,11 @@ found:
void
search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct winname *wn;
struct menu *mi, *tierp[4], *before = NULL;
int ntiers = sizeof(tierp)/sizeof(tierp[0]);
struct winname *wn;
struct menu *mi, *tierp[4], *before = NULL;
int ntiers;
ntiers = sizeof(tierp) / sizeof(tierp[0]);
TAILQ_INIT(resultq);
@ -334,16 +57,16 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
if (cc->label != NULL && _strsubmatch(search, cc->label, 0)) {
cc->matchname = cc->label;
tier = 0;
}
}
/* Then, on window names. */
if (tier < 0) {
TAILQ_FOREACH_REVERSE(wn, &cc->nameq, winname_q, entry)
if (_strsubmatch(search, wn->name, 0)) {
cc->matchname = wn->name;
tier = 2;
break;
}
if (_strsubmatch(search, wn->name, 0)) {
cc->matchname = wn->name;
tier = 2;
break;
}
}
/*
@ -398,8 +121,10 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
void
search_print_client(struct menu *mi, int list)
{
struct client_ctx *cc = mi->ctx;
char flag = ' ';
struct client_ctx *cc;
char flag = ' ';
cc = mi->ctx;
if (cc == client_current())
flag = '!';
@ -413,9 +138,11 @@ search_print_client(struct menu *mi, int list)
if (!list && cc->matchname != cc->name &&
strlen(mi->print) < sizeof(mi->print) - 1) {
int diff = sizeof(mi->print) - 1 - strlen(mi->print);
const char *marker = "";
char buf[MENU_MAXENTRY + 1];
const char *marker = "";
char buf[MENU_MAXENTRY + 1];
int diff;
diff = sizeof(mi->print) - 1 - strlen(mi->print);
/* One for the ':' */
diff -= 1;
@ -436,7 +163,7 @@ search_print_client(struct menu *mi, int list)
void
search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
struct menu *mi;
TAILQ_INIT(resultq);
@ -448,7 +175,7 @@ search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
void
search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
struct menu *mi;
TAILQ_INIT(resultq);
@ -460,8 +187,8 @@ search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
static int
_strsubmatch(char *sub, char *str, int zeroidx)
{
size_t len, sublen;
u_int n, flen;
size_t len, sublen;
u_int n, flen;
if (sub == NULL || str == NULL)
return (0);
@ -476,6 +203,7 @@ _strsubmatch(char *sub, char *str, int zeroidx)
flen = len - sublen;
else
flen = 0;
for (n = 0; n <= flen; n++)
if (strncasecmp(sub, str + n, sublen) == 0)
return (1);

67
util.c
View File

@ -26,19 +26,10 @@
int
u_spawn(char *argstr)
{
char *args[MAXARGLEN], **ap;
char **end = &args[MAXARGLEN - 1];
switch (fork()) {
case 0:
ap = args;
while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL)
ap++;
*ap = NULL;
setsid();
execvp(args[0], args);
err(1, args[0]);
u_exec(argstr);
err(1, "%s", argstr);
break;
case -1:
warn("fork");
@ -51,42 +42,34 @@ u_spawn(char *argstr)
}
void
exec_wm(char *argstr)
u_exec(char *argstr)
{
char *args[MAXARGLEN], **ap = args;
char **end = &args[MAXARGLEN - 1];
char *args[MAXARGLEN], **ap = args;
char **end = &args[MAXARGLEN - 1], *tmp;
while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL)
while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) {
if (**ap == '\0')
continue;
ap++;
if (argstr != NULL) {
/* deal with quoted strings */
switch(argstr[0]) {
case '"':
case '\'':
if ((tmp = strchr(argstr + 1, argstr[0]))
!= NULL) {
*(tmp++) = '\0';
*(ap++) = ++argstr;
argstr = tmp;
}
break;
default:
break;
}
}
}
*ap = NULL;
setsid();
execvp(args[0], args);
warn(args[0]);
}
int dirent_isdir(char *filename) {
struct stat buffer;
int return_value;
return_value = stat(filename, &buffer);
if(return_value == -1)
return 0;
else
return S_ISDIR(buffer.st_mode);
}
int dirent_islink(char *filename) {
struct stat buffer;
int return_value;
return_value = lstat(filename, &buffer);
if(return_value == -1)
return 0;
else
return S_ISLNK(buffer.st_mode);
}

331
xevents.c
View File

@ -35,24 +35,24 @@
void
xev_handle_maprequest(struct xevent *xev, XEvent *ee)
{
XMapRequestEvent *e = &ee->xmaprequest;
struct client_ctx *cc = NULL, *old_cc = client_current();
XWindowAttributes xattr;
struct screen_ctx *sc;
XMapRequestEvent *e = &ee->xmaprequest;
XWindowAttributes xattr;
struct client_ctx *cc = NULL, *old_cc;
struct screen_ctx *sc;
#ifdef notyet
int state;
#endif
if (old_cc != NULL)
if ((old_cc = client_current()) != NULL)
client_ptrsave(old_cc);
if ((cc = client_find(e->window)) == NULL) {
if ((cc = client_find(e->window)) == NULL) {
XGetWindowAttributes(X_Dpy, e->window, &xattr);
cc = client_new(e->window, screen_fromroot(xattr.root), 1);
sc = CCTOSC(cc);
} else {
} else
cc->beepbeep = 1;
}
#ifdef notyet /* XXX - possibly, we shouldn't map if
* the window is withdrawn. */
@ -67,32 +67,20 @@ xev_handle_maprequest(struct xevent *xev, XEvent *ee)
void
xev_handle_unmapnotify(struct xevent *xev, XEvent *ee)
{
XUnmapEvent *e = &ee->xunmap;
struct client_ctx *cc;
struct screen_ctx *sc;
int wascurrent;
XUnmapEvent *e = &ee->xunmap;
struct client_ctx *cc;
if ((cc = client_find(e->window)) != NULL) {
sc = CCTOSC(cc);
wascurrent = cc == client_current();
if ((cc = client_find(e->window)) != NULL)
client_delete(cc, e->send_event, 0);
#ifdef notyet
/* XXX disable the ptrwarp until we handle it
* better. */
if (!client_delete(cc, e->send_event, 0) && wascurrent)
;/* client_ptrwarp(new_cc); */
#endif
}
xev_register(xev);
}
void
xev_handle_destroynotify(struct xevent *xev, XEvent *ee)
{
XDestroyWindowEvent *e = &ee->xdestroywindow;
struct client_ctx *cc;
XDestroyWindowEvent *e = &ee->xdestroywindow;
struct client_ctx *cc;
if ((cc = client_find(e->window)) != NULL)
client_delete(cc, 1, 1);
@ -103,10 +91,10 @@ xev_handle_destroynotify(struct xevent *xev, XEvent *ee)
void
xev_handle_configurerequest(struct xevent *xev, XEvent *ee)
{
XConfigureRequestEvent *e = &ee->xconfigurerequest;
struct client_ctx *cc;
struct screen_ctx *sc;
XWindowChanges wc;
XConfigureRequestEvent *e = &ee->xconfigurerequest;
struct client_ctx *cc;
struct screen_ctx *sc;
XWindowChanges wc;
if ((cc = client_find(e->window)) != NULL) {
sc = CCTOSC(cc);
@ -121,13 +109,13 @@ xev_handle_configurerequest(struct xevent *xev, XEvent *ee)
if (e->value_mask & CWY)
cc->geom.y = e->y;
if (cc->geom.x == 0 &&
if (cc->geom.x == 0 &&
cc->geom.width >= DisplayWidth(X_Dpy, sc->which))
cc->geom.x -= cc->bwidth;
if (cc->geom.y == 0 &&
if (cc->geom.y == 0 &&
cc->geom.height >= DisplayHeight(X_Dpy, sc->which))
cc->geom.y -= cc->bwidth;
cc->geom.y -= cc->bwidth;
client_gravitate(cc, 1);
@ -142,8 +130,8 @@ xev_handle_configurerequest(struct xevent *xev, XEvent *ee)
xev_reconfig(cc);
}
wc.x = cc != NULL ? 0 : e->x;
wc.y = cc != NULL ? 0 : e->y;
wc.x = cc != NULL ? cc->bwidth : e->x;
wc.y = cc != NULL ? cc->bwidth : e->y;
wc.width = e->width;
wc.height = e->height;
wc.stack_mode = Above;
@ -159,12 +147,12 @@ xev_handle_configurerequest(struct xevent *xev, XEvent *ee)
void
xev_handle_propertynotify(struct xevent *xev, XEvent *ee)
{
XPropertyEvent *e = &ee->xproperty;
struct client_ctx *cc;
long tmp;
XPropertyEvent *e = &ee->xproperty;
struct client_ctx *cc;
long tmp;
if ((cc = client_find(e->window)) != NULL) {
switch(e->atom) {
switch (e->atom) {
case XA_WM_NORMAL_HINTS:
XGetWMNormalHints(X_Dpy, cc->win, cc->size, &tmp);
break;
@ -183,7 +171,7 @@ xev_handle_propertynotify(struct xevent *xev, XEvent *ee)
void
xev_reconfig(struct client_ctx *cc)
{
XConfigureEvent ce;
XConfigureEvent ce;
ce.type = ConfigureNotify;
ce.event = cc->win;
@ -202,8 +190,8 @@ xev_reconfig(struct client_ctx *cc)
void
xev_handle_enternotify(struct xevent *xev, XEvent *ee)
{
XCrossingEvent *e = &ee->xcrossing;
struct client_ctx *cc;
XCrossingEvent *e = &ee->xcrossing;
struct client_ctx *cc;
if ((cc = client_find(e->window)) == NULL) {
/*
@ -233,153 +221,67 @@ xev_handle_leavenotify(struct xevent *xev, XEvent *ee)
void
xev_handle_buttonpress(struct xevent *xev, XEvent *ee)
{
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc, *old_cc = client_current();
struct screen_ctx *sc = screen_fromroot(e->root);
char *wname;
int altcontrol = e->state == (ControlMask|Mod1Mask);
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc;
struct screen_ctx *sc;
struct mousebinding *mb;
char *wname;
sc = screen_fromroot(e->root);
cc = client_find(e->window);
if (sc->rootwin == e->window && !altcontrol) {
struct menu_q menuq;
struct menu *mi;
/* Ignore caps lock and numlock */
e->state &= ~(Mod2Mask | LockMask);
/* XXXSIGH!!!! */
if (e->button == Button2) {
group_menu(e);
goto out;
}
TAILQ_INIT(&menuq);
switch (e->button) {
case Button1:
TAILQ_FOREACH(cc, &Clientq, entry) {
if (cc->flags & CLIENT_HIDDEN) {
if (cc->label != NULL)
wname = cc->label;
else
wname = cc->name;
if (wname == NULL)
continue;
XCALLOC(mi, struct menu);
strlcpy(mi->text,
wname, sizeof(mi->text));
mi->ctx = cc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
}
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
if (e->button == mb->button && e->state == mb->modmask)
break;
case Button3: {
struct cmd *cmd;
if (conf_cmd_changed(Conf.menu_path)) {
conf_cmd_clear(&Conf);
conf_cmd_populate(&Conf, Conf.menu_path);
}
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
XCALLOC(mi, struct menu);
strlcpy(mi->text, cmd->label, sizeof(mi->text));
mi->ctx = cmd;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
break;
}
default:
break;
}
if (TAILQ_EMPTY(&menuq))
goto out;
mi = (struct menu *)grab_menu(e, &menuq);
if (mi == NULL)
goto cleanup;
switch (e->button) {
case Button1:
cc = (struct client_ctx *)mi->ctx;
client_unhide(cc);
if (old_cc != NULL)
client_ptrsave(old_cc);
client_ptrwarp(cc);
break;
case Button3:
u_spawn(((struct cmd *)mi->ctx)->image);
break;
default:
break;
}
cleanup:
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
goto out;
}
if (cc == NULL || e->state == 0)
if (mb == NULL)
goto out;
sc = CCTOSC(cc);
switch (e->button) {
case Button1:
if (altcontrol && !Groupmode)
group_sticky_toggle_enter(cc);
else {
grab_drag(cc);
client_move(cc);
}
break;
case Button2:
/* XXXSIGH!!! */
if (Groupmode)
group_click(cc);
else {
grab_sweep(cc);
client_resize(cc);
}
break;
case Button3:
client_ptrsave(cc);
client_lower(cc);
break;
if (mb->context == MOUSEBIND_CTX_ROOT) {
if (e->window != sc->rootwin)
goto out;
} else if (mb->context == MOUSEBIND_CTX_WIN) {
cc = client_find(e->window);
if (cc == NULL)
goto out;
}
out:
(*mb->callback)(cc, e);
out:
xev_register(xev);
}
void
xev_handle_buttonrelease(struct xevent *xev, XEvent *ee)
{
struct client_ctx *cc = client_current();
struct client_ctx *cc;
if (cc != NULL && !Groupmode)
if ((cc = client_current()) != NULL)
group_sticky_toggle_exit(cc);
xev_register(xev);
}
void
xev_handle_keypress(struct xevent *xev, XEvent *ee)
{
XKeyEvent *e = &ee->xkey;
struct client_ctx *cc = NULL; /* Make gcc happy. */
struct keybinding *kb;
KeySym keysym, skeysym;
int modshift;
XKeyEvent *e = &ee->xkey;
struct client_ctx *cc = NULL;
struct keybinding *kb;
KeySym keysym, skeysym;
int modshift;
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1);
TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
/* we don't care about caps lock and numlock here */
e->state &= ~(LockMask | Mod2Mask);
TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
if (keysym != kb->keysym && skeysym == kb->keysym)
modshift = ShiftMask;
else
@ -389,18 +291,15 @@ xev_handle_keypress(struct xevent *xev, XEvent *ee)
continue;
if ((kb->keycode != 0 && kb->keysym == NoSymbol &&
kb->keycode == e->keycode) || kb->keysym ==
(modshift == 0 ? keysym : skeysym))
kb->keycode == e->keycode) || kb->keysym ==
(modshift == 0 ? keysym : skeysym))
break;
}
if (kb == NULL && e->window == screen_current()->groupwin)
group_display_keypress(e->keycode);
}
if (kb == NULL)
goto out;
if ((kb->flags & (KBFLAG_NEEDCLIENT|KBFLAG_FINDCLIENT)) &&
if ((kb->flags & (KBFLAG_NEEDCLIENT)) &&
(cc = client_find(e->window)) == NULL &&
(cc = client_current()) == NULL)
if (kb->flags & KBFLAG_NEEDCLIENT)
@ -418,35 +317,45 @@ out:
void
xev_handle_keyrelease(struct xevent *xev, XEvent *ee)
{
XKeyEvent *e = &ee->xkey;
struct screen_ctx *sc = screen_fromroot(e->root);
int keysym;
XKeyEvent *e = &ee->xkey;
struct screen_ctx *sc;
struct client_ctx *cc;
int keysym;
sc = screen_fromroot(e->root);
cc = client_current();
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
if (keysym != XK_Alt_L && keysym != XK_Alt_R)
goto out;
sc->altpersist = 0;
/*
* XXX - better interface... xevents should not know about
* how/when to mtf.
*/
client_mtf(NULL);
client_altrelease();
out:
if (cc != NULL) {
group_sticky_toggle_exit(cc);
XUngrabKeyboard(X_Dpy, CurrentTime);
}
out:
xev_register(xev);
}
void
xev_handle_clientmessage(struct xevent *xev, XEvent *ee)
{
XClientMessageEvent *e = &ee->xclient;
struct client_ctx *cc = client_find(e->window);
Atom xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
XClientMessageEvent *e = &ee->xclient;
Atom xa_wm_change_state;
struct client_ctx *cc;
if (cc == NULL)
xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
if ((cc = client_find(e->window)) == NULL)
goto out;
if (e->message_type == xa_wm_change_state && e->format == 32 &&
@ -456,12 +365,44 @@ out:
xev_register(xev);
}
void
xev_handle_shape(struct xevent *xev, XEvent *ee)
{
XShapeEvent *sev = (XShapeEvent *) ee;
struct client_ctx *cc;
if ((cc = client_find(sev->window)) != NULL)
client_do_shape(cc);
}
/*
* Called when the keymap has changed.
* Ungrab all keys, reload keymap and then regrab
*/
void
xev_handle_mapping(struct xevent *xev, XEvent *ee)
{
XMappingEvent *e = &ee->xmapping;
struct keybinding *kb;
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_ungrab(&Conf, kb);
XRefreshKeyboardMapping(e);
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_grab(&Conf, kb);
xev_register(xev);
}
/*
* X Event handling
*/
static struct xevent_q _xevq, _xevq_putaway;
static short _xev_q_lock = 0;
static struct xevent_q _xevq, _xevq_putaway;
static short _xev_q_lock = 0;
volatile sig_atomic_t _xev_quit = 0;
void
xev_init(void)
@ -474,7 +415,7 @@ struct xevent *
xev_new(Window *win, Window *root,
int type, void (*cb)(struct xevent *, XEvent *), void *arg)
{
struct xevent *xev;
struct xevent *xev;
XMALLOC(xev, struct xevent);
xev->xev_win = win;
@ -489,7 +430,7 @@ xev_new(Window *win, Window *root,
void
xev_register(struct xevent *xev)
{
struct xevent_q *xq;
struct xevent_q *xq;
xq = _xev_q_lock ? &_xevq_putaway : &_xevq;
TAILQ_INSERT_TAIL(xq, xev, entry);
@ -498,7 +439,7 @@ xev_register(struct xevent *xev)
void
_xev_reincorporate(void)
{
struct xevent *xev;
struct xevent *xev;
while ((xev = TAILQ_FIRST(&_xevq_putaway)) != NULL) {
TAILQ_REMOVE(&_xevq_putaway, xev, entry);
@ -509,15 +450,13 @@ _xev_reincorporate(void)
void
xev_handle_expose(struct xevent *xev, XEvent *ee)
{
XExposeEvent *e = &ee->xexpose;
struct screen_ctx *sc = screen_current();
struct client_ctx *cc;
XExposeEvent *e = &ee->xexpose;
struct client_ctx *cc;
if ((cc = client_find(e->window)) != NULL)
if ((cc = client_find(e->window)) != NULL && e->count == 0) {
client_draw_border(cc);
if (sc->groupwin == e->window)
group_display_draw(sc);
client_do_shape(cc);
}
xev_register(xev);
}
@ -534,16 +473,16 @@ xev_handle_expose(struct xevent *xev, XEvent *ee)
void
xev_loop(void)
{
Window win, root;
Window win, root;
XEvent e;
struct xevent *xev = NULL, *nextxev;
int type;
XEvent e;
struct xevent *xev, *nextxev;
for (;;) {
while (_xev_quit == 0) {
#ifdef DIAGNOSTIC
if (TAILQ_EMPTY(&_xevq))
errx(1, "X event queue empty");
#endif
#endif
XNextEvent(X_Dpy, &e);
type = e.type;
@ -583,7 +522,9 @@ xev_loop(void)
case ClientMessage:
ASSIGN1(xclient);
break;
default: /* XXX - still need shape event support. */
default:
if (e.type == Shape_ev)
xev_handle_shape(xev, &e);
break;
}

View File

@ -24,7 +24,7 @@
void *
xmalloc(size_t siz)
{
void *p;
void *p;
if ((p = malloc(siz)) == NULL)
err(1, "malloc");
@ -33,11 +33,11 @@ xmalloc(size_t siz)
}
void *
xcalloc(size_t siz)
xcalloc(size_t no, size_t siz)
{
void *p;
void *p;
if ((p = calloc(1, siz)) == NULL)
if ((p = calloc(no, siz)) == NULL)
err(1, "calloc");
return (p);
@ -52,7 +52,7 @@ xfree(void *p)
char *
xstrdup(const char *str)
{
char *p;
char *p;
if ((p = strdup(str)) == NULL)
err(1, "strdup");

71
xutil.c
View File

@ -21,19 +21,21 @@
#include "headers.h"
#include "calmwm.h"
unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask };
int
xu_ptr_grab(Window win, int mask, Cursor curs)
{
return (XGrabPointer(X_Dpy, win, False, mask,
GrabModeAsync, GrabModeAsync,
None, curs, CurrentTime) == GrabSuccess ? 0 : -1);
GrabModeAsync, GrabModeAsync,
None, curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
int
xu_ptr_regrab(int mask, Cursor curs)
{
return (XChangeActivePointerGrab(X_Dpy, mask,
curs, CurrentTime) == GrabSuccess ? 0 : -1);
curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
void
@ -42,28 +44,32 @@ xu_ptr_ungrab(void)
XUngrabPointer(X_Dpy, CurrentTime);
}
int
void
xu_btn_grab(Window win, int mask, u_int btn)
{
return (XGrabButton(X_Dpy, btn, mask, win,
int i;
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XGrabButton(X_Dpy, btn, (mask | ign_mods[i]), win,
False, ButtonMask, GrabModeAsync,
GrabModeSync, None, None) == GrabSuccess ? 0 : -1);
GrabModeSync, None, None);
}
void
xu_btn_ungrab(Window win, int mask, u_int btn)
{
XUngrabButton(X_Dpy, btn, mask, win);
int i;
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XUngrabButton(X_Dpy, btn, (mask | ign_mods[i]), win);
}
void
xu_ptr_getpos(Window rootwin, int *x, int *y)
{
int tmp0, tmp1;
u_int tmp2;
Window w0, w1;
Window w0, w1;
int tmp0, tmp1;
u_int tmp2;
XQueryPointer(X_Dpy, rootwin, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
XQueryPointer(X_Dpy, rootwin, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
}
void
@ -75,21 +81,38 @@ xu_ptr_setpos(Window win, int x, int y)
void
xu_key_grab(Window win, int mask, int keysym)
{
KeyCode code;
KeyCode code;
int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XKeycodeToKeysym(X_Dpy, code, 0) != keysym) &&
(XKeycodeToKeysym(X_Dpy, code, 1) == keysym))
mask |= ShiftMask;
XGrabKey(X_Dpy, XKeysymToKeycode(X_Dpy, keysym), mask, win, True,
GrabModeAsync, GrabModeAsync);
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XGrabKey(X_Dpy, code, (mask | ign_mods[i]), win,
True, GrabModeAsync, GrabModeAsync);
}
void
xu_key_ungrab(Window win, int mask, int keysym)
{
KeyCode code;
int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XKeycodeToKeysym(X_Dpy, code, 0) != keysym) &&
(XKeycodeToKeysym(X_Dpy, code, 1) == keysym))
mask |= ShiftMask;
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XUngrabKey(X_Dpy, code, (mask | ign_mods[i]), win);
}
void
xu_sendmsg(struct client_ctx *cc, Atom atm, long val)
{
XEvent e;
XEvent e;
memset(&e, 0, sizeof(e));
e.xclient.type = ClientMessage;
@ -105,12 +128,12 @@ xu_sendmsg(struct client_ctx *cc, Atom atm, long val)
int
xu_getprop(struct client_ctx *cc, Atom atm, Atom type, long len, u_char **p)
{
Atom realtype;
u_long n, extra;
int format;
Atom realtype;
u_long n, extra;
int format;
if (XGetWindowProperty(X_Dpy, cc->win, atm, 0L, len, False, type,
&realtype, &format, &n, &extra, p) != Success || *p == NULL)
&realtype, &format, &n, &extra, p) != Success || *p == NULL)
return (-1);
if (n == 0)
@ -122,8 +145,10 @@ xu_getprop(struct client_ctx *cc, Atom atm, Atom type, long len, u_char **p)
int
xu_getstate(struct client_ctx *cc, int *state)
{
Atom wm_state = XInternAtom(X_Dpy, "WM_STATE", False);
long *p = NULL;
Atom wm_state;
long *p = NULL;
wm_state = XInternAtom(X_Dpy, "WM_STATE", False);
if (xu_getprop(cc, wm_state, wm_state, 2L, (u_char **)&p) <= 0)
return (-1);
@ -137,8 +162,8 @@ xu_getstate(struct client_ctx *cc, int *state)
void
xu_setstate(struct client_ctx *cc, int state)
{
long dat[2];
Atom wm_state;
Atom wm_state;
long dat[2];
/* XXX cache */
wm_state = XInternAtom(X_Dpy, "WM_STATE", False);