89 Commits
v6.3 ... linux

Author SHA1 Message Date
op
73cef0ffb0 cvsimport
* refs/heads/master:
  cwm: fix a semi-transparency border issue with some applications
2023-03-22 08:27:36 +00:00
op
9eb763ab87 cwm: fix a semi-transparency border issue with some applications
When running with a compositor the border of some applications (firefox,
chromium, zathura...) is not rendered correctly.  Initializing the highest
significant bits of the color fixes it.

diff from Julien Blanchard (julien at typed-hole [dot] org); ok okan@
2023-03-22 08:27:36 +00:00
481894147a cvsimport
* refs/heads/master:
  Fix overlapping menu items as reported by kn@ and probably others at some point; with op@ and Walter Alejandro Iglesias helping along the way.  Consistently use font ascent+descent instead of an arbitrary font height+1 for individual menu item rectangles and placement.
2022-10-15 16:06:07 +00:00
4a6128d5e4 Fix overlapping menu items as reported by kn@ and probably others at
some point; with op@ and Walter Alejandro Iglesias helping along the
way.  Consistently use font ascent+descent instead of an arbitrary font
height+1 for individual menu item rectangles and placement.

ok kn@ op@
2022-10-15 16:06:07 +00:00
c55191fdc5 README: update 2022-04-30 17:11:23 +02:00
4e73ce533c cycling fix: when no client is active, warp pointer to last active;
from Walter Alejandro Iglesias.
2022-02-27 14:59:55 +00:00
7af3a7b8b6 cvsimport
* refs/heads/master:
  cycling fix: when no client is active, warp pointer to last active; from Walter Alejandro Iglesias.
  whitespace
  Fix spelling of some unused MWM hints; from Sean C. Farley.
  Add group-last command that shows only the previously active group; ok okan
  Allow bare numbers for key and mouse bindings; taken from similar support in other parse.y's; from Leon Fischer <lfischer@airmail.cc>.
  sync parse.y changes from base; ok naddy@
  Do not attempt to grab keys without a keycode; this incidentally allows XF86 keys support.
2022-02-27 14:59:55 +00:00
496bcc879d whitespace 2022-02-26 15:19:18 +00:00
5e5221d82d Fix spelling of some unused MWM hints; from Sean C. Farley.
While here, flesh out the rest of the MWM hints.
2022-02-26 15:03:42 +00:00
op
7c22b36a23 Add group-last command that shows only the previously active group; ok okan 2022-01-27 18:45:10 +00:00
81a08ddb89 Allow bare numbers for key and mouse bindings; taken from similar
support in other parse.y's; from Leon Fischer <lfischer@airmail.cc>.
2021-12-24 16:00:47 +00:00
35b0da9202 Makefile: use implicit rule for yacc
Closes #15.
2021-11-30 16:43:23 +01:00
a9eeb04606 sync parse.y changes from base; ok naddy@
original from naddy@:
> Don't declare variables as "unsigned char *" that are passed to
> functions that take "char *" arguments.  Where such chars are
> assigned to int or passed to ctype functions, explicitly cast them
> to unsigned char.
>
> For OpenBSD's clang, -Wpointer-sign has been disabled by default,
> but when the parse.y code was built elsewhere, the compiler would
> complain.
>
> With help from millert@
> ok benno@ deraadt@
2021-11-22 00:51:54 +00:00
055b84f4d4 Do not attempt to grab keys without a keycode; this incidentally allows
XF86 keys support.

found and fix by Luis Henriques <henrix@camandro.org>
2021-11-19 19:13:14 +00:00
ce65ff30c7 README: update IRC channel 2021-05-21 10:12:43 +02:00
kn
d46f34f01e Keep pointer within window on maximize/fullscreen toggle
Spawn a window, maximize it in any way, move the cursor to a window border
that is not on the screen's edge and unmaximize again:  While the window
goes back the cursor stays at the screen's edge, i.e.  focus is lost to the
underlaying window.

Moving, resizing, tiling or snapping windows in any way always moves the
cursor along iff needed, e.g. using MS-[hjkl] to move a small window from
the center to the edge keeps the cursor within window borders -- no matter
what you do with the keyboard, focus stays on that window.

Make CM-f, CM-m, CM-equal and CMS-equal (default bindings) for toggling
full-screen mode, maximization, vertical maximization and horizontal
maximization of the current window drag the cursor along if needed as well.

OK okan kmos dv
2021-04-22 10:02:55 +00:00
kn
f24dd47517 cvsimport
* refs/heads/master:
  Keep pointer within window on maximize/fullscreen toggle
2021-04-22 10:02:55 +00:00
9fb725f417 README: update 2020-05-22 21:40:11 +02:00
3a570bb679 cvsimport
* refs/heads/master:
  Fixed memory leak in xu_get_strprop.
  Prevent out of boundary write with configuration files in which too many quoted arguments are stored for other window managers.
  Allow configuring a percentage window size of the master window during htile/vtile actions. From Uwe Werler, with a few manpage tweaks.
  zap stray tabs
  Instead of using _NET_ACTIVE_WINDOW on restart, use the pointer location to determine what client to set active. Reduces a round trip for every window.
  Add support for SIGINT/SIGTERM.
  Simplify conditional construct.
  Trim event_mask to those that the root window actually needs.
  No need to lookup current client early; move to right before it is needed.
  Recommit 1.259, but now with TAILQ_FOREACH_SAFE.
  Revert previous. Causes a crash as reported by Tom Murphy.
  Simplify list markup.
  Plug two memory leaks. Also get rid of a variable that is no longer necessary.
  Remove ColormaskChange from event-mask since there's no event handler.
  Unrelated style fixes, consistency changes and sorting, appropriate dosage/removal of wrappers, simplification of name queue, client cycle joins other kb/mb bound functions.
2020-05-14 23:39:56 +02:00
91c05f9403 Fixed memory leak in xu_get_strprop.
If a client calls XSetTextProperty for a window to clear all its
properties, then allocated memory within libX11 is not freed.

OK okan@
2020-04-25 20:07:28 +00:00
5fde2a2465 use PKG_CONFIG for cross compilation in gentoo
needed for ebuild validity across different platforms which define their specific PKGCONFIG variable

add underscore in PKG_CONFIG

Closes: #14 [via git-merge-pr]
2020-04-21 14:14:50 +02:00
3ebe04ee8e Prevent out of boundary write with configuration files in which too many
quoted arguments are stored for other window managers.

The quotation handling happens within the while loop without checking if
the "end" limit has been already reached. If this happens, the final
NULL assignment leads to an out of boundary write on stack.

OK okan@
2020-04-16 17:12:49 +00:00
6407eb9bc1 Allow configuring a percentage window size of the master window during
htile/vtile actions. From Uwe Werler, with a few manpage tweaks.
2020-04-16 13:32:35 +00:00
6afdd483c7 zap stray tabs 2020-03-24 14:48:29 +00:00
6c20772841 Instead of using _NET_ACTIVE_WINDOW on restart, use the pointer location
to determine what client to set active. Reduces a round trip for every
window.
2020-03-24 14:47:29 +00:00
0a7d8cc5c4 Add support for SIGINT/SIGTERM. 2020-03-23 20:14:27 +00:00
tim
6c7b8261df Simplify conditional construct.
OK okan@
2020-03-20 18:50:08 +00:00
49f839e194 Trim event_mask to those that the root window actually needs. 2020-03-20 15:16:31 +00:00
207b71ef1c No need to lookup current client early; move to right before it is
needed.
2020-03-20 12:13:20 +00:00
tim
3b9b98c024 Recommit 1.259, but now with TAILQ_FOREACH_SAFE.
From and OK okan@

Original commit message:

Plug two memory leaks. Also get rid of a variable that is no longer
necessary.

OK okan@
2020-03-16 17:50:44 +00:00
tim
b9213d0a02 Revert previous. Causes a crash as reported by Tom Murphy. 2020-03-14 16:11:09 +00:00
tim
146fa08e4d Simplify list markup.
OK okan@ schwarze@
2020-03-13 20:50:07 +00:00
tim
d8c7d87737 Plug two memory leaks. Also get rid of a variable that is no longer
necessary.

OK okan@
2020-03-13 20:49:13 +00:00
3d1a8028c2 Remove ColormaskChange from event-mask since there's no event handler. 2020-02-28 13:38:35 +00:00
96275a835d Unrelated style fixes, consistency changes and sorting, appropriate
dosage/removal of wrappers, simplification of name queue, client cycle joins
other kb/mb bound functions.
2020-02-27 14:56:39 +00:00
54d95c0610 cvsimport
* refs/heads/master:
  Allow the 'empty' group clients to be window-{h,v}tile'd.
  Map ('5') and allow mod5mask (altgr) as a modifier.
  add, then use, xvasprintf, checking for appropriate return.
  Ensure the pointer stays within client bounds after a window 'snap' (to edge).
2020-02-07 18:53:41 +00:00
450ab06ab7 parse.y: use "queue.h"
Found by Alexander Müller <ddondy@gmail.com>.
2020-02-10 14:41:00 +01:00
7a88b2bdb2 Allow the 'empty' group clients to be window-{h,v}tile'd.
Behaviour (or lack there of) noticed by Raf Czlonka.
2020-02-07 18:53:41 +00:00
d3410dd10d Map ('5') and allow mod5mask (altgr) as a modifier.
From Artturi Alm (though changed from 'm' to '5')
2020-02-03 16:38:02 +00:00
83de84b7f8 add, then use, xvasprintf, checking for appropriate return. 2020-01-22 19:58:35 +00:00
2fc191f978 Ensure the pointer stays within client bounds after a window 'snap' (to edge).
reported by Stefan Hagen.
2020-01-21 15:50:03 +00:00
669e3406ec README: update 2020-01-04 21:45:17 +01:00
f2a2839cec Makefile: use gpg2 2020-01-04 21:45:05 +01:00
781af9c998 Because cwm warps the pointer during a client move (to stay within the client),
there's a window of time where an expose or enternotify event will get
generated for a lower client; use a hammer and drain events after keyboard
move/resize, until such a time that cwm doesn't warp the pointer.  Behavior
noticed by kn.

ok kn@
2019-08-13 18:45:38 +00:00
4154b9b194 cvsimport
* refs/heads/master:
  Because cwm warps the pointer during a client move (to stay within the client), there's a window of time where an expose or enternotify event will get generated for a lower client; use a hammer and drain events after keyboard move/resize, until such a time that cwm doesn't warp the pointer.  Behavior noticed by kn.
  Fix regression from r1.107 (lost a return); kettenis@ pointed out the high potential for a use-after-free (true!) where kn@ ran into the regression using an app that actually exercised the XGrabPointer() failure path.
  Add application section
  command uses execvp(3) not execve(2)
  Plug a memory leak in log_debug(); OK okan@
  Handle _NET_WM_NAME changes.
2019-08-13 18:45:38 +00:00
6131e36f8a Fix regression from r1.107 (lost a return); kettenis@ pointed out the high
potential for a use-after-free (true!) where kn@ ran into the regression using
an app that actually exercised the XGrabPointer() failure path.
2019-08-12 00:52:36 +00:00
kn
fdb841c3b0 Add application section
Link what is described as "applications" here to how they are actually
defined in cwmrc(5).

While here, call the configuration file what it is instead of reusing
the default path (already mentioned in the FILES section).

OK okan
2019-07-09 21:38:44 +00:00
kn
40000724cc command uses execvp(3) not execve(2)
util.c:u_exec() has been doing so since import.
2019-07-02 23:37:47 +00:00
tim
25b699b582 Plug a memory leak in log_debug(); OK okan@ 2019-04-29 19:03:20 +00:00
tim
3f1caab46a Handle _NET_WM_NAME changes.
This fixes the problem where cwm's window menu wouldn't show Firefox's current
window title if it contains non-ASCII characters.

OK okan@
2019-04-29 19:02:21 +00:00
85d88f3304 cvsimport
* refs/heads/master: (23 commits)
  Check the atom type on propertynotify before iterating.
  use screen_find() for xrandr crtc changes
  Find the managed screen from the parent window for client_current().
  Print window id in hex; while here, remove unnecessary newline.
  Similar to keypress event, fetch the screen from the event root window in the buttonpress handler; bail if we don't manage the screen. Allows us to find the current client based on the screen/event root.
  extend verbose logging for key/button events
  [keypress event] turns out we've been checking the wrong window for a matching client thus always falling back to client_current(); while the current client is problaby right in most cases, use event's subwindow (not window) to find the client. Bail early if this event came to us from a screen we don't manage. This is result of us grabing all keybindings off the root window instead of selectively.
  add parans for readibility
  Teach client_current() to use a screen to find the current client instead of iterating over all (fallback if no screen provided for now). Initially convert trivial uses of client_current().
  check cc->gc directly
  zip extra lines
  gc clientq inside groups, instead use the better maintained one per-screen
  shuffle deck chairs: rename group actions to match intent for clarity
  same thing as screen_find()
  Separate out the menu window from the client resize/move geom window; in each case, create and destroy on-demand. Isolate more menu specific code.
  fix a few misplaced (and misnamed) ewmh root window functions
  _NET_WORKAREA needs ngroups, so screen_update_geometry() needs to come after conf_group().
  simplify xftcolor config
  Tie group number and name together during config.
  Move the group index (desktop number) check to the only 2 callers that require checking due to ewmh.
  ...
2019-03-11 15:25:46 +00:00
9a7528f5b9 Check the atom type on propertynotify before iterating. 2019-03-11 15:25:46 +00:00
e55c0d48fa use screen_find() for xrandr crtc changes 2019-03-10 22:53:11 +00:00
5bc2098c6f Find the managed screen from the parent window for client_current(). 2019-03-10 20:38:28 +00:00
5071baa2aa Print window id in hex; while here, remove unnecessary newline. 2019-03-08 20:33:30 +00:00
4470a247c8 Similar to keypress event, fetch the screen from the event root window in the
buttonpress handler; bail if we don't manage the screen. Allows us to find the
current client based on the screen/event root.
2019-03-08 17:40:43 +00:00
eab4b7e4b5 extend verbose logging for key/button events 2019-03-08 15:04:39 +00:00
bf43b62414 [keypress event] turns out we've been checking the wrong window for a matching
client thus always falling back to client_current(); while the current client
is problaby right in most cases, use event's subwindow (not window) to find the
client. Bail early if this event came to us from a screen we don't manage.
This is result of us grabing all keybindings off the root window instead of
selectively.
2019-03-08 14:48:02 +00:00
2a3c2b5231 add parans for readibility 2019-03-08 13:17:26 +00:00
7c45b87622 Teach client_current() to use a screen to find the current client instead of
iterating over all (fallback if no screen provided for now). Initially convert
trivial uses of client_current().
2019-03-07 14:28:17 +00:00
01be5b4e4a check cc->gc directly 2019-03-07 13:24:44 +00:00
823566a653 zip extra lines 2019-03-07 13:24:10 +00:00
aa79351d2e gc clientq inside groups, instead use the better maintained one per-screen 2019-03-07 13:14:41 +00:00
b26202724a shuffle deck chairs: rename group actions to match intent for clarity 2019-03-07 12:54:21 +00:00
9efa6c8c85 same thing as screen_find() 2019-03-06 13:32:19 +00:00
0bda8f7606 Separate out the menu window from the client resize/move geom window; in each
case, create and destroy on-demand. Isolate more menu specific code.
2019-03-04 19:28:17 +00:00
9d5b0e5d22 fix a few misplaced (and misnamed) ewmh root window functions 2019-03-04 14:48:59 +00:00
fda68a40de _NET_WORKAREA needs ngroups, so screen_update_geometry() needs to come after
conf_group().
2019-03-04 14:36:02 +00:00
0c0551b8bf simplify xftcolor config 2019-03-04 13:33:39 +00:00
9d25218458 Tie group number and name together during config. 2019-03-01 14:32:01 +00:00
412b0c9ef4 README: update links 2019-03-01 12:29:38 +01:00
ae231f67d0 Move the group index (desktop number) check to the only 2 callers that require
checking due to ewmh.
2019-02-28 23:26:12 +00:00
880b5cda3f Ensure we don't action on the last group when the requested one is not found. 2019-02-28 23:20:52 +00:00
8cd6d1154c Selectively hide and show clients based on state; merge client_unhide() and
client_show().
2019-02-28 13:11:53 +00:00
f4286ad453 cvsimport
* refs/heads/master:
  Add 'group-close-[n]' action to close all windows within specified group.
  simplify screen 'area' usage for initial client placement
  restore order from before r1.248 (vtile/vtile containment changes).
  Rename internal functions to delinate between client remove, delete and xproto delete; 'window-close' is now the proper action, but 'window-delete' as an alias will remain until more interesting changes require breaking configs.
  Limit vtile/htile actions to clients fully within the screen of master client.
  fix missing includes
2019-02-25 19:45:12 +01:00
cd4be1c17a Add a configtest flag (-n).
based on a diff from Sascha Paunovic.
2019-02-25 18:07:48 +00:00
a5ba9aa9da Add 'group-close-[n]' action to close all windows within specified group.
heavily based on a diff from Nam Nguyen.
2019-02-25 16:40:49 +00:00
43cd19378e simplify screen 'area' usage for initial client placement 2019-02-23 19:17:17 +00:00
953cf1ce89 restore order from before r1.248 (vtile/vtile containment changes). 2019-02-22 20:52:13 +00:00
535cf541c8 Rename internal functions to delinate between client remove, delete and xproto
delete; 'window-close' is now the proper action, but 'window-delete' as an
alias will remain until more interesting changes require breaking configs.
2019-02-22 19:40:32 +00:00
044ef5a8cd Limit vtile/htile actions to clients fully within the screen of master client.
from Charles A Daniels.
2019-02-22 14:39:18 +00:00
c307e37dcb fix missing includes 2019-02-13 15:43:24 +00:00
544b4da339 cvsimport
* refs/heads/master:
  Stop asking for events (NoEventMask) from menu window once done with the menu (we don't destroy it, only unmap).
  Allow 'transientfor' clients to inherit group and bwidth either during init or via property notify events. Previously only the flags were set but nothing was in the path to apply said flags and/or bwidth. Required slight of re-orgnaization of client_init.
  merge from base, from sashan@:
  Use the original client border width to adjust initial placement of clients containing {P,US}Position requests where they are explicitly set to 'ignore' in cwmrc(5); clients are unaware that their border will be altered (removed in this case) when calcuating position and thus end up a factor of their original border width off once mapped by cwm(1). cwm(1) will essentially shift the client to the edge if the original request's position and border match.
2018-11-14 19:22:51 +00:00
695eb1d8e5 Stop asking for events (NoEventMask) from menu window once done with
the menu (we don't destroy it, only unmap).
2018-11-14 19:22:51 +00:00
194589eb6b Allow 'transientfor' clients to inherit group and bwidth either during init or
via property notify events. Previously only the flags were set but nothing was
in the path to apply said flags and/or bwidth. Required slight of re-orgnaization
of client_init.
2018-11-13 17:37:13 +00:00
268deed916 merge from base, from sashan@:
> - odd condition/test in PF lexer
> (and other lexers too)
>
> This commit rectifies earlier change:
>
> in the lex... even inside quotes, a \ followed by space or tab should
> expand to space or tab, and a \ followed by newline should be ignored
> (as a line continuation).  compatible with the needs of hoststated
> (which has the most strict quoted string requirements), and ifstated
> (where one commonly does line continuations in strings).
>
> OK deraadt@, OK millert@
2018-11-09 16:00:54 +00:00
a63b87e315 Use the original client border width to adjust initial placement of clients
containing {P,US}Position requests where they are explicitly set to 'ignore' in
cwmrc(5); clients are unaware that their border will be altered (removed in
this case) when calcuating position and thus end up a factor of their original
border width off once mapped by cwm(1). cwm(1) will essentially shift the
client to the edge if the original request's position and border match.

Window offset noticed by at least Andre Stoebe via bugs@, and others since
(and likely before). Thanks!
2018-11-08 15:49:42 +00:00
b4d4eba6af cvsimport
* refs/heads/master:
  Remove unused prototype; from Ross L Richardson.
2018-07-16 14:19:23 +00:00
9999c3e6e0 Remove unused prototype; from Ross L Richardson. 2018-07-16 14:19:23 +00:00
18 changed files with 1139 additions and 933 deletions

View File

@ -11,26 +11,25 @@ SRCS= calmwm.c screen.c xmalloc.c client.c menu.c \
OBJS= calmwm.o screen.o xmalloc.o client.o menu.o \ OBJS= calmwm.o screen.o xmalloc.o client.o menu.o \
search.o util.o xutil.o conf.o xevents.o group.o \ search.o util.o xutil.o conf.o xevents.o group.o \
kbfunc.o strlcpy.o strlcat.o y.tab.o \ kbfunc.o strlcpy.o strlcat.o parse.o \
strtonum.o reallocarray.o strtonum.o reallocarray.o
PKG_CONFIG?= pkg-config
CPPFLAGS+= `pkg-config --cflags x11 xft xrandr` CPPFLAGS+= `${PKG_CONFIG} --cflags x11 xft xrandr`
CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE
LDFLAGS+= `pkg-config --libs x11 xft xrandr` LDFLAGS+= `${PKG_CONFIG} --libs x11 xft xrandr`
MANPREFIX?= ${PREFIX}/share/man MANPREFIX?= ${PREFIX}/share/man
all: ${PROG} all: ${PROG}
clean: clean:
rm -f ${OBJS} ${PROG} y.tab.c rm -f ${OBJS} ${PROG} parse.c
y.tab.c: parse.y ${PROG}: ${OBJS}
yacc parse.y
${PROG}: ${OBJS} y.tab.o
${CC} ${OBJS} ${LDFLAGS} -o ${PROG} ${CC} ${OBJS} ${LDFLAGS} -o ${PROG}
.c.o: .c.o:
@ -48,6 +47,8 @@ release:
sign: sign:
VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \ VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \
gpg --armor --detach-sign cwm-$$VERSION.tar.gz && \ gpg2 --armor --detach-sign cwm-$$VERSION.tar.gz && \
signify -S -s ~/.signify/cwm.sec -m cwm-$$VERSION.tar.gz && \ signify -S -s ~/.signify/cwm.sec -m cwm-$$VERSION.tar.gz && \
sed -i '1cuntrusted comment: verify with cwm.pub' cwm-$$VERSION.tar.gz.sig sed -i '1cuntrusted comment: verify with cwm.pub' cwm-$$VERSION.tar.gz.sig
.PRECIOUS: parse.c

37
README
View File

@ -1,5 +1,4 @@
This is a port of OpenBSD's excellent cwm[0] to Linux and other This is a port of OpenBSD's excellent cwm[0] to Linux and other Unices.
Unices.
cwm is a window manager for X11 which contains many features that cwm is a window manager for X11 which contains many features that
concentrate on the efficiency and transparency of window concentrate on the efficiency and transparency of window
@ -13,10 +12,11 @@ OpenBSD, FreeBSD, NetBSD, OS X 10.9 and Linux.
This version actively tracks changes in the OpenBSD CVS repository. This version actively tracks changes in the OpenBSD CVS repository.
Releases are roughly coordinated. Releases are roughly coordinated.
The revision controlled version is at https://github.com/chneukirchen/cwm The revision controlled version is at https://github.com/leahneukirchen/cwm
Releases can be found at http://chneukirchen.org/releases Releases can be found at http://leahneukirchen.org/releases
You are welcome to join the IRC channel ##cwm on Freenode to talk about cwm. You are welcome to join the IRC channel ##cwm on irc.libera.chat
to talk about cwm.
ChangeLog: ChangeLog:
@ -115,6 +115,33 @@ Changes made between OpenBSD 6.2 and 6.3
2018-05-14: Fifth public release 6.3 of portable cwm. 2018-05-14: Fifth public release 6.3 of portable cwm.
2020-01-04: Sixth public release 6.6 of portable cwm.
Changes made between OpenBSD 6.4 and 6.5
* Added a configtest flag (-n) to cwm(1).
* Introduced 'group-close-[n]' action to cwm(1) to close all windows
within a specified group.
2020-05-22: Seventh public release 6.7 of portable cwm.
Changes made between OpenBSD 6.6 and 6.7
* Allowed cwm(1) configuration of window size based on percentage of
the master window during horizontal and vertical tiling actions.
* Allowed use of window-htile and window-vtile with the "empty" group
clients in cwm(1).
2022-04-30: Eighth public release 7.1 of portable cwm.
Changes made between OpenBSD 6.9 and 7.0
* Changed cwm(1) maximization and full-screen mode toggling to keep
the cursor within the window, preventing focus loss.
Changes made between OpenBSD 7.0 and 7.1
* Added a cwm(1) "group-last" command that shows only the previously
active group.
* Allowed bare numbers for key and mouse bindings in cwm(1).
--Leah Neukirchen <leah@vuxu.org> --Leah Neukirchen <leah@vuxu.org>
[0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/ [0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/

View File

@ -44,6 +44,7 @@ struct screen_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct conf Conf; struct conf Conf;
volatile sig_atomic_t cwm_status; volatile sig_atomic_t cwm_status;
void usage(void);
static void sighdlr(int); static void sighdlr(int);
static int x_errorhandler(Display *, XErrorEvent *); static int x_errorhandler(Display *, XErrorEvent *);
static int x_init(const char *); static int x_init(const char *);
@ -55,7 +56,7 @@ main(int argc, char **argv)
{ {
char *display_name = NULL; char *display_name = NULL;
char *fallback; char *fallback;
int ch, xfd; int ch, xfd, nflag = 0;
struct pollfd pfd[1]; struct pollfd pfd[1];
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
@ -66,7 +67,7 @@ main(int argc, char **argv)
fallback = u_argv(argv); fallback = u_argv(argv);
Conf.wm_argv = u_argv(argv); Conf.wm_argv = u_argv(argv);
while ((ch = getopt(argc, argv, "c:d:v")) != -1) { while ((ch = getopt(argc, argv, "c:d:nv")) != -1) {
switch (ch) { switch (ch) {
case 'c': case 'c':
free(Conf.conf_file); free(Conf.conf_file);
@ -75,6 +76,9 @@ main(int argc, char **argv)
case 'd': case 'd':
display_name = optarg; display_name = optarg;
break; break;
case 'n':
nflag = 1;
break;
case 'v': case 'v':
Conf.debug++; Conf.debug++;
break; break;
@ -85,13 +89,19 @@ main(int argc, char **argv)
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (signal(SIGCHLD, sighdlr) == SIG_ERR) if (signal(SIGCHLD, sighdlr) == SIG_ERR ||
err(1, "signal"); signal(SIGHUP, sighdlr) == SIG_ERR ||
if (signal(SIGHUP, sighdlr) == SIG_ERR) signal(SIGINT, sighdlr) == SIG_ERR ||
signal(SIGTERM, sighdlr) == SIG_ERR)
err(1, "signal"); err(1, "signal");
if (parse_config(Conf.conf_file, &Conf) == -1) if (parse_config(Conf.conf_file, &Conf) == -1) {
warnx("error parsing config file"); warnx("error parsing config file");
if (nflag)
return 1;
}
if (nflag)
return 0;
xfd = x_init(display_name); xfd = x_init(display_name);
cwm_status = CWM_RUNNING; cwm_status = CWM_RUNNING;
@ -118,7 +128,7 @@ main(int argc, char **argv)
u_exec(fallback); u_exec(fallback);
} }
return(0); return 0;
} }
static int static int
@ -136,7 +146,7 @@ x_init(const char *dpyname)
Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i); Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i);
conf_atoms(); xu_atom_init();
conf_cursor(&Conf); conf_cursor(&Conf);
for (i = 0; i < ScreenCount(X_Dpy); i++) for (i = 0; i < ScreenCount(X_Dpy); i++)
@ -159,8 +169,6 @@ x_teardown(void)
DefaultColormap(X_Dpy, sc->which), DefaultColormap(X_Dpy, sc->which),
&sc->xftcolor[i]); &sc->xftcolor[i]);
XftFontClose(X_Dpy, sc->xftfont); XftFontClose(X_Dpy, sc->xftfont);
XftDrawDestroy(sc->menu.xftdraw);
XDestroyWindow(X_Dpy, sc->menu.win);
XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin); XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin);
} }
XUngrabPointer(X_Dpy, CurrentTime); XUngrabPointer(X_Dpy, CurrentTime);
@ -176,7 +184,7 @@ static int
x_wmerrorhandler(Display *dpy, XErrorEvent *e) x_wmerrorhandler(Display *dpy, XErrorEvent *e)
{ {
errx(1, "root window unavailable - perhaps another wm is running?"); errx(1, "root window unavailable - perhaps another wm is running?");
return(0); return 0;
} }
static int static int
@ -192,7 +200,7 @@ x_errorhandler(Display *dpy, XErrorEvent *e)
warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg); warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg);
#endif #endif
return(0); return 0;
} }
static void static void
@ -211,6 +219,10 @@ sighdlr(int sig)
case SIGHUP: case SIGHUP:
cwm_status = CWM_EXEC_WM; cwm_status = CWM_EXEC_WM;
break; break;
case SIGINT:
case SIGTERM:
cwm_status = CWM_QUIT;
break;
} }
errno = save_errno; errno = save_errno;
@ -221,7 +233,7 @@ usage(void)
{ {
extern char *__progname; extern char *__progname;
(void)fprintf(stderr, "usage: %s [-v] [-c file] [-d display]\n", (void)fprintf(stderr, "usage: %s [-nv] [-c file] [-d display]\n",
__progname); __progname);
exit(1); exit(1);
} }

164
calmwm.h
View File

@ -68,9 +68,6 @@ size_t strlcpy(char *, const char *, size_t);
#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
#define MOUSEMASK (BUTTONMASK | PointerMotionMask) #define MOUSEMASK (BUTTONMASK | PointerMotionMask)
#define MENUMASK (MOUSEMASK | ButtonMotionMask | KeyPressMask | \
ExposureMask)
#define MENUGRABMASK (MOUSEMASK | ButtonMotionMask | StructureNotifyMask)
#define IGNOREMODMASK (LockMask | Mod2Mask | 0x2000) #define IGNOREMODMASK (LockMask | Mod2Mask | 0x2000)
/* direction/amount */ /* direction/amount */
@ -87,7 +84,6 @@ size_t strlcpy(char *, const char *, size_t);
#define CWM_UP_LEFT (CWM_UP | CWM_LEFT) #define CWM_UP_LEFT (CWM_UP | CWM_LEFT)
#define CWM_DOWN_RIGHT (CWM_DOWN | CWM_RIGHT) #define CWM_DOWN_RIGHT (CWM_DOWN | CWM_RIGHT)
#define CWM_DOWN_LEFT (CWM_DOWN | CWM_LEFT) #define CWM_DOWN_LEFT (CWM_DOWN | CWM_LEFT)
#define DIRECTIONMASK (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)
#define CWM_CYCLE_FORWARD 0x0001 #define CWM_CYCLE_FORWARD 0x0001
#define CWM_CYCLE_REVERSE 0x0002 #define CWM_CYCLE_REVERSE 0x0002
@ -124,11 +120,6 @@ struct geom {
int w; int w;
int h; int h;
}; };
enum apply_gap {
CWM_NOGAP = 0,
CWM_GAP
};
struct gap { struct gap {
int top; int top;
int bottom; int bottom;
@ -145,12 +136,12 @@ TAILQ_HEAD(ignore_q, winname);
struct client_ctx { struct client_ctx {
TAILQ_ENTRY(client_ctx) entry; TAILQ_ENTRY(client_ctx) entry;
TAILQ_ENTRY(client_ctx) group_entry;
struct screen_ctx *sc; struct screen_ctx *sc;
struct group_ctx *gc; struct group_ctx *gc;
Window win; Window win;
Colormap colormap; Colormap colormap;
int bwidth; /* border width */ int bwidth; /* border width */
int obwidth; /* original border width */
struct geom geom, savegeom, fullgeom; struct geom geom, savegeom, fullgeom;
struct { struct {
long flags; /* defined hints */ long flags; /* defined hints */
@ -200,8 +191,9 @@ struct client_ctx {
struct name_q nameq; struct name_q nameq;
char *name; char *name;
char *label; char *label;
XClassHint ch; char *res_class; /* class hint */
XWMHints *wmh; char *res_name; /* class hint */
int initial_state; /* wm hint */
}; };
TAILQ_HEAD(client_q, client_ctx); TAILQ_HEAD(client_q, client_ctx);
@ -210,7 +202,6 @@ struct group_ctx {
struct screen_ctx *sc; struct screen_ctx *sc;
char *name; char *name;
int num; int num;
struct client_q clientq;
}; };
TAILQ_HEAD(group_q, group_ctx); TAILQ_HEAD(group_q, group_ctx);
@ -244,12 +235,13 @@ struct screen_ctx {
struct region_q regionq; struct region_q regionq;
struct group_q groupq; struct group_q groupq;
struct group_ctx *group_active; struct group_ctx *group_active;
struct group_ctx *group_last;
Colormap colormap; Colormap colormap;
Visual *visual; Visual *visual;
struct { struct {
Window win; Window win;
XftDraw *xftdraw; XftDraw *xftdraw;
} menu; } prop;
XftColor xftcolor[CWM_COLOR_NITEMS]; XftColor xftcolor[CWM_COLOR_NITEMS];
XftFont *xftfont; XftFont *xftfont;
}; };
@ -321,6 +313,8 @@ struct conf {
int bwidth; int bwidth;
int mamount; int mamount;
int snapdist; int snapdist;
int htile;
int vtile;
struct gap gap; struct gap gap;
char *color[CWM_COLOR_NITEMS]; char *color[CWM_COLOR_NITEMS];
char *font; char *font;
@ -336,30 +330,39 @@ struct conf {
/* MWM hints */ /* MWM hints */
struct mwm_hints { struct mwm_hints {
#define MWM_HINTS_ELEMENTS 3L #define MWM_HINTS_ELEMENTS 5L
#define MWM_FLAGS_STATUS (1<<3)
#define MWM_FLAGS_FUNCTIONS (1<<0) #define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_FLAGS_DECORATIONS (1<<1) #define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_FLAGS_INPUT_MODE (1<<2) #define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_HINTS_STATUS (1L << 3)
unsigned long flags; unsigned long flags;
#define MWM_FUNCS_ALL (1<<0) #define MWM_FUNC_ALL (1L << 0)
#define MWM_FUNCS_RESIZE (1<<1) #define MWM_FUNC_RESIZE (1L << 1)
#define MWM_FUNCS_MOVE (1<<2) #define MWM_FUNC_MOVE (1L << 2)
#define MWM_FUNCS_MINIMIZE (1<<3) #define MWM_FUNC_MINIMIZE (1L << 3)
#define MWM_FUNCS_MAXIMIZE (1<<4) #define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNCS_CLOSE (1<<5) #define MWM_FUNC_CLOSE (1L << 5)
unsigned long functions; unsigned long functions;
#define MWM_DECOR_ALL (1<<0) #define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1<<1) #define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZE_HANDLE (1<<2) #define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLEBAR (1<<3) #define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1<<4) #define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1<<5) #define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1<<6) #define MWM_DECOR_MAXIMIZE (1L << 6)
unsigned long decorations; unsigned long decorations;
#define MWM_INPUT_MODELESS 0
#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
#define MWM_INPUT_SYSTEM_MODAL 2
#define MWM_INPUT_FULL_APPLICATION_MODAL 3
long inputMode;
#define MWM_TEAROFF_WINDOW (1L << 0)
unsigned long status;
}; };
enum cwmh { enum cwmh {
@ -417,78 +420,75 @@ extern struct conf Conf;
void usage(void); void usage(void);
void client_applysizehints(struct client_ctx *); void client_apply_sizehints(struct client_ctx *);
void client_close(struct client_ctx *);
void client_config(struct client_ctx *); void client_config(struct client_ctx *);
struct client_ctx *client_current(void); struct client_ctx *client_current(struct screen_ctx *);
void client_cycle(struct screen_ctx *, int);
void client_delete(struct client_ctx *);
void client_draw_border(struct client_ctx *); void client_draw_border(struct client_ctx *);
struct client_ctx *client_find(Window); struct client_ctx *client_find(Window);
long client_get_wm_state(struct client_ctx *); void client_get_sizehints(struct client_ctx *);
void client_getsizehints(struct client_ctx *);
void client_hide(struct client_ctx *); void client_hide(struct client_ctx *);
void client_htile(struct client_ctx *); void client_htile(struct client_ctx *);
int client_inbound(struct client_ctx *, int, int);
struct client_ctx *client_init(Window, struct screen_ctx *);
void client_lower(struct client_ctx *); void client_lower(struct client_ctx *);
void client_map(struct client_ctx *);
void client_msg(struct client_ctx *, Atom, Time);
void client_move(struct client_ctx *); void client_move(struct client_ctx *);
void client_mtf(struct client_ctx *); void client_mtf(struct client_ctx *);
int client_inbound(struct client_ctx *, int, int); struct client_ctx *client_next(struct client_ctx *);
struct client_ctx *client_init(Window, struct screen_ctx *, int); struct client_ctx *client_prev(struct client_ctx *);
void client_ptr_inbound(struct client_ctx *, int); void client_ptr_inbound(struct client_ctx *, int);
void client_ptrsave(struct client_ctx *); void client_ptr_save(struct client_ctx *);
void client_ptrwarp(struct client_ctx *); void client_ptr_warp(struct client_ctx *);
void client_raise(struct client_ctx *); void client_raise(struct client_ctx *);
void client_remove(struct client_ctx *);
void client_resize(struct client_ctx *, int); void client_resize(struct client_ctx *, int);
void client_send_delete(struct client_ctx *); void client_set_active(struct client_ctx *);
void client_set_wm_state(struct client_ctx *, long); void client_set_name(struct client_ctx *);
void client_setactive(struct client_ctx *);
void client_setname(struct client_ctx *);
void client_show(struct client_ctx *); void client_show(struct client_ctx *);
int client_snapcalc(int, int, int, int, int); int client_snapcalc(int, int, int, int, int);
void client_toggle_freeze(struct client_ctx *);
void client_toggle_fullscreen(struct client_ctx *);
void client_toggle_hidden(struct client_ctx *); void client_toggle_hidden(struct client_ctx *);
void client_toggle_hmaximize(struct client_ctx *); void client_toggle_hmaximize(struct client_ctx *);
void client_toggle_fullscreen(struct client_ctx *);
void client_toggle_freeze(struct client_ctx *);
void client_toggle_maximize(struct client_ctx *); void client_toggle_maximize(struct client_ctx *);
void client_toggle_skip_pager(struct client_ctx *); void client_toggle_skip_pager(struct client_ctx *);
void client_toggle_skip_taskbar(struct client_ctx *); void client_toggle_skip_taskbar(struct client_ctx *);
void client_toggle_sticky(struct client_ctx *); void client_toggle_sticky(struct client_ctx *);
void client_toggle_vmaximize(struct client_ctx *); void client_toggle_vmaximize(struct client_ctx *);
void client_transient(struct client_ctx *); void client_transient(struct client_ctx *);
void client_unhide(struct client_ctx *);
void client_urgency(struct client_ctx *); void client_urgency(struct client_ctx *);
void client_vtile(struct client_ctx *); void client_vtile(struct client_ctx *);
void client_wm_hints(struct client_ctx *); void client_wm_hints(struct client_ctx *);
void group_alltoggle(struct screen_ctx *);
void group_assign(struct group_ctx *, struct client_ctx *); void group_assign(struct group_ctx *, struct client_ctx *);
int group_autogroup(struct client_ctx *); int group_autogroup(struct client_ctx *);
void group_cycle(struct screen_ctx *, int); void group_cycle(struct screen_ctx *, int);
void group_hide(struct group_ctx *); void group_hide(struct group_ctx *);
void group_hidetoggle(struct screen_ctx *, int);
int group_holds_only_hidden(struct group_ctx *); int group_holds_only_hidden(struct group_ctx *);
int group_holds_only_sticky(struct group_ctx *); int group_holds_only_sticky(struct group_ctx *);
void group_init(struct screen_ctx *, int); void group_init(struct screen_ctx *, int, const char *);
void group_movetogroup(struct client_ctx *, int); void group_movetogroup(struct client_ctx *, int);
void group_only(struct screen_ctx *, int); void group_only(struct screen_ctx *, int);
void group_close(struct screen_ctx *, int);
int group_restore(struct client_ctx *); int group_restore(struct client_ctx *);
void group_show(struct group_ctx *); void group_show(struct group_ctx *);
void group_toggle(struct screen_ctx *, int);
void group_toggle_all(struct screen_ctx *);
void group_toggle_membership(struct client_ctx *); void group_toggle_membership(struct client_ctx *);
void group_update_names(struct screen_ctx *); void group_update_names(struct screen_ctx *);
void search_match_client(struct menu_q *, struct menu_q *, void search_match_client(struct menu_q *, struct menu_q *,
char *); char *);
void search_match_cmd(struct menu_q *, struct menu_q *,
char *);
void search_match_exec(struct menu_q *, struct menu_q *, void search_match_exec(struct menu_q *, struct menu_q *,
char *); char *);
void search_match_group(struct menu_q *, struct menu_q *,
char *);
void search_match_path(struct menu_q *, struct menu_q *, void search_match_path(struct menu_q *, struct menu_q *,
char *); char *);
void search_match_text(struct menu_q *, struct menu_q *, void search_match_text(struct menu_q *, struct menu_q *,
char *); char *);
void search_match_cmd(struct menu_q *, struct menu_q *,
char *);
void search_match_group(struct menu_q *, struct menu_q *,
char *);
void search_match_wm(struct menu_q *, struct menu_q *, void search_match_wm(struct menu_q *, struct menu_q *,
char *); char *);
void search_print_client(struct menu *, int); void search_print_client(struct menu *, int);
@ -498,20 +498,25 @@ void search_print_text(struct menu *, int);
void search_print_wm(struct menu *, int); void search_print_wm(struct menu *, int);
struct region_ctx *region_find(struct screen_ctx *, int, int); struct region_ctx *region_find(struct screen_ctx *, int, int);
void screen_assert_clients_within(struct screen_ctx *);
struct geom screen_area(struct screen_ctx *, int, int, int);
struct screen_ctx *screen_find(Window); struct screen_ctx *screen_find(Window);
struct geom screen_area(struct screen_ctx *, int, int,
enum apply_gap);
void screen_init(int); void screen_init(int);
void screen_prop_win_create(struct screen_ctx *, Window);
void screen_prop_win_destroy(struct screen_ctx *);
void screen_prop_win_draw(struct screen_ctx *,
const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
void screen_update_geometry(struct screen_ctx *); void screen_update_geometry(struct screen_ctx *);
void screen_updatestackingorder(struct screen_ctx *); void screen_updatestackingorder(struct screen_ctx *);
void screen_assert_clients_within(struct screen_ctx *);
void kbfunc_cwm_status(void *, struct cargs *); void kbfunc_cwm_status(void *, struct cargs *);
void kbfunc_ptrmove(void *, struct cargs *); void kbfunc_ptrmove(void *, struct cargs *);
void kbfunc_client_snap(void *, struct cargs *); void kbfunc_client_snap(void *, struct cargs *);
void kbfunc_client_move(void *, struct cargs *); void kbfunc_client_move(void *, struct cargs *);
void kbfunc_client_resize(void *, struct cargs *); void kbfunc_client_resize(void *, struct cargs *);
void kbfunc_client_delete(void *, struct cargs *); void kbfunc_client_close(void *, struct cargs *);
void kbfunc_client_lower(void *, struct cargs *); void kbfunc_client_lower(void *, struct cargs *);
void kbfunc_client_raise(void *, struct cargs *); void kbfunc_client_raise(void *, struct cargs *);
void kbfunc_client_hide(void *, struct cargs *); void kbfunc_client_hide(void *, struct cargs *);
@ -529,8 +534,10 @@ void kbfunc_client_toggle_group(void *, struct cargs *);
void kbfunc_client_movetogroup(void *, struct cargs *); void kbfunc_client_movetogroup(void *, struct cargs *);
void kbfunc_group_toggle(void *, struct cargs *); void kbfunc_group_toggle(void *, struct cargs *);
void kbfunc_group_only(void *, struct cargs *); void kbfunc_group_only(void *, struct cargs *);
void kbfunc_group_last(void *, struct cargs *);
void kbfunc_group_close(void *, struct cargs *);
void kbfunc_group_cycle(void *, struct cargs *); void kbfunc_group_cycle(void *, struct cargs *);
void kbfunc_group_alltoggle(void *, struct cargs *); void kbfunc_group_toggle_all(void *, struct cargs *);
void kbfunc_menu_client(void *, struct cargs *); void kbfunc_menu_client(void *, struct cargs *);
void kbfunc_menu_cmd(void *, struct cargs *); void kbfunc_menu_cmd(void *, struct cargs *);
void kbfunc_menu_group(void *, struct cargs *); void kbfunc_menu_group(void *, struct cargs *);
@ -542,10 +549,6 @@ void kbfunc_exec_cmd(void *, struct cargs *);
void kbfunc_exec_lock(void *, struct cargs *); void kbfunc_exec_lock(void *, struct cargs *);
void kbfunc_exec_term(void *, struct cargs *); void kbfunc_exec_term(void *, struct cargs *);
void menu_windraw(struct screen_ctx *, Window,
const char *, ...)
__attribute__((__format__ (printf, 3, 4)))
__attribute__((__nonnull__ (3)));
struct menu *menu_filter(struct screen_ctx *, struct menu_q *, struct menu *menu_filter(struct screen_ctx *, struct menu_q *,
const char *, const char *, int, const char *, const char *, int,
void (*)(struct menu_q *, struct menu_q *, char *), void (*)(struct menu_q *, struct menu_q *, char *),
@ -556,7 +559,6 @@ void menuq_clear(struct menu_q *);
int parse_config(const char *, struct conf *); int parse_config(const char *, struct conf *);
void conf_atoms(void);
void conf_autogroup(struct conf *, int, const char *, void conf_autogroup(struct conf *, int, const char *,
const char *); const char *);
int conf_bind_key(struct conf *, const char *, int conf_bind_key(struct conf *, const char *,
@ -575,34 +577,38 @@ void conf_grab_mouse(Window);
void conf_init(struct conf *); void conf_init(struct conf *);
void conf_ignore(struct conf *, const char *); void conf_ignore(struct conf *, const char *);
void conf_screen(struct screen_ctx *); void conf_screen(struct screen_ctx *);
void conf_group(struct screen_ctx *);
void xev_process(void); void xev_process(void);
int xu_getprop(Window, Atom, Atom, long, unsigned char **); int xu_get_prop(Window, Atom, Atom, long, unsigned char **);
int xu_getstrprop(Window, Atom, char **); int xu_get_strprop(Window, Atom, char **);
void xu_ptr_getpos(Window, int *, int *); void xu_ptr_get(Window, int *, int *);
void xu_ptr_setpos(Window, int, int); void xu_ptr_set(Window, int, int);
void xu_get_wm_state(Window, long *);
void xu_set_wm_state(Window, long);
void xu_send_clientmsg(Window, Atom, Time);
void xu_xorcolor(XftColor, XftColor, XftColor *); void xu_xorcolor(XftColor, XftColor, XftColor *);
void xu_atom_init(void);
void xu_ewmh_net_supported(struct screen_ctx *); void xu_ewmh_net_supported(struct screen_ctx *);
void xu_ewmh_net_supported_wm_check(struct screen_ctx *); void xu_ewmh_net_supported_wm_check(struct screen_ctx *);
void xu_ewmh_net_desktop_geometry(struct screen_ctx *); void xu_ewmh_net_desktop_geometry(struct screen_ctx *);
void xu_ewmh_net_desktop_viewport(struct screen_ctx *);
void xu_ewmh_net_workarea(struct screen_ctx *); void xu_ewmh_net_workarea(struct screen_ctx *);
void xu_ewmh_net_client_list(struct screen_ctx *); void xu_ewmh_net_client_list(struct screen_ctx *);
void xu_ewmh_net_client_list_stacking(struct screen_ctx *); void xu_ewmh_net_client_list_stacking(struct screen_ctx *);
void xu_ewmh_net_active_window(struct screen_ctx *, Window); void xu_ewmh_net_active_window(struct screen_ctx *, Window);
Window xu_ewmh_get_net_active_window(struct screen_ctx *); void xu_ewmh_net_number_of_desktops(struct screen_ctx *);
void xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *);
void xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *);
void xu_ewmh_net_showing_desktop(struct screen_ctx *); void xu_ewmh_net_showing_desktop(struct screen_ctx *);
void xu_ewmh_net_virtual_roots(struct screen_ctx *); void xu_ewmh_net_virtual_roots(struct screen_ctx *);
void xu_ewmh_net_current_desktop(struct screen_ctx *); void xu_ewmh_net_current_desktop(struct screen_ctx *);
void xu_ewmh_net_desktop_names(struct screen_ctx *); void xu_ewmh_net_desktop_names(struct screen_ctx *);
int xu_ewmh_get_net_wm_desktop(struct client_ctx *, long *);
void xu_ewmh_net_wm_desktop(struct client_ctx *); void xu_ewmh_set_net_wm_desktop(struct client_ctx *);
Atom *xu_ewmh_get_net_wm_state(struct client_ctx *, int *); Atom *xu_ewmh_get_net_wm_state(struct client_ctx *, int *);
void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *, void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *,
int, Atom , Atom); int, Atom, Atom);
void xu_ewmh_set_net_wm_state(struct client_ctx *); void xu_ewmh_set_net_wm_state(struct client_ctx *);
void xu_ewmh_restore_net_wm_state(struct client_ctx *); void xu_ewmh_restore_net_wm_state(struct client_ctx *);
@ -620,5 +626,7 @@ char *xstrdup(const char *);
int xasprintf(char **, const char *, ...) int xasprintf(char **, const char *, ...)
__attribute__((__format__ (printf, 2, 3))) __attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2))); __attribute__((__nonnull__ (2)));
int xvasprintf(char **, const char *, va_list)
__attribute__((__nonnull__ (2)));
#endif /* _CALMWM_H_ */ #endif /* _CALMWM_H_ */

529
client.c
View File

@ -31,90 +31,87 @@
#include "calmwm.h" #include "calmwm.h"
static struct client_ctx *client_next(struct client_ctx *); static void client_class_hint(struct client_ctx *);
static struct client_ctx *client_prev(struct client_ctx *); static void client_placement(struct client_ctx *);
static void client_placecalc(struct client_ctx *);
static void client_wm_protocols(struct client_ctx *);
static void client_mwm_hints(struct client_ctx *); static void client_mwm_hints(struct client_ctx *);
static void client_wm_protocols(struct client_ctx *);
struct client_ctx * struct client_ctx *
client_init(Window win, struct screen_ctx *sc, int active) client_init(Window win, struct screen_ctx *sc)
{ {
struct client_ctx *cc; struct client_ctx *cc;
XWindowAttributes wattr; XWindowAttributes wattr;
int mapped; int mapped;
Window rwin, cwin; long state;
int x, y, wx, wy;
unsigned int mask;
if (win == None) if (win == None)
return(NULL); return NULL;
if (!XGetWindowAttributes(X_Dpy, win, &wattr)) if (!XGetWindowAttributes(X_Dpy, win, &wattr))
return(NULL); return NULL;
if (sc == NULL) { if (sc == NULL) {
if ((sc = screen_find(wattr.root)) == NULL) if ((sc = screen_find(wattr.root)) == NULL)
return(NULL); return NULL;
mapped = 1; mapped = 1;
} else { } else {
if (wattr.override_redirect || wattr.map_state != IsViewable) if (wattr.override_redirect || wattr.map_state != IsViewable)
return(NULL); return NULL;
mapped = wattr.map_state != IsUnmapped; mapped = wattr.map_state != IsUnmapped;
} }
cc = xmalloc(sizeof(*cc));
XGrabServer(X_Dpy); XGrabServer(X_Dpy);
cc = xmalloc(sizeof(*cc));
cc->sc = sc; cc->sc = sc;
cc->win = win; cc->win = win;
cc->name = NULL;
cc->label = NULL; cc->label = NULL;
cc->gc = NULL; cc->gc = NULL;
cc->res_class = NULL;
cc->res_name = NULL;
cc->flags = 0; cc->flags = 0;
cc->stackingorder = 0; cc->stackingorder = 0;
cc->initial_state = 0;
memset(&cc->hint, 0, sizeof(cc->hint)); memset(&cc->hint, 0, sizeof(cc->hint));
memset(&cc->ch, 0, sizeof(cc->ch));
TAILQ_INIT(&cc->nameq); TAILQ_INIT(&cc->nameq);
client_setname(cc);
conf_client(cc);
XGetClassHint(X_Dpy, cc->win, &cc->ch);
client_wm_hints(cc);
client_wm_protocols(cc);
client_getsizehints(cc);
client_mwm_hints(cc);
cc->geom.x = wattr.x; cc->geom.x = wattr.x;
cc->geom.y = wattr.y; cc->geom.y = wattr.y;
cc->geom.w = wattr.width; cc->geom.w = wattr.width;
cc->geom.h = wattr.height; cc->geom.h = wattr.height;
cc->colormap = wattr.colormap;
cc->obwidth = wattr.border_width;
cc->bwidth = Conf.bwidth;
client_set_name(cc);
conf_client(cc);
client_wm_hints(cc);
client_class_hint(cc);
client_wm_protocols(cc);
client_get_sizehints(cc);
client_transient(cc);
client_mwm_hints(cc);
if ((cc->flags & CLIENT_IGNORE))
cc->bwidth = 0;
cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw; cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch; cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
cc->ptr.x = cc->geom.w / 2; cc->ptr.x = cc->geom.w / 2;
cc->ptr.y = cc->geom.h / 2; cc->ptr.y = cc->geom.h / 2;
cc->colormap = wattr.colormap;
if (wattr.map_state != IsViewable) { if (wattr.map_state != IsViewable) {
client_placecalc(cc); client_placement(cc);
client_resize(cc, 0); client_resize(cc, 0);
if ((cc->wmh) && (cc->wmh->flags & StateHint)) if (cc->initial_state)
client_set_wm_state(cc, cc->wmh->initial_state); xu_set_wm_state(cc->win, cc->initial_state);
} else {
if ((active == 0) && (XQueryPointer(X_Dpy, cc->win, &rwin,
&cwin, &x, &y, &wx, &wy, &mask)) && (cwin != None))
active = 1;
} }
XSelectInput(X_Dpy, cc->win, ColormapChangeMask | EnterWindowMask | XSelectInput(X_Dpy, cc->win,
PropertyChangeMask | KeyReleaseMask); EnterWindowMask | PropertyChangeMask | KeyReleaseMask);
XAddToSaveSet(X_Dpy, cc->win); XAddToSaveSet(X_Dpy, cc->win);
client_transient(cc);
/* Notify client of its configuration. */ /* Notify client of its configuration. */
client_config(cc); client_config(cc);
@ -124,12 +121,17 @@ client_init(Window win, struct screen_ctx *sc, int active)
xu_ewmh_net_client_list_stacking(sc); xu_ewmh_net_client_list_stacking(sc);
xu_ewmh_restore_net_wm_state(cc); xu_ewmh_restore_net_wm_state(cc);
if (client_get_wm_state(cc) == IconicState) xu_get_wm_state(cc->win, &state);
if (state == IconicState)
client_hide(cc); client_hide(cc);
else else
client_unhide(cc); client_show(cc);
if (mapped) { if (mapped) {
if (cc->gc) {
group_movetogroup(cc, cc->gc->num);
goto out;
}
if (group_restore(cc)) if (group_restore(cc))
goto out; goto out;
if (group_autogroup(cc)) if (group_autogroup(cc))
@ -143,10 +145,29 @@ out:
XSync(X_Dpy, False); XSync(X_Dpy, False);
XUngrabServer(X_Dpy); XUngrabServer(X_Dpy);
if (active) return cc;
client_setactive(cc); }
return(cc); struct client_ctx *
client_current(struct screen_ctx *sc)
{
struct screen_ctx *_sc;
struct client_ctx *cc;
if (sc) {
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->flags & CLIENT_ACTIVE)
return cc;
}
} else {
TAILQ_FOREACH(_sc, &Screenq, entry) {
TAILQ_FOREACH(cc, &_sc->clientq, entry) {
if (cc->flags & CLIENT_ACTIVE)
return cc;
}
}
}
return NULL;
} }
struct client_ctx * struct client_ctx *
@ -158,14 +179,34 @@ client_find(Window win)
TAILQ_FOREACH(sc, &Screenq, entry) { TAILQ_FOREACH(sc, &Screenq, entry) {
TAILQ_FOREACH(cc, &sc->clientq, entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->win == win) if (cc->win == win)
return(cc); return cc;
} }
} }
return(NULL); return NULL;
}
struct client_ctx *
client_next(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct client_ctx *newcc;
return(((newcc = TAILQ_NEXT(cc, entry)) != NULL) ?
newcc : TAILQ_FIRST(&sc->clientq));
}
struct client_ctx *
client_prev(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct client_ctx *newcc;
return(((newcc = TAILQ_PREV(cc, client_q, entry)) != NULL) ?
newcc : TAILQ_LAST(&sc->clientq, client_q));
} }
void void
client_delete(struct client_ctx *cc) client_remove(struct client_ctx *cc)
{ {
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
struct winname *wn; struct winname *wn;
@ -178,27 +219,21 @@ client_delete(struct client_ctx *cc)
if (cc->flags & CLIENT_ACTIVE) if (cc->flags & CLIENT_ACTIVE)
xu_ewmh_net_active_window(sc, None); xu_ewmh_net_active_window(sc, None);
if (cc->gc != NULL)
TAILQ_REMOVE(&cc->gc->clientq, cc, group_entry);
while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) { while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) {
TAILQ_REMOVE(&cc->nameq, wn, entry); TAILQ_REMOVE(&cc->nameq, wn, entry);
free(wn->name); free(wn->name);
free(wn); free(wn);
} }
if (cc->ch.res_class) free(cc->name);
XFree(cc->ch.res_class); free(cc->label);
if (cc->ch.res_name) free(cc->res_class);
XFree(cc->ch.res_name); free(cc->res_name);
if (cc->wmh)
XFree(cc->wmh);
free(cc); free(cc);
} }
void void
client_setactive(struct client_ctx *cc) client_set_active(struct client_ctx *cc)
{ {
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
struct client_ctx *oldcc; struct client_ctx *oldcc;
@ -214,9 +249,9 @@ client_setactive(struct client_ctx *cc)
RevertToPointerRoot, CurrentTime); RevertToPointerRoot, CurrentTime);
} }
if (cc->flags & CLIENT_WM_TAKE_FOCUS) if (cc->flags & CLIENT_WM_TAKE_FOCUS)
client_msg(cc, cwmh[WM_TAKE_FOCUS], Last_Event_Time); xu_send_clientmsg(cc->win, cwmh[WM_TAKE_FOCUS], Last_Event_Time);
if ((oldcc = client_current()) != NULL) { if ((oldcc = client_current(sc)) != NULL) {
oldcc->flags &= ~CLIENT_ACTIVE; oldcc->flags &= ~CLIENT_ACTIVE;
client_draw_border(oldcc); client_draw_border(oldcc);
} }
@ -232,21 +267,6 @@ client_setactive(struct client_ctx *cc)
xu_ewmh_net_active_window(sc, cc->win); xu_ewmh_net_active_window(sc, cc->win);
} }
struct client_ctx *
client_current(void)
{
struct screen_ctx *sc;
struct client_ctx *cc;
TAILQ_FOREACH(sc, &Screenq, entry) {
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->flags & CLIENT_ACTIVE)
return(cc);
}
}
return(NULL);
}
void void
client_toggle_freeze(struct client_ctx *cc) client_toggle_freeze(struct client_ctx *cc)
{ {
@ -307,7 +327,7 @@ client_toggle_fullscreen(struct client_ctx *cc)
area = screen_area(sc, area = screen_area(sc,
cc->geom.x + cc->geom.w / 2, cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_NOGAP); cc->geom.y + cc->geom.h / 2, 0);
cc->bwidth = 0; cc->bwidth = 0;
cc->geom = area; cc->geom = area;
@ -316,6 +336,7 @@ client_toggle_fullscreen(struct client_ctx *cc)
resize: resize:
client_resize(cc, 0); client_resize(cc, 0);
xu_ewmh_set_net_wm_state(cc); xu_ewmh_set_net_wm_state(cc);
client_ptr_inbound(cc, 1);
} }
void void
@ -345,7 +366,7 @@ client_toggle_maximize(struct client_ctx *cc)
area = screen_area(sc, area = screen_area(sc,
cc->geom.x + cc->geom.w / 2, cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.y + cc->geom.h / 2, 1);
cc->geom.x = area.x; cc->geom.x = area.x;
cc->geom.y = area.y; cc->geom.y = area.y;
@ -356,6 +377,7 @@ client_toggle_maximize(struct client_ctx *cc)
resize: resize:
client_resize(cc, 0); client_resize(cc, 0);
xu_ewmh_set_net_wm_state(cc); xu_ewmh_set_net_wm_state(cc);
client_ptr_inbound(cc, 1);
} }
void void
@ -379,7 +401,7 @@ client_toggle_vmaximize(struct client_ctx *cc)
area = screen_area(sc, area = screen_area(sc,
cc->geom.x + cc->geom.w / 2, cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.y + cc->geom.h / 2, 1);
cc->geom.y = area.y; cc->geom.y = area.y;
cc->geom.h = area.h - (cc->bwidth * 2); cc->geom.h = area.h - (cc->bwidth * 2);
@ -388,6 +410,7 @@ client_toggle_vmaximize(struct client_ctx *cc)
resize: resize:
client_resize(cc, 0); client_resize(cc, 0);
xu_ewmh_set_net_wm_state(cc); xu_ewmh_set_net_wm_state(cc);
client_ptr_inbound(cc, 1);
} }
void void
@ -411,7 +434,7 @@ client_toggle_hmaximize(struct client_ctx *cc)
area = screen_area(sc, area = screen_area(sc,
cc->geom.x + cc->geom.w / 2, cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.y + cc->geom.h / 2, 1);
cc->geom.x = area.x; cc->geom.x = area.x;
cc->geom.w = area.w - (cc->bwidth * 2); cc->geom.w = area.w - (cc->bwidth * 2);
@ -420,6 +443,7 @@ client_toggle_hmaximize(struct client_ctx *cc)
resize: resize:
client_resize(cc, 0); client_resize(cc, 0);
xu_ewmh_set_net_wm_state(cc); xu_ewmh_set_net_wm_state(cc);
client_ptr_inbound(cc, 1);
} }
void void
@ -482,7 +506,7 @@ void
client_ptr_inbound(struct client_ctx *cc, int getpos) client_ptr_inbound(struct client_ctx *cc, int getpos)
{ {
if (getpos) if (getpos)
xu_ptr_getpos(cc->win, &cc->ptr.x, &cc->ptr.y); xu_ptr_get(cc->win, &cc->ptr.x, &cc->ptr.y);
if (cc->ptr.x < 0) if (cc->ptr.x < 0)
cc->ptr.x = 0; cc->ptr.x = 0;
@ -493,21 +517,21 @@ client_ptr_inbound(struct client_ctx *cc, int getpos)
else if (cc->ptr.y > cc->geom.h - 1) else if (cc->ptr.y > cc->geom.h - 1)
cc->ptr.y = cc->geom.h - 1; cc->ptr.y = cc->geom.h - 1;
client_ptrwarp(cc); client_ptr_warp(cc);
} }
void void
client_ptrwarp(struct client_ctx *cc) client_ptr_warp(struct client_ctx *cc)
{ {
xu_ptr_setpos(cc->win, cc->ptr.x, cc->ptr.y); xu_ptr_set(cc->win, cc->ptr.x, cc->ptr.y);
} }
void void
client_ptrsave(struct client_ctx *cc) client_ptr_save(struct client_ctx *cc)
{ {
int x, y; int x, y;
xu_ptr_getpos(cc->win, &x, &y); xu_ptr_get(cc->win, &x, &y);
if (client_inbound(cc, x, y)) { if (client_inbound(cc, x, y)) {
cc->ptr.x = x; cc->ptr.x = x;
cc->ptr.y = y; cc->ptr.y = y;
@ -522,30 +546,21 @@ client_hide(struct client_ctx *cc)
{ {
XUnmapWindow(X_Dpy, cc->win); XUnmapWindow(X_Dpy, cc->win);
if (cc->flags & CLIENT_ACTIVE) if (cc->flags & CLIENT_ACTIVE) {
cc->flags &= ~CLIENT_ACTIVE;
xu_ewmh_net_active_window(cc->sc, None); xu_ewmh_net_active_window(cc->sc, None);
}
cc->flags &= ~CLIENT_ACTIVE;
cc->flags |= CLIENT_HIDDEN; cc->flags |= CLIENT_HIDDEN;
client_set_wm_state(cc, IconicState); xu_set_wm_state(cc->win, IconicState);
} }
void void
client_show(struct client_ctx *cc) client_show(struct client_ctx *cc)
{
if (cc->flags & CLIENT_HIDDEN)
client_unhide(cc);
else
client_raise(cc);
}
void
client_unhide(struct client_ctx *cc)
{ {
XMapRaised(X_Dpy, cc->win); XMapRaised(X_Dpy, cc->win);
cc->flags &= ~CLIENT_HIDDEN; cc->flags &= ~CLIENT_HIDDEN;
client_set_wm_state(cc, NormalState); xu_set_wm_state(cc->win, NormalState);
client_draw_border(cc); client_draw_border(cc);
} }
@ -581,7 +596,24 @@ client_draw_border(struct client_ctx *cc)
pixel = sc->xftcolor[CWM_COLOR_BORDER_URGENCY].pixel; pixel = sc->xftcolor[CWM_COLOR_BORDER_URGENCY].pixel;
XSetWindowBorderWidth(X_Dpy, cc->win, (unsigned int)cc->bwidth); XSetWindowBorderWidth(X_Dpy, cc->win, (unsigned int)cc->bwidth);
XSetWindowBorder(X_Dpy, cc->win, pixel); XSetWindowBorder(X_Dpy, cc->win, pixel | (0xffu << 24));
}
static void
client_class_hint(struct client_ctx *cc)
{
XClassHint ch;
if (XGetClassHint(X_Dpy, cc->win, &ch)) {
if (ch.res_class) {
cc->res_class = xstrdup(ch.res_class);
XFree(ch.res_class);
}
if (ch.res_name) {
cc->res_name = xstrdup(ch.res_name);
XFree(ch.res_name);
}
}
} }
static void static void
@ -604,152 +636,64 @@ client_wm_protocols(struct client_ctx *cc)
void void
client_wm_hints(struct client_ctx *cc) client_wm_hints(struct client_ctx *cc)
{ {
if ((cc->wmh = XGetWMHints(X_Dpy, cc->win)) == NULL) XWMHints *wmh;
return;
if ((cc->wmh->flags & InputHint) && (cc->wmh->input)) if ((wmh = XGetWMHints(X_Dpy, cc->win)) != NULL) {
cc->flags |= CLIENT_INPUT; if ((wmh->flags & InputHint) && (wmh->input))
cc->flags |= CLIENT_INPUT;
if ((cc->wmh->flags & XUrgencyHint)) if ((wmh->flags & XUrgencyHint))
client_urgency(cc); client_urgency(cc);
if ((wmh->flags & StateHint))
cc->initial_state = wmh->initial_state;
XFree(wmh);
}
} }
void void
client_msg(struct client_ctx *cc, Atom proto, Time ts) client_close(struct client_ctx *cc)
{
XClientMessageEvent cm;
(void)memset(&cm, 0, sizeof(cm));
cm.type = ClientMessage;
cm.window = cc->win;
cm.message_type = cwmh[WM_PROTOCOLS];
cm.format = 32;
cm.data.l[0] = proto;
cm.data.l[1] = ts;
XSendEvent(X_Dpy, cc->win, False, NoEventMask, (XEvent *)&cm);
}
void
client_send_delete(struct client_ctx *cc)
{ {
if (cc->flags & CLIENT_WM_DELETE_WINDOW) if (cc->flags & CLIENT_WM_DELETE_WINDOW)
client_msg(cc, cwmh[WM_DELETE_WINDOW], CurrentTime); xu_send_clientmsg(cc->win, cwmh[WM_DELETE_WINDOW], CurrentTime);
else else
XKillClient(X_Dpy, cc->win); XKillClient(X_Dpy, cc->win);
} }
void void
client_setname(struct client_ctx *cc) client_set_name(struct client_ctx *cc)
{ {
struct winname *wn; struct winname *wn, *wnnxt;
char *newname;
int i = 0; int i = 0;
if (!xu_getstrprop(cc->win, ewmh[_NET_WM_NAME], &newname)) free(cc->name);
if (!xu_getstrprop(cc->win, XA_WM_NAME, &newname)) if (!xu_get_strprop(cc->win, ewmh[_NET_WM_NAME], &cc->name))
newname = xstrdup(""); if (!xu_get_strprop(cc->win, XA_WM_NAME, &cc->name))
cc->name = xstrdup("");
TAILQ_FOREACH(wn, &cc->nameq, entry) { TAILQ_FOREACH_SAFE(wn, &cc->nameq, entry, wnnxt) {
if (strcmp(wn->name, newname) == 0) { if (strcmp(wn->name, cc->name) == 0) {
/* Move to the last since we got a hit. */
TAILQ_REMOVE(&cc->nameq, wn, entry); TAILQ_REMOVE(&cc->nameq, wn, entry);
TAILQ_INSERT_TAIL(&cc->nameq, wn, entry); free(wn->name);
goto match; free(wn);
} }
i++;
} }
wn = xmalloc(sizeof(*wn)); wn = xmalloc(sizeof(*wn));
wn->name = newname; wn->name = xstrdup(cc->name);
TAILQ_INSERT_TAIL(&cc->nameq, wn, entry); TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
match:
cc->name = wn->name;
/* Do some garbage collection. */ /* Garbage collection. */
TAILQ_FOREACH(wn, &cc->nameq, entry) if ((i + 1) > Conf.nameqlen) {
i++;
if (i > Conf.nameqlen) {
wn = TAILQ_FIRST(&cc->nameq); wn = TAILQ_FIRST(&cc->nameq);
TAILQ_REMOVE(&cc->nameq, wn, entry); TAILQ_REMOVE(&cc->nameq, wn, entry);
free(wn->name); free(wn->name);
free(wn); free(wn);
i--;
} }
} }
void
client_cycle(struct screen_ctx *sc, int flags)
{
struct client_ctx *newcc, *oldcc, *prevcc;
int again = 1;
if (TAILQ_EMPTY(&sc->clientq))
return;
prevcc = TAILQ_FIRST(&sc->clientq);
oldcc = client_current();
if (oldcc == NULL)
oldcc = (flags & CWM_CYCLE_REVERSE) ?
TAILQ_LAST(&sc->clientq, client_q) :
TAILQ_FIRST(&sc->clientq);
newcc = oldcc;
while (again) {
again = 0;
newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) :
client_next(newcc);
/* Only cycle visible and non-ignored windows. */
if ((newcc->flags & (CLIENT_SKIP_CYCLE)) ||
((flags & CWM_CYCLE_INGROUP) &&
(newcc->gc != oldcc->gc)))
again = 1;
/* Is oldcc the only non-hidden window? */
if (newcc == oldcc) {
if (again)
return; /* No windows visible. */
break;
}
}
/* Reset when cycling mod is released. XXX I hate this hack */
sc->cycling = 1;
client_ptrsave(oldcc);
client_raise(prevcc);
client_raise(newcc);
if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) {
newcc->ptr.x = newcc->geom.w / 2;
newcc->ptr.y = newcc->geom.h / 2;
}
client_ptrwarp(newcc);
}
static struct client_ctx *
client_next(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct client_ctx *newcc;
return(((newcc = TAILQ_NEXT(cc, entry)) != NULL) ?
newcc : TAILQ_FIRST(&sc->clientq));
}
static struct client_ctx *
client_prev(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct client_ctx *newcc;
return(((newcc = TAILQ_PREV(cc, client_q, entry)) != NULL) ?
newcc : TAILQ_LAST(&sc->clientq, client_q));
}
static void static void
client_placecalc(struct client_ctx *cc) client_placement(struct client_ctx *cc)
{ {
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
int xslack, yslack;
if (cc->hint.flags & (USPosition | PPosition)) { if (cc->hint.flags & (USPosition | PPosition)) {
if (cc->geom.x >= sc->view.w) if (cc->geom.x >= sc->view.w)
@ -760,34 +704,36 @@ client_placecalc(struct client_ctx *cc)
cc->geom.x = sc->view.h - cc->bwidth - 1; cc->geom.x = sc->view.h - cc->bwidth - 1;
if (cc->geom.y + cc->geom.h + cc->bwidth <= 0) if (cc->geom.y + cc->geom.h + cc->bwidth <= 0)
cc->geom.y = -(cc->geom.h + cc->bwidth - 1); cc->geom.y = -(cc->geom.h + cc->bwidth - 1);
if (cc->flags & CLIENT_IGNORE) {
if (((cc->obwidth * 2) + cc->geom.x + cc->geom.w) == sc->view.w)
cc->geom.x += cc->obwidth * 2;
if (((cc->obwidth * 2) + cc->geom.y + cc->geom.h) == sc->view.h)
cc->geom.y += cc->obwidth * 2;
}
} else { } else {
struct geom area; struct geom area;
int xmouse, ymouse; int xmouse, ymouse, xslack, yslack;
xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse); xu_ptr_get(sc->rootwin, &xmouse, &ymouse);
area = screen_area(sc, xmouse, ymouse, CWM_GAP); area = screen_area(sc, xmouse, ymouse, 1);
area.w += area.x;
area.h += area.y;
xmouse = MAX(xmouse, area.x) - cc->geom.w / 2;
ymouse = MAX(ymouse, area.y) - cc->geom.h / 2;
xmouse = MAX(xmouse, area.x); xmouse = MAX(MAX(xmouse, area.x) - cc->geom.w / 2, area.x);
ymouse = MAX(ymouse, area.y); ymouse = MAX(MAX(ymouse, area.y) - cc->geom.h / 2, area.y);
xslack = area.w - cc->geom.w - cc->bwidth * 2; xslack = area.x + area.w - cc->geom.w - cc->bwidth * 2;
yslack = area.h - cc->geom.h - cc->bwidth * 2; yslack = area.y + area.h - cc->geom.h - cc->bwidth * 2;
if (xslack >= area.x) { if (xslack >= area.x) {
cc->geom.x = MAX(MIN(xmouse, xslack), area.x); cc->geom.x = MAX(MIN(xmouse, xslack), area.x);
} else { } else {
cc->geom.x = area.x; cc->geom.x = area.x;
cc->geom.w = area.w; cc->geom.w = area.x + area.w;
} }
if (yslack >= area.y) { if (yslack >= area.y) {
cc->geom.y = MAX(MIN(ymouse, yslack), area.y); cc->geom.y = MAX(MIN(ymouse, yslack), area.y);
} else { } else {
cc->geom.y = area.y; cc->geom.y = area.y;
cc->geom.h = area.h; cc->geom.h = area.y + area.h;
} }
} }
} }
@ -802,7 +748,7 @@ client_mtf(struct client_ctx *cc)
} }
void void
client_getsizehints(struct client_ctx *cc) client_get_sizehints(struct client_ctx *cc)
{ {
long tmp; long tmp;
XSizeHints size; XSizeHints size;
@ -850,7 +796,7 @@ client_getsizehints(struct client_ctx *cc)
} }
void void
client_applysizehints(struct client_ctx *cc) client_apply_sizehints(struct client_ctx *cc)
{ {
Bool baseismin; Bool baseismin;
@ -901,15 +847,17 @@ client_mwm_hints(struct client_ctx *cc)
{ {
struct mwm_hints *mwmh; struct mwm_hints *mwmh;
if (xu_getprop(cc->win, cwmh[_MOTIF_WM_HINTS], if (xu_get_prop(cc->win, cwmh[_MOTIF_WM_HINTS],
cwmh[_MOTIF_WM_HINTS], MWM_HINTS_ELEMENTS, cwmh[_MOTIF_WM_HINTS], MWM_HINTS_ELEMENTS,
(unsigned char **)&mwmh) == MWM_HINTS_ELEMENTS) { (unsigned char **)&mwmh) <= 0)
if (mwmh->flags & MWM_FLAGS_DECORATIONS && return;
!(mwmh->decorations & MWM_DECOR_ALL) &&
!(mwmh->decorations & MWM_DECOR_BORDER)) if ((mwmh->flags & MWM_HINTS_DECORATIONS) &&
!(mwmh->decorations & MWM_DECOR_ALL)) {
if (!(mwmh->decorations & MWM_DECOR_BORDER))
cc->bwidth = 0; cc->bwidth = 0;
XFree(mwmh);
} }
XFree(mwmh);
} }
void void
@ -919,10 +867,11 @@ client_transient(struct client_ctx *cc)
Window trans; Window trans;
if (XGetTransientForHint(X_Dpy, cc->win, &trans)) { if (XGetTransientForHint(X_Dpy, cc->win, &trans)) {
if ((tc = client_find(trans)) != NULL && tc->gc) { if ((tc = client_find(trans)) != NULL) {
group_movetogroup(cc, tc->gc->num); if (tc->flags & CLIENT_IGNORE) {
if (tc->flags & CLIENT_IGNORE)
cc->flags |= CLIENT_IGNORE; cc->flags |= CLIENT_IGNORE;
cc->bwidth = tc->bwidth;
}
} }
} }
} }
@ -950,43 +899,45 @@ client_snapcalc(int n0, int n1, int e0, int e1, int snapdist)
/* possible to snap in both directions */ /* possible to snap in both directions */
if (s0 != 0 && s1 != 0) if (s0 != 0 && s1 != 0)
if (abs(s0) < abs(s1)) if (abs(s0) < abs(s1))
return(s0); return s0;
else else
return(s1); return s1;
else if (s0 != 0) else if (s0 != 0)
return(s0); return s0;
else if (s1 != 0) else if (s1 != 0)
return(s1); return s1;
else else
return(0); return 0;
} }
void void
client_htile(struct client_ctx *cc) client_htile(struct client_ctx *cc)
{ {
struct client_ctx *ci; struct client_ctx *ci;
struct group_ctx *gc = cc->gc;
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
struct geom area; struct geom area;
int i, n, mh, x, w, h; int i, n, mh, x, w, h;
if (!gc)
return;
i = n = 0; i = n = 0;
area = screen_area(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, 1);
TAILQ_FOREACH(ci, &gc->clientq, group_entry) { TAILQ_FOREACH(ci, &sc->clientq, entry) {
if (ci->gc != cc->gc)
continue;
if (ci->flags & CLIENT_HIDDEN || if (ci->flags & CLIENT_HIDDEN ||
ci->flags & CLIENT_IGNORE || (ci == cc)) ci->flags & CLIENT_IGNORE || (ci == cc) ||
ci->geom.x < area.x ||
ci->geom.x > (area.x + area.w) ||
ci->geom.y < area.y ||
ci->geom.y > (area.y + area.h))
continue; continue;
n++; n++;
} }
if (n == 0) if (n == 0)
return; return;
area = screen_area(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP);
if (cc->flags & CLIENT_VMAXIMIZED || if (cc->flags & CLIENT_VMAXIMIZED ||
cc->geom.h + (cc->bwidth * 2) >= area.h) cc->geom.h + (cc->bwidth * 2) >= area.h)
return; return;
@ -995,17 +946,24 @@ client_htile(struct client_ctx *cc)
cc->geom.x = area.x; cc->geom.x = area.x;
cc->geom.y = area.y; cc->geom.y = area.y;
cc->geom.w = area.w - (cc->bwidth * 2); cc->geom.w = area.w - (cc->bwidth * 2);
cc->geom.h = (area.h - (cc->bwidth * 2)) / 2; if (Conf.htile > 0)
cc->geom.h = ((area.h - (cc->bwidth * 2)) * Conf.htile) / 100;
client_resize(cc, 1); client_resize(cc, 1);
client_ptrwarp(cc); client_ptr_warp(cc);
mh = cc->geom.h + (cc->bwidth * 2); mh = cc->geom.h + (cc->bwidth * 2);
x = area.x; x = area.x;
w = area.w / n; w = area.w / n;
h = area.h - mh; h = area.h - mh;
TAILQ_FOREACH(ci, &gc->clientq, group_entry) { TAILQ_FOREACH(ci, &sc->clientq, entry) {
if (ci->gc != cc->gc)
continue;
if (ci->flags & CLIENT_HIDDEN || if (ci->flags & CLIENT_HIDDEN ||
ci->flags & CLIENT_IGNORE || (ci == cc)) ci->flags & CLIENT_IGNORE || (ci == cc) ||
ci->geom.x < area.x ||
ci->geom.x > (area.x + area.w) ||
ci->geom.y < area.y ||
ci->geom.y > (area.y + area.h))
continue; continue;
ci->bwidth = Conf.bwidth; ci->bwidth = Conf.bwidth;
ci->geom.x = x; ci->geom.x = x;
@ -1025,28 +983,30 @@ void
client_vtile(struct client_ctx *cc) client_vtile(struct client_ctx *cc)
{ {
struct client_ctx *ci; struct client_ctx *ci;
struct group_ctx *gc = cc->gc;
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
struct geom area; struct geom area;
int i, n, mw, y, w, h; int i, n, mw, y, w, h;
if (!gc)
return;
i = n = 0; i = n = 0;
area = screen_area(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, 1);
TAILQ_FOREACH(ci, &gc->clientq, group_entry) { TAILQ_FOREACH(ci, &sc->clientq, entry) {
if (ci->gc != cc->gc)
continue;
if (ci->flags & CLIENT_HIDDEN || if (ci->flags & CLIENT_HIDDEN ||
ci->flags & CLIENT_IGNORE || (ci == cc)) ci->flags & CLIENT_IGNORE || (ci == cc) ||
ci->geom.x < area.x ||
ci->geom.x > (area.x + area.w) ||
ci->geom.y < area.y ||
ci->geom.y > (area.y + area.h))
continue; continue;
n++; n++;
} }
if (n == 0) if (n == 0)
return; return;
area = screen_area(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP);
if (cc->flags & CLIENT_HMAXIMIZED || if (cc->flags & CLIENT_HMAXIMIZED ||
cc->geom.w + (cc->bwidth * 2) >= area.w) cc->geom.w + (cc->bwidth * 2) >= area.w)
return; return;
@ -1054,18 +1014,25 @@ client_vtile(struct client_ctx *cc)
cc->flags &= ~CLIENT_VMAXIMIZED; cc->flags &= ~CLIENT_VMAXIMIZED;
cc->geom.x = area.x; cc->geom.x = area.x;
cc->geom.y = area.y; cc->geom.y = area.y;
cc->geom.w = (area.w - (cc->bwidth * 2)) / 2; if (Conf.vtile > 0)
cc->geom.w = ((area.w - (cc->bwidth * 2)) * Conf.vtile) / 100;
cc->geom.h = area.h - (cc->bwidth * 2); cc->geom.h = area.h - (cc->bwidth * 2);
client_resize(cc, 1); client_resize(cc, 1);
client_ptrwarp(cc); client_ptr_warp(cc);
mw = cc->geom.w + (cc->bwidth * 2); mw = cc->geom.w + (cc->bwidth * 2);
y = area.y; y = area.y;
h = area.h / n; h = area.h / n;
w = area.w - mw; w = area.w - mw;
TAILQ_FOREACH(ci, &gc->clientq, group_entry) { TAILQ_FOREACH(ci, &sc->clientq, entry) {
if (ci->gc != cc->gc)
continue;
if (ci->flags & CLIENT_HIDDEN || if (ci->flags & CLIENT_HIDDEN ||
ci->flags & CLIENT_IGNORE || (ci == cc)) ci->flags & CLIENT_IGNORE || (ci == cc) ||
ci->geom.x < area.x ||
ci->geom.x > (area.x + area.w) ||
ci->geom.y < area.y ||
ci->geom.y > (area.y + area.h))
continue; continue;
ci->bwidth = Conf.bwidth; ci->bwidth = Conf.bwidth;
ci->geom.x = area.x + mw; ci->geom.x = area.x + mw;
@ -1080,25 +1047,3 @@ client_vtile(struct client_ctx *cc)
client_resize(ci, 1); client_resize(ci, 1);
} }
} }
long
client_get_wm_state(struct client_ctx *cc)
{
long *p, state = -1;
if (xu_getprop(cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
(unsigned char **)&p) > 0) {
state = *p;
XFree(p);
}
return(state);
}
void
client_set_wm_state(struct client_ctx *cc, long state)
{
long data[] = { state, None };
XChangeProperty(X_Dpy, cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
PropModeReplace, (unsigned char *)data, 2);
}

149
conf.c
View File

@ -20,7 +20,6 @@
#include <sys/types.h> #include <sys/types.h>
#include "queue.h" #include "queue.h"
#include <sys/stat.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
@ -33,10 +32,25 @@
#include "calmwm.h" #include "calmwm.h"
static const char *conf_bind_getmask(const char *, unsigned int *); static const char *conf_bind_mask(const char *, unsigned int *);
static void conf_unbind_key(struct conf *, struct bind_ctx *); static void conf_unbind_key(struct conf *, struct bind_ctx *);
static void conf_unbind_mouse(struct conf *, struct bind_ctx *); static void conf_unbind_mouse(struct conf *, struct bind_ctx *);
static const struct {
int num;
const char *name;
} group_binds[] = {
{ 0, "nogroup" },
{ 1, "one" },
{ 2, "two" },
{ 3, "three" },
{ 4, "four" },
{ 5, "five" },
{ 6, "six" },
{ 7, "seven" },
{ 8, "eight" },
{ 9, "nine" },
};
static int cursor_binds[] = { static int cursor_binds[] = {
XC_left_ptr, /* CF_NORMAL */ XC_left_ptr, /* CF_NORMAL */
XC_fleur, /* CF_MOVE */ XC_fleur, /* CF_MOVE */
@ -68,7 +82,8 @@ static const struct {
{ FUNC_CC(window-lower, client_lower, 0) }, { FUNC_CC(window-lower, client_lower, 0) },
{ FUNC_CC(window-raise, client_raise, 0) }, { FUNC_CC(window-raise, client_raise, 0) },
{ FUNC_CC(window-hide, client_hide, 0) }, { FUNC_CC(window-hide, client_hide, 0) },
{ FUNC_CC(window-delete, client_delete, 0) }, { FUNC_CC(window-close, client_close, 0) },
{ FUNC_CC(window-delete, client_close, 0) },
{ FUNC_CC(window-htile, client_htile, 0) }, { FUNC_CC(window-htile, client_htile, 0) },
{ FUNC_CC(window-vtile, client_vtile, 0) }, { FUNC_CC(window-vtile, client_vtile, 0) },
{ FUNC_CC(window-stick, client_toggle_sticky, 0) }, { FUNC_CC(window-stick, client_toggle_sticky, 0) },
@ -124,7 +139,8 @@ static const struct {
{ FUNC_SC(group-cycle, group_cycle, (CWM_CYCLE_FORWARD)) }, { FUNC_SC(group-cycle, group_cycle, (CWM_CYCLE_FORWARD)) },
{ FUNC_SC(group-rcycle, group_cycle, (CWM_CYCLE_REVERSE)) }, { FUNC_SC(group-rcycle, group_cycle, (CWM_CYCLE_REVERSE)) },
{ FUNC_SC(group-toggle-all, group_alltoggle, 0) }, { FUNC_SC(group-last, group_last, 0) },
{ FUNC_SC(group-toggle-all, group_toggle_all, 0) },
{ FUNC_SC(group-toggle-1, group_toggle, 1) }, { FUNC_SC(group-toggle-1, group_toggle, 1) },
{ FUNC_SC(group-toggle-2, group_toggle, 2) }, { FUNC_SC(group-toggle-2, group_toggle, 2) },
{ FUNC_SC(group-toggle-3, group_toggle, 3) }, { FUNC_SC(group-toggle-3, group_toggle, 3) },
@ -143,6 +159,15 @@ static const struct {
{ FUNC_SC(group-only-7, group_only, 7) }, { FUNC_SC(group-only-7, group_only, 7) },
{ FUNC_SC(group-only-8, group_only, 8) }, { FUNC_SC(group-only-8, group_only, 8) },
{ FUNC_SC(group-only-9, group_only, 9) }, { FUNC_SC(group-only-9, group_only, 9) },
{ FUNC_SC(group-close-1, group_close, 1) },
{ FUNC_SC(group-close-2, group_close, 2) },
{ FUNC_SC(group-close-3, group_close, 3) },
{ FUNC_SC(group-close-4, group_close, 4) },
{ FUNC_SC(group-close-5, group_close, 5) },
{ FUNC_SC(group-close-6, group_close, 6) },
{ FUNC_SC(group-close-7, group_close, 7) },
{ FUNC_SC(group-close-8, group_close, 8) },
{ FUNC_SC(group-close-9, group_close, 9) },
{ FUNC_SC(pointer-move-up, ptrmove, (CWM_UP)) }, { FUNC_SC(pointer-move-up, ptrmove, (CWM_UP)) },
{ FUNC_SC(pointer-move-down, ptrmove, (CWM_DOWN)) }, { FUNC_SC(pointer-move-down, ptrmove, (CWM_DOWN)) },
@ -173,10 +198,11 @@ static const struct {
const char ch; const char ch;
int mask; int mask;
} bind_mods[] = { } bind_mods[] = {
{ 'S', ShiftMask },
{ 'C', ControlMask }, { 'C', ControlMask },
{ 'M', Mod1Mask }, { 'M', Mod1Mask },
{ '4', Mod4Mask }, { '4', Mod4Mask },
{ 'S', ShiftMask }, { '5', Mod5Mask },
}; };
static const struct { static const struct {
const char *key; const char *key;
@ -195,7 +221,7 @@ static const struct {
{ "M-Tab", "window-cycle" }, { "M-Tab", "window-cycle" },
{ "MS-Tab", "window-rcycle" }, { "MS-Tab", "window-rcycle" },
{ "CM-n", "window-menu-label" }, { "CM-n", "window-menu-label" },
{ "CM-x", "window-delete" }, { "CM-x", "window-close" },
{ "CM-a", "group-toggle-all" }, { "CM-a", "group-toggle-all" },
{ "CM-0", "group-toggle-all" }, { "CM-0", "group-toggle-all" },
{ "CM-1", "group-toggle-1" }, { "CM-1", "group-toggle-1" },
@ -256,8 +282,10 @@ conf_init(struct conf *c)
c->stickygroups = 0; c->stickygroups = 0;
c->bwidth = 1; c->bwidth = 1;
c->mamount = 1; c->mamount = 1;
c->htile = 50;
c->vtile = 50;
c->snapdist = 0; c->snapdist = 0;
c->ngroups = 10; c->ngroups = 0;
c->nameqlen = 5; c->nameqlen = 5;
TAILQ_INIT(&c->ignoreq); TAILQ_INIT(&c->ignoreq);
@ -434,16 +462,13 @@ void
conf_client(struct client_ctx *cc) conf_client(struct client_ctx *cc)
{ {
struct winname *wn; struct winname *wn;
int ignore = 0;
TAILQ_FOREACH(wn, &Conf.ignoreq, entry) { TAILQ_FOREACH(wn, &Conf.ignoreq, entry) {
if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) { if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) {
ignore = 1; cc->flags |= CLIENT_IGNORE;
break; break;
} }
} }
cc->bwidth = (ignore) ? 0 : Conf.bwidth;
cc->flags |= (ignore) ? CLIENT_IGNORE : 0;
} }
void void
@ -472,32 +497,30 @@ conf_screen(struct screen_ctx *sc)
warnx("XftColorAllocValue: %s", Conf.color[i]); warnx("XftColorAllocValue: %s", Conf.color[i]);
break; break;
} }
if (XftColorAllocName(X_Dpy, sc->visual, sc->colormap, if (!XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
Conf.color[i], &xc)) { Conf.color[i], &sc->xftcolor[i])) {
sc->xftcolor[i] = xc;
XftColorFree(X_Dpy, sc->visual, sc->colormap, &xc);
} else {
warnx("XftColorAllocName: %s", Conf.color[i]); warnx("XftColorAllocName: %s", Conf.color[i]);
XftColorAllocName(X_Dpy, sc->visual, sc->colormap, XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
color_binds[i], &sc->xftcolor[i]); color_binds[i], &sc->xftcolor[i]);
} }
} }
sc->menu.win = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
Conf.bwidth,
sc->xftcolor[CWM_COLOR_MENU_FG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
sc->menu.xftdraw = XftDrawCreate(X_Dpy, sc->menu.win,
sc->visual, sc->colormap);
if (sc->menu.xftdraw == NULL)
errx(1, "%s: XftDrawCreate", __func__);
conf_grab_kbd(sc->rootwin); conf_grab_kbd(sc->rootwin);
} }
void
conf_group(struct screen_ctx *sc)
{
unsigned int i;
for (i = 0; i < nitems(group_binds); i++) {
group_init(sc, group_binds[i].num, group_binds[i].name);
Conf.ngroups++;
}
}
static const char * static const char *
conf_bind_getmask(const char *name, unsigned int *mask) conf_bind_mask(const char *name, unsigned int *mask)
{ {
char *dash; char *dash;
const char *ch; const char *ch;
@ -505,13 +528,13 @@ conf_bind_getmask(const char *name, unsigned int *mask)
*mask = 0; *mask = 0;
if ((dash = strchr(name, '-')) == NULL) if ((dash = strchr(name, '-')) == NULL)
return(name); return name;
for (i = 0; i < nitems(bind_mods); i++) { for (i = 0; i < nitems(bind_mods); i++) {
if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash) if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash)
*mask |= bind_mods[i].mask; *mask |= bind_mods[i].mask;
} }
/* Skip past modifiers. */ /* Skip past modifiers. */
return(dash + 1); return (dash + 1);
} }
int int
@ -524,20 +547,20 @@ conf_bind_key(struct conf *c, const char *bind, const char *cmd)
if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
conf_unbind_key(c, NULL); conf_unbind_key(c, NULL);
return(1); return 1;
} }
kb = xmalloc(sizeof(*kb)); kb = xmalloc(sizeof(*kb));
key = conf_bind_getmask(bind, &kb->modmask); key = conf_bind_mask(bind, &kb->modmask);
kb->press.keysym = XStringToKeysym(key); kb->press.keysym = XStringToKeysym(key);
if (kb->press.keysym == NoSymbol) { if (kb->press.keysym == NoSymbol) {
warnx("unknown symbol: %s", key); warnx("unknown symbol: %s", key);
free(kb); free(kb);
return(0); return 0;
} }
conf_unbind_key(c, kb); conf_unbind_key(c, kb);
if (cmd == NULL) { if (cmd == NULL) {
free(kb); free(kb);
return(1); return 1;
} }
cargs = xcalloc(1, sizeof(*cargs)); cargs = xcalloc(1, sizeof(*cargs));
for (i = 0; i < nitems(name_to_func); i++) { for (i = 0; i < nitems(name_to_func); i++) {
@ -555,7 +578,7 @@ conf_bind_key(struct conf *c, const char *bind, const char *cmd)
out: out:
kb->cargs = cargs; kb->cargs = cargs;
TAILQ_INSERT_TAIL(&c->keybindq, kb, entry); TAILQ_INSERT_TAIL(&c->keybindq, kb, entry);
return(1); return 1;
} }
static void static void
@ -585,20 +608,20 @@ conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
conf_unbind_mouse(c, NULL); conf_unbind_mouse(c, NULL);
return(1); return 1;
} }
mb = xmalloc(sizeof(*mb)); mb = xmalloc(sizeof(*mb));
button = conf_bind_getmask(bind, &mb->modmask); button = conf_bind_mask(bind, &mb->modmask);
mb->press.button = strtonum(button, Button1, Button5, &errstr); mb->press.button = strtonum(button, Button1, Button5, &errstr);
if (errstr) { if (errstr) {
warnx("button number is %s: %s", errstr, button); warnx("button number is %s: %s", errstr, button);
free(mb); free(mb);
return(0); return 0;
} }
conf_unbind_mouse(c, mb); conf_unbind_mouse(c, mb);
if (cmd == NULL) { if (cmd == NULL) {
free(mb); free(mb);
return(1); return 1;
} }
cargs = xcalloc(1, sizeof(*cargs)); cargs = xcalloc(1, sizeof(*cargs));
for (i = 0; i < nitems(name_to_func); i++) { for (i = 0; i < nitems(name_to_func); i++) {
@ -616,7 +639,7 @@ conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
out: out:
mb->cargs = cargs; mb->cargs = cargs;
TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry); TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry);
return(1); return 1;
} }
static void static void
@ -625,7 +648,7 @@ conf_unbind_mouse(struct conf *c, struct bind_ctx *unbind)
struct bind_ctx *mb = NULL, *mbnxt; struct bind_ctx *mb = NULL, *mbnxt;
TAILQ_FOREACH_SAFE(mb, &c->mousebindq, entry, mbnxt) { TAILQ_FOREACH_SAFE(mb, &c->mousebindq, entry, mbnxt) {
if ((unbind == NULL) || if ((unbind == NULL) ||
((mb->modmask == unbind->modmask) && ((mb->modmask == unbind->modmask) &&
(mb->press.button == unbind->press.button))) { (mb->press.button == unbind->press.button))) {
TAILQ_REMOVE(&c->mousebindq, mb, entry); TAILQ_REMOVE(&c->mousebindq, mb, entry);
@ -647,6 +670,8 @@ conf_grab_kbd(Window win)
TAILQ_FOREACH(kb, &Conf.keybindq, entry) { TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
kc = XKeysymToKeycode(X_Dpy, kb->press.keysym); kc = XKeysymToKeycode(X_Dpy, kb->press.keysym);
if (kc == 0)
continue;
if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) && if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) &&
(XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym)) (XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym))
kb->modmask |= ShiftMask; kb->modmask |= ShiftMask;
@ -676,47 +701,3 @@ conf_grab_mouse(Window win)
} }
} }
} }
static char *cwmhints[] = {
"WM_STATE",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
"WM_PROTOCOLS",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"WM_CHANGE_STATE",
};
static char *ewmhints[] = {
"_NET_SUPPORTED",
"_NET_SUPPORTING_WM_CHECK",
"_NET_ACTIVE_WINDOW",
"_NET_CLIENT_LIST",
"_NET_CLIENT_LIST_STACKING",
"_NET_NUMBER_OF_DESKTOPS",
"_NET_CURRENT_DESKTOP",
"_NET_DESKTOP_VIEWPORT",
"_NET_DESKTOP_GEOMETRY",
"_NET_VIRTUAL_ROOTS",
"_NET_SHOWING_DESKTOP",
"_NET_DESKTOP_NAMES",
"_NET_WORKAREA",
"_NET_WM_NAME",
"_NET_WM_DESKTOP",
"_NET_CLOSE_WINDOW",
"_NET_WM_STATE",
"_NET_WM_STATE_STICKY",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_HIDDEN",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_DEMANDS_ATTENTION",
"_NET_WM_STATE_SKIP_PAGER",
"_NET_WM_STATE_SKIP_TASKBAR",
"_CWM_WM_STATE_FREEZE",
};
void
conf_atoms(void)
{
XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
}

15
cwm.1
View File

@ -23,7 +23,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.\" For a program: program [-abc] file ... .\" For a program: program [-abc] file ...
.Nm cwm .Nm cwm
.Op Fl v .Op Fl nv
.Op Fl c Ar file .Op Fl c Ar file
.Op Fl d Ar display .Op Fl d Ar display
.Sh DESCRIPTION .Sh DESCRIPTION
@ -48,6 +48,9 @@ however,
will continue to process the rest of the configuration file. will continue to process the rest of the configuration file.
.It Fl d Ar display .It Fl d Ar display
Specify the display to use. Specify the display to use.
.It Fl n
Configtest mode.
Only check the configuration file for validity.
.It Fl v .It Fl v
Verbose mode. Verbose mode.
Multiple Multiple
@ -100,7 +103,7 @@ Cycle through currently visible windows.
.It Ic MS-Tab .It Ic MS-Tab
Reverse cycle through currently visible windows. Reverse cycle through currently visible windows.
.It Ic CM-x .It Ic CM-x
Delete current window. Close current window.
.It Ic CM-[n] .It Ic CM-[n]
Toggle visibility of group n, where n is 1-9. Toggle visibility of group n, where n is 1-9.
.It Ic CM-a .It Ic CM-a
@ -216,6 +219,11 @@ Window is currently focused.
.It & .It &
Window is hidden. Window is hidden.
.El .El
.Sh APPLICATIONS
.Nm
manages a list of applications defined with the
.Cm command
configuration option.
.Sh GROUPS .Sh GROUPS
.Nm .Nm
has the ability to group windows together, and use the groups to has the ability to group windows together, and use the groups to
@ -239,8 +247,7 @@ Selecting an item will warp to that window, unhiding it if necessary.
Show list of currently defined groups. Show list of currently defined groups.
Selecting an item will hide/unhide that group. Selecting an item will hide/unhide that group.
.It Ic M3 .It Ic M3
Show list of applications as defined in Show list of applications as defined in the configuration file.
.Pa ~/.cwmrc .
Selecting an item will spawn that application. Selecting an item will spawn that application.
.El .El
.Sh ENVIRONMENT .Sh ENVIRONMENT

77
cwmrc.5
View File

@ -37,10 +37,8 @@ Arguments containing whitespace should be surrounded by double quotes
.Pq \&" . .Pq \&" .
.Pp .Pp
The following options are accepted: The following options are accepted:
.Pp .Bl -tag -width Ds
.Bl -tag -width Ds -compact .It Ic autogroup Ar group Oo Ar windowname , Oc Ns Ar windowclass
.It Ic autogroup Ar group windowclass
.It Ic autogroup Ar group windowname,windowclass
Automatically add new windows to Automatically add new windows to
.Ar group .Ar group
if their class property matches if their class property matches
@ -62,7 +60,6 @@ used to override
The name and class values, respectively, for existing windows The name and class values, respectively, for existing windows
are both set in the WM_CLASS property and may be obtained using are both set in the WM_CLASS property and may be obtained using
.Xr xprop 1 . .Xr xprop 1 .
.Pp
.It Ic bind-key Ar key function .It Ic bind-key Ar key function
Bind or rebind key Bind or rebind key
.Ar key .Ar key
@ -84,6 +81,8 @@ Meta key.
Shift key. Shift key.
.It Ic 4 .It Ic 4
Mod4 (windows) key. Mod4 (windows) key.
.It Ic 5
Mod5 (AltGr) key.
.El .El
.Pp .Pp
The The
@ -91,7 +90,6 @@ The
may either be one from the may either be one from the
.Sx BIND FUNCTION LIST .Sx BIND FUNCTION LIST
(see below) or the command line that is to be executed. (see below) or the command line that is to be executed.
.Pp
.It Ic bind-mouse Ar button function .It Ic bind-mouse Ar button function
Bind or rebind button Bind or rebind button
.Ar button .Ar button
@ -101,18 +99,10 @@ The modifier keys come first, followed by a
.Sq - , .Sq - ,
then the button number. then the button number.
.Pp .Pp
The following modifiers are recognised: The same modifiers are recognised as for
.Pp .Ar key
.Bl -tag -width Ds -offset indent -compact in
.It Ic C .Nm bind-key .
Control key.
.It Ic M
Meta key.
.It Ic S
Shift key.
.It Ic 4
Mod4 (windows) key.
.El
.Pp .Pp
The following buttons are recognised: The following buttons are recognised:
.Pp .Pp
@ -134,38 +124,27 @@ The
may be taken from the may be taken from the
.Sx BIND FUNCTION LIST .Sx BIND FUNCTION LIST
(see below) or the command line that is to be executed. (see below) or the command line that is to be executed.
.Pp
.It Ic borderwidth Ar pixels .It Ic borderwidth Ar pixels
Set the window border width to Set the window border width to
.Ar pixels . .Ar pixels .
.Pp
.It Ic color activeborder Ar color .It Ic color activeborder Ar color
Set the color of the active border. Set the color of the active border.
.Pp
.It Ic color font Ar color .It Ic color font Ar color
Set menu font color. Set menu font color.
.Pp
.It Ic color selfont Ar color .It Ic color selfont Ar color
Set font color for selected menu item. Set font color for selected menu item.
.Pp
.It Ic color groupborder Ar color .It Ic color groupborder Ar color
Set the color of the border while grouping a window. Set the color of the border while grouping a window.
.Pp
.It Ic color inactiveborder Ar color .It Ic color inactiveborder Ar color
Set the color of the inactive border. Set the color of the inactive border.
.Pp
.It Ic color menubg Ar color .It Ic color menubg Ar color
Set menu background color. Set menu background color.
.Pp
.It Ic color menufg Ar color .It Ic color menufg Ar color
Set menu foreground color. Set menu foreground color.
.Pp
.It Ic color urgencyborder Ar color .It Ic color urgencyborder Ar color
Set the color of the border of a window indicating urgency. Set the color of the border of a window indicating urgency.
.Pp
.It Ic color ungroupborder Ar color .It Ic color ungroupborder Ar color
Set the color of the border while ungrouping a window. Set the color of the border while ungrouping a window.
.Pp
.It Ic command Ar name path .It Ic command Ar name path
Every Every
.Ar name .Ar name
@ -173,7 +152,7 @@ entry is shown in the application menu.
When selected, the defined When selected, the defined
.Ar path .Ar path
is executed with is executed with
.Xr execve 2 . .Xr execvp 3 .
.Pp .Pp
The The
.Ar name .Ar name
@ -189,13 +168,11 @@ The defaults are
and and
.Xr xlock 1 , .Xr xlock 1 ,
respectively. respectively.
.Pp
.It Ic fontname Ar font .It Ic fontname Ar font
Change the default Change the default
.Ar font .Ar font
for for
.Xr Xft 3 . .Xr Xft 3 .
.Pp
.It Ic gap Ar top bottom left right .It Ic gap Ar top bottom left right
Define a Define a
.Dq gap .Dq gap
@ -206,28 +183,30 @@ This
can be used for applications such as can be used for applications such as
.Xr xclock 1 , .Xr xclock 1 ,
where the user may wish to remain visible. where the user may wish to remain visible.
.Pp .It Ic htile Ar percent
Set the percentage of screen the master window should occupy
after calling
.Ic window-htile .
If set to 0, the horizontal size of the master window will
remain unchanged.
The default is 50.
.It Ic ignore Ar windowname .It Ic ignore Ar windowname
Ignore, and do not warp to, windows with the name Ignore, and do not warp to, windows with the name
.Ar windowname .Ar windowname
when drawing borders and cycling through windows. when drawing borders and cycling through windows.
.Pp
.It Ic moveamount Ar pixels .It Ic moveamount Ar pixels
Set a default size for the keyboard movement bindings, Set a default size for the keyboard movement bindings,
in pixels. in pixels.
The default is 1. The default is 1.
.Pp
.It Ic snapdist Ar pixels .It Ic snapdist Ar pixels
Minimum distance to snap-to adjacent edge, in pixels. Minimum distance to snap-to adjacent edge, in pixels.
The default is 0. The default is 0.
.Pp
.It Ic sticky Ic yes Ns \&| Ns Ic no .It Ic sticky Ic yes Ns \&| Ns Ic no
Toggle sticky group mode. Toggle sticky group mode.
The default behavior for new windows is to not assign any group. The default behavior for new windows is to not assign any group.
By enabling sticky group mode, By enabling sticky group mode,
.Xr cwm 1 .Xr cwm 1
will assign new windows to the currently selected group. will assign new windows to the currently selected group.
.Pp
.It Ic unbind-key Ar key .It Ic unbind-key Ar key
Unbind function bound to Unbind function bound to
.Ar key . .Ar key .
@ -236,7 +215,6 @@ A special
keyword keyword
.Dq all .Dq all
can be used to unbind all keys. can be used to unbind all keys.
.Pp
.It Ic unbind-mouse Ar button .It Ic unbind-mouse Ar button
Unbind function bound to Unbind function bound to
.Ar button . .Ar button .
@ -245,6 +223,13 @@ A special
keyword keyword
.Dq all .Dq all
can be used to unbind all buttons. can be used to unbind all buttons.
.It Ic vtile Ar percent
Set the percentage of screen the master window should occupy
after calling
.Ic window-vtile .
If set to 0, the vertical size of the master window will
remain unchanged.
The default is 50.
.It Ic wm Ar name path .It Ic wm Ar name path
Every Every
.Ar name .Ar name
@ -288,6 +273,10 @@ menu.
Toggle visibility of group n, where n is 1-9. Toggle visibility of group n, where n is 1-9.
.It group-only-[n] .It group-only-[n]
Show only group n, where n is 1-9, hiding other groups. Show only group n, where n is 1-9, hiding other groups.
.It group-last
Show only the previously active group.
.It group-close-[n]
Close all windows in group n, where n is 1-9.
.It group-toggle-all .It group-toggle-all
Toggle visibility of all groups. Toggle visibility of all groups.
.It window-group .It window-group
@ -306,8 +295,8 @@ Reverse cycle through windows.
Forward cycle through windows in current group. Forward cycle through windows in current group.
.It window-rcycle-ingroup .It window-rcycle-ingroup
Reverse cycle through windows in current group. Reverse cycle through windows in current group.
.It window-delete .It window-close
Delete current window. Close current window.
.It window-hide .It window-hide
Hide current window. Hide current window.
.It window-lower .It window-lower
@ -330,11 +319,15 @@ Vertically maximize current window (gap + border honored).
Horizontally maximize current window (gap + border honored). Horizontally maximize current window (gap + border honored).
.It window-htile .It window-htile
Current window is placed at the top of the screen, maximized Current window is placed at the top of the screen, maximized
horizontally and resized to half of the vertical screen space. horizontally and resized to
.Ar htile
(default half) of the vertical screen space.
Other windows in its group share remaining screen space. Other windows in its group share remaining screen space.
.It window-vtile .It window-vtile
Current window is placed on the left of the screen, maximized vertically Current window is placed on the left of the screen, maximized vertically
and resized to half of the horizontal screen space. and resized to
.Ar vtile
(default half) of the horizontal screen space.
Other windows in its group share remaining screen space. Other windows in its group share remaining screen space.
.It window-move .It window-move
Move current window. Move current window.

210
group.c
View File

@ -35,39 +35,32 @@
static struct group_ctx *group_next(struct group_ctx *); static struct group_ctx *group_next(struct group_ctx *);
static struct group_ctx *group_prev(struct group_ctx *); static struct group_ctx *group_prev(struct group_ctx *);
static void group_restack(struct group_ctx *); static void group_restack(struct group_ctx *);
static void group_setactive(struct group_ctx *); static void group_set_active(struct group_ctx *);
const char *num_to_name[] = {
"nogroup", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine"
};
void void
group_assign(struct group_ctx *gc, struct client_ctx *cc) group_assign(struct group_ctx *gc, struct client_ctx *cc)
{ {
if (cc->gc != NULL)
TAILQ_REMOVE(&cc->gc->clientq, cc, group_entry);
if ((gc != NULL) && (gc->num == 0)) if ((gc != NULL) && (gc->num == 0))
gc = NULL; gc = NULL;
cc->gc = gc; cc->gc = gc;
if (cc->gc != NULL) xu_ewmh_set_net_wm_desktop(cc);
TAILQ_INSERT_TAIL(&gc->clientq, cc, group_entry);
xu_ewmh_net_wm_desktop(cc);
} }
void void
group_hide(struct group_ctx *gc) group_hide(struct group_ctx *gc)
{ {
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc; struct client_ctx *cc;
screen_updatestackingorder(gc->sc); screen_updatestackingorder(gc->sc);
TAILQ_FOREACH(cc, &gc->clientq, group_entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (!(cc->flags & CLIENT_STICKY)) if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY) &&
!(cc->flags & CLIENT_HIDDEN))
client_hide(cc); client_hide(cc);
} }
} }
@ -75,33 +68,41 @@ group_hide(struct group_ctx *gc)
void void
group_show(struct group_ctx *gc) group_show(struct group_ctx *gc)
{ {
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc; struct client_ctx *cc;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (!(cc->flags & CLIENT_STICKY)) if (cc->gc != gc)
client_unhide(cc); continue;
if (!(cc->flags & CLIENT_STICKY) &&
(cc->flags & CLIENT_HIDDEN))
client_show(cc);
} }
group_restack(gc); group_restack(gc);
group_setactive(gc); group_set_active(gc);
} }
static void static void
group_restack(struct group_ctx *gc) group_restack(struct group_ctx *gc)
{ {
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc; struct client_ctx *cc;
Window *winlist; Window *winlist;
int i, lastempty = -1; int i, lastempty = -1;
int nwins = 0, highstack = 0; int nwins = 0, highstack = 0;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (cc->stackingorder > highstack) if (cc->stackingorder > highstack)
highstack = cc->stackingorder; highstack = cc->stackingorder;
} }
winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist)); winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist));
/* Invert the stacking order for XRestackWindows(). */ /* Invert the stacking order for XRestackWindows(). */
TAILQ_FOREACH(cc, &gc->clientq, group_entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
winlist[highstack - cc->stackingorder] = cc->win; winlist[highstack - cc->stackingorder] = cc->win;
nwins++; nwins++;
} }
@ -122,24 +123,22 @@ group_restack(struct group_ctx *gc)
} }
void void
group_init(struct screen_ctx *sc, int num) group_init(struct screen_ctx *sc, int num, const char *name)
{ {
struct group_ctx *gc; struct group_ctx *gc;
gc = xmalloc(sizeof(*gc)); gc = xmalloc(sizeof(*gc));
gc->sc = sc; gc->sc = sc;
gc->name = xstrdup(num_to_name[num]); gc->name = xstrdup(name);
gc->num = num; gc->num = num;
TAILQ_INIT(&gc->clientq);
TAILQ_INSERT_TAIL(&sc->groupq, gc, entry); TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
if (num == 1) if (num == 1)
group_setactive(gc); group_set_active(gc);
} }
void void
group_setactive(struct group_ctx *gc) group_set_active(struct group_ctx *gc)
{ {
struct screen_ctx *sc = gc->sc; struct screen_ctx *sc = gc->sc;
@ -154,19 +153,15 @@ group_movetogroup(struct client_ctx *cc, int idx)
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
struct group_ctx *gc; struct group_ctx *gc;
if (idx < 0 || idx >= Conf.ngroups)
return;
TAILQ_FOREACH(gc, &sc->groupq, entry) { TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) if (gc->num == idx) {
break; if (cc->gc == gc)
return;
if (gc->num != 0 && group_holds_only_hidden(gc))
client_hide(cc);
group_assign(gc, cc);
}
} }
if (cc->gc == gc)
return;
if (gc->num != 0 && group_holds_only_hidden(gc))
client_hide(cc);
group_assign(gc, cc);
} }
void void
@ -175,62 +170,44 @@ group_toggle_membership(struct client_ctx *cc)
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
struct group_ctx *gc = sc->group_active; struct group_ctx *gc = sc->group_active;
if (gc == cc->gc) { if (cc->gc == gc) {
group_assign(NULL, cc); group_assign(NULL, cc);
cc->flags |= CLIENT_UNGROUP; cc->flags |= CLIENT_UNGROUP;
} else { } else {
group_assign(gc, cc); group_assign(gc, cc);
cc->flags |= CLIENT_GROUP; cc->flags |= CLIENT_GROUP;
} }
client_draw_border(cc); client_draw_border(cc);
} }
int int
group_holds_only_sticky(struct group_ctx *gc) group_holds_only_sticky(struct group_ctx *gc)
{ {
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc; struct client_ctx *cc;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY)) if (!(cc->flags & CLIENT_STICKY))
return(0); return 0;
} }
return(1); return 1;
} }
int int
group_holds_only_hidden(struct group_ctx *gc) group_holds_only_hidden(struct group_ctx *gc)
{ {
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc; struct client_ctx *cc;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY))) if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY)))
return(0); return 0;
}
return(1);
}
void
group_hidetoggle(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
if (idx < 0 || idx >= Conf.ngroups)
return;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
break;
}
if (group_holds_only_hidden(gc))
group_show(gc);
else {
group_hide(gc);
/* make clients stick to empty group */
if (TAILQ_EMPTY(&gc->clientq))
group_setactive(gc);
} }
return 1;
} }
void void
@ -238,8 +215,8 @@ group_only(struct screen_ctx *sc, int idx)
{ {
struct group_ctx *gc; struct group_ctx *gc;
if (idx < 0 || idx >= Conf.ngroups) if (sc->group_last != sc->group_active)
return; sc->group_last = sc->group_active;
TAILQ_FOREACH(gc, &sc->groupq, entry) { TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) if (gc->num == idx)
@ -249,6 +226,52 @@ group_only(struct screen_ctx *sc, int idx)
} }
} }
void
group_toggle(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
if (group_holds_only_hidden(gc))
group_show(gc);
else
group_hide(gc);
}
}
}
void
group_toggle_all(struct screen_ctx *sc)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->hideall)
group_show(gc);
else
group_hide(gc);
}
sc->hideall = !sc->hideall;
}
void
group_close(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
struct client_ctx *cc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
client_close(cc);
}
}
}
}
void void
group_cycle(struct screen_ctx *sc, int flags) group_cycle(struct screen_ctx *sc, int flags)
{ {
@ -269,7 +292,6 @@ group_cycle(struct screen_ctx *sc, int flags)
else if (!group_holds_only_hidden(newgc)) else if (!group_holds_only_hidden(newgc))
group_hide(newgc); group_hide(newgc);
} }
if (showgroup == NULL) if (showgroup == NULL)
return; return;
@ -278,7 +300,7 @@ group_cycle(struct screen_ctx *sc, int flags)
if (group_holds_only_hidden(showgroup)) if (group_holds_only_hidden(showgroup))
group_show(showgroup); group_show(showgroup);
else else
group_setactive(showgroup); group_set_active(showgroup);
} }
static struct group_ctx * static struct group_ctx *
@ -301,43 +323,27 @@ group_prev(struct group_ctx *gc)
newgc : TAILQ_LAST(&sc->groupq, group_q)); newgc : TAILQ_LAST(&sc->groupq, group_q));
} }
void
group_alltoggle(struct screen_ctx *sc)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->hideall)
group_show(gc);
else
group_hide(gc);
}
sc->hideall = !sc->hideall;
}
int int
group_restore(struct client_ctx *cc) group_restore(struct client_ctx *cc)
{ {
struct screen_ctx *sc = cc->sc; struct screen_ctx *sc = cc->sc;
struct group_ctx *gc; struct group_ctx *gc;
int num; int num;
long *grpnum; long grpnum;
if (xu_getprop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L, if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum))
(unsigned char **)&grpnum) <= 0) return 0;
return(0);
num = (*grpnum == -1) ? 0 : *grpnum; num = (grpnum == -1) ? 0 : grpnum;
num = MIN(num, (Conf.ngroups - 1)); num = MIN(num, (Conf.ngroups - 1));
XFree(grpnum);
TAILQ_FOREACH(gc, &sc->groupq, entry) { TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == num) { if (gc->num == num) {
group_assign(gc, cc); group_assign(gc, cc);
return(1); return 1;
} }
} }
return(0); return 0;
} }
int int
@ -348,13 +354,13 @@ group_autogroup(struct client_ctx *cc)
struct group_ctx *gc; struct group_ctx *gc;
int num = -1, both_match = 0; int num = -1, both_match = 0;
if (cc->ch.res_class == NULL || cc->ch.res_name == NULL) if (cc->res_class == NULL || cc->res_name == NULL)
return(0); return 0;
TAILQ_FOREACH(ag, &Conf.autogroupq, entry) { TAILQ_FOREACH(ag, &Conf.autogroupq, entry) {
if (strcmp(ag->class, cc->ch.res_class) == 0) { if (strcmp(ag->class, cc->res_class) == 0) {
if ((ag->name != NULL) && if ((ag->name != NULL) &&
(strcmp(ag->name, cc->ch.res_name) == 0)) { (strcmp(ag->name, cc->res_name) == 0)) {
num = ag->num; num = ag->num;
both_match = 1; both_match = 1;
} else if (ag->name == NULL && !both_match) } else if (ag->name == NULL && !both_match)
@ -365,8 +371,8 @@ group_autogroup(struct client_ctx *cc)
TAILQ_FOREACH(gc, &sc->groupq, entry) { TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == num) { if (gc->num == num) {
group_assign(gc, cc); group_assign(gc, cc);
return(1); return 1;
} }
} }
return(0); return 0;
} }

155
kbfunc.c
View File

@ -23,6 +23,7 @@
#include <sys/types.h> #include <sys/types.h>
#include "queue.h" #include "queue.h"
#include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#include <err.h> #include <err.h>
@ -61,7 +62,7 @@ kbfunc_amount(int flags, int amt, int *mx, int *my)
if (flags & CWM_BIGAMOUNT) if (flags & CWM_BIGAMOUNT)
amt *= CWM_FACTOR; amt *= CWM_FACTOR;
switch (flags & DIRECTIONMASK) { switch (flags & (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)) {
case CWM_UP: case CWM_UP:
*my -= amt; *my -= amt;
break; break;
@ -86,8 +87,8 @@ kbfunc_ptrmove(void *ctx, struct cargs *cargs)
kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my); kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my);
xu_ptr_getpos(sc->rootwin, &x, &y); xu_ptr_get(sc->rootwin, &x, &y);
xu_ptr_setpos(sc->rootwin, x + mx, y + my); xu_ptr_set(sc->rootwin, x + mx, y + my);
} }
void void
@ -134,7 +135,7 @@ kbfunc_client_move_kb(void *ctx, struct cargs *cargs)
area = screen_area(sc, area = screen_area(sc,
cc->geom.x + cc->geom.w / 2, cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.y + cc->geom.h / 2, 1);
cc->geom.x += client_snapcalc(cc->geom.x, cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.x + cc->geom.w + (cc->bwidth * 2), cc->geom.x + cc->geom.w + (cc->bwidth * 2),
area.x, area.x + area.w, sc->snapdist); area.x, area.x + area.w, sc->snapdist);
@ -144,6 +145,7 @@ kbfunc_client_move_kb(void *ctx, struct cargs *cargs)
client_move(cc); client_move(cc);
client_ptr_inbound(cc, 1); client_ptr_inbound(cc, 1);
XSync(X_Dpy, True);
} }
static void static void
@ -168,8 +170,8 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
CurrentTime) != GrabSuccess) CurrentTime) != GrabSuccess)
return; return;
menu_windraw(sc, cc->win, "%4d, %-4d", cc->geom.x, cc->geom.y); screen_prop_win_create(sc, cc->win);
screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y);
while (move) { while (move) {
XMaskEvent(X_Dpy, MOUSEMASK, &ev); XMaskEvent(X_Dpy, MOUSEMASK, &ev);
switch (ev.type) { switch (ev.type) {
@ -184,7 +186,7 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
area = screen_area(sc, area = screen_area(sc,
cc->geom.x + cc->geom.w / 2, cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.y + cc->geom.h / 2, 1);
cc->geom.x += client_snapcalc(cc->geom.x, cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.x + cc->geom.w + (cc->bwidth * 2), cc->geom.x + cc->geom.w + (cc->bwidth * 2),
area.x, area.x + area.w, sc->snapdist); area.x, area.x + area.w, sc->snapdist);
@ -192,8 +194,8 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
cc->geom.y + cc->geom.h + (cc->bwidth * 2), cc->geom.y + cc->geom.h + (cc->bwidth * 2),
area.y, area.y + area.h, sc->snapdist); area.y, area.y + area.h, sc->snapdist);
client_move(cc); client_move(cc);
menu_windraw(sc, cc->win, screen_prop_win_draw(sc,
"%4d, %-4d", cc->geom.x, cc->geom.y); "%+5d%+5d", cc->geom.x, cc->geom.y);
break; break;
case ButtonRelease: case ButtonRelease:
move = 0; move = 0;
@ -202,8 +204,7 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
} }
if (ltime) if (ltime)
client_move(cc); client_move(cc);
XUnmapWindow(X_Dpy, sc->menu.win); screen_prop_win_destroy(sc);
XReparentWindow(X_Dpy, sc->menu.win, sc->rootwin, 0, 0);
XUngrabPointer(X_Dpy, CurrentTime); XUngrabPointer(X_Dpy, CurrentTime);
} }
@ -233,6 +234,7 @@ kbfunc_client_resize_kb(void *ctx, struct cargs *cargs)
client_resize(cc, 1); client_resize(cc, 1);
client_ptr_inbound(cc, 1); client_ptr_inbound(cc, 1);
XSync(X_Dpy, True);
} }
static void static void
@ -248,16 +250,17 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
return; return;
client_raise(cc); client_raise(cc);
client_ptrsave(cc); client_ptr_save(cc);
xu_ptr_setpos(cc->win, cc->geom.w, cc->geom.h); xu_ptr_set(cc->win, cc->geom.w, cc->geom.h);
if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK, if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK,
GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE], GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE],
CurrentTime) != GrabSuccess) CurrentTime) != GrabSuccess)
return; return;
menu_windraw(sc, cc->win, "%4d x %-4d", cc->dim.w, cc->dim.h); screen_prop_win_create(sc, cc->win);
screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h);
while (resize) { while (resize) {
XMaskEvent(X_Dpy, MOUSEMASK, &ev); XMaskEvent(X_Dpy, MOUSEMASK, &ev);
switch (ev.type) { switch (ev.type) {
@ -269,9 +272,9 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
cc->geom.w = ev.xmotion.x; cc->geom.w = ev.xmotion.x;
cc->geom.h = ev.xmotion.y; cc->geom.h = ev.xmotion.y;
client_applysizehints(cc); client_apply_sizehints(cc);
client_resize(cc, 1); client_resize(cc, 1);
menu_windraw(sc, cc->win, screen_prop_win_draw(sc,
"%4d x %-4d", cc->dim.w, cc->dim.h); "%4d x %-4d", cc->dim.w, cc->dim.h);
break; break;
case ButtonRelease: case ButtonRelease:
@ -281,8 +284,7 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
} }
if (ltime) if (ltime)
client_resize(cc, 1); client_resize(cc, 1);
XUnmapWindow(X_Dpy, sc->menu.win); screen_prop_win_destroy(sc);
XReparentWindow(X_Dpy, sc->menu.win, sc->rootwin, 0, 0);
XUngrabPointer(X_Dpy, CurrentTime); XUngrabPointer(X_Dpy, CurrentTime);
/* Make sure the pointer stays within the window. */ /* Make sure the pointer stays within the window. */
@ -299,7 +301,7 @@ kbfunc_client_snap(void *ctx, struct cargs *cargs)
area = screen_area(sc, area = screen_area(sc,
cc->geom.x + cc->geom.w / 2, cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.y + cc->geom.h / 2, 1);
flags = cargs->flag; flags = cargs->flag;
while (flags) { while (flags) {
@ -323,18 +325,19 @@ kbfunc_client_snap(void *ctx, struct cargs *cargs)
} }
} }
client_move(cc); client_move(cc);
client_ptr_inbound(cc, 1);
} }
void void
kbfunc_client_delete(void *ctx, struct cargs *cargs) kbfunc_client_close(void *ctx, struct cargs *cargs)
{ {
client_send_delete(ctx); client_close(ctx);
} }
void void
kbfunc_client_lower(void *ctx, struct cargs *cargs) kbfunc_client_lower(void *ctx, struct cargs *cargs)
{ {
client_ptrsave(ctx); client_ptr_save(ctx);
client_lower(ctx); client_lower(ctx);
} }
@ -402,13 +405,64 @@ void
kbfunc_client_cycle(void *ctx, struct cargs *cargs) kbfunc_client_cycle(void *ctx, struct cargs *cargs)
{ {
struct screen_ctx *sc = ctx; struct screen_ctx *sc = ctx;
struct client_ctx *newcc, *oldcc, *prevcc;
int again = 1, flags = cargs->flag;
/* For X apps that ignore/steal events. */ /* For X apps that ignore/steal events. */
if (cargs->xev == CWM_XEV_KEY) if (cargs->xev == CWM_XEV_KEY)
XGrabKeyboard(X_Dpy, sc->rootwin, True, XGrabKeyboard(X_Dpy, sc->rootwin, True,
GrabModeAsync, GrabModeAsync, CurrentTime); GrabModeAsync, GrabModeAsync, CurrentTime);
client_cycle(sc, cargs->flag); if (TAILQ_EMPTY(&sc->clientq))
return;
prevcc = TAILQ_FIRST(&sc->clientq);
oldcc = client_current(sc);
if (oldcc == NULL)
oldcc = (flags & CWM_CYCLE_REVERSE) ?
TAILQ_LAST(&sc->clientq, client_q) :
TAILQ_FIRST(&sc->clientq);
newcc = oldcc;
while (again) {
again = 0;
newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) :
client_next(newcc);
/* Only cycle visible and non-ignored windows. */
if ((newcc->flags & (CLIENT_SKIP_CYCLE)) ||
((flags & CWM_CYCLE_INGROUP) &&
(newcc->gc != oldcc->gc)))
again = 1;
/* Is oldcc the only non-hidden window? */
if (newcc == oldcc) {
if (again)
return; /* No windows visible. */
break;
}
}
/* Reset when cycling mod is released. XXX I hate this hack */
sc->cycling = 1;
client_ptr_save(oldcc);
client_raise(prevcc);
client_raise(newcc);
if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) {
newcc->ptr.x = newcc->geom.w / 2;
newcc->ptr.y = newcc->geom.h / 2;
}
/* When no client is active, warp pointer to last active. */
if (oldcc->flags & (CLIENT_ACTIVE))
client_ptr_warp(newcc);
else if (oldcc->flags & (CLIENT_SKIP_CYCLE))
client_ptr_warp(newcc);
else {
client_raise(oldcc);
client_ptr_warp(oldcc);
}
} }
void void
@ -430,30 +484,44 @@ kbfunc_client_movetogroup(void *ctx, struct cargs *cargs)
group_movetogroup(ctx, cargs->flag); group_movetogroup(ctx, cargs->flag);
} }
void
kbfunc_group_toggle(void *ctx, struct cargs *cargs)
{
group_hidetoggle(ctx, cargs->flag);
}
void void
kbfunc_group_only(void *ctx, struct cargs *cargs) kbfunc_group_only(void *ctx, struct cargs *cargs)
{ {
group_only(ctx, cargs->flag); group_only(ctx, cargs->flag);
} }
void
kbfunc_group_last(void *ctx, struct cargs *cargs)
{
struct screen_ctx *sc = ctx;
group_only(ctx, sc->group_last->num);
}
void
kbfunc_group_toggle(void *ctx, struct cargs *cargs)
{
group_toggle(ctx, cargs->flag);
}
void
kbfunc_group_toggle_all(void *ctx, struct cargs *cargs)
{
group_toggle_all(ctx);
}
void
kbfunc_group_close(void *ctx, struct cargs *cargs)
{
group_close(ctx, cargs->flag);
}
void void
kbfunc_group_cycle(void *ctx, struct cargs *cargs) kbfunc_group_cycle(void *ctx, struct cargs *cargs)
{ {
group_cycle(ctx, cargs->flag); group_cycle(ctx, cargs->flag);
} }
void
kbfunc_group_alltoggle(void *ctx, struct cargs *cargs)
{
group_alltoggle(ctx);
}
void void
kbfunc_menu_client(void *ctx, struct cargs *cargs) kbfunc_menu_client(void *ctx, struct cargs *cargs)
{ {
@ -461,20 +529,15 @@ kbfunc_menu_client(void *ctx, struct cargs *cargs)
struct client_ctx *cc, *old_cc; struct client_ctx *cc, *old_cc;
struct menu *mi; struct menu *mi;
struct menu_q menuq; struct menu_q menuq;
int all = (cargs->flag & CWM_MENU_WINDOW_ALL);
int mflags = 0; int mflags = 0;
if (cargs->xev == CWM_XEV_BTN) if (cargs->xev == CWM_XEV_BTN)
mflags |= CWM_MENU_LIST; mflags |= CWM_MENU_LIST;
old_cc = client_current();
TAILQ_INIT(&menuq); TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &sc->clientq, entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (!all) { if ((cargs->flag & CWM_MENU_WINDOW_ALL) ||
if (cc->flags & CLIENT_HIDDEN) (cc->flags & CLIENT_HIDDEN))
menuq_add(&menuq, cc, NULL);
} else
menuq_add(&menuq, cc, NULL); menuq_add(&menuq, cc, NULL);
} }
@ -482,9 +545,9 @@ kbfunc_menu_client(void *ctx, struct cargs *cargs)
search_match_client, search_print_client)) != NULL) { search_match_client, search_print_client)) != NULL) {
cc = (struct client_ctx *)mi->ctx; cc = (struct client_ctx *)mi->ctx;
client_show(cc); client_show(cc);
if (old_cc) if ((old_cc = client_current(sc)) != NULL)
client_ptrsave(old_cc); client_ptr_save(old_cc);
client_ptrwarp(cc); client_ptr_warp(cc);
} }
menuq_clear(&menuq); menuq_clear(&menuq);
@ -617,7 +680,7 @@ kbfunc_menu_exec(void *ctx, struct cargs *cargs)
/* lstat(2) in case d_type isn't supported. */ /* lstat(2) in case d_type isn't supported. */
if (lstat(tpath, &sb) == -1) if (lstat(tpath, &sb) == -1)
continue; continue;
if (!S_ISREG(sb.st_mode) && if (!S_ISREG(sb.st_mode) &&
!S_ISLNK(sb.st_mode)) !S_ISLNK(sb.st_mode))
continue; continue;
} }

139
menu.c
View File

@ -37,6 +37,10 @@
#define PROMPT_SCHAR "\xc2\xbb" #define PROMPT_SCHAR "\xc2\xbb"
#define PROMPT_ECHAR "\xc2\xab" #define PROMPT_ECHAR "\xc2\xab"
#define MENUMASK (MOUSEMASK | ButtonMotionMask | KeyPressMask | \
ExposureMask)
#define MENUGRABMASK (MOUSEMASK | ButtonMotionMask | StructureNotifyMask)
enum ctltype { enum ctltype {
CTL_NONE = -1, CTL_NONE = -1,
CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN, CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
@ -45,6 +49,9 @@ enum ctltype {
struct menu_ctx { struct menu_ctx {
struct screen_ctx *sc; struct screen_ctx *sc;
Window win;
XftDraw *xftdraw;
struct geom geom;
char searchstr[MENU_MAXENTRY + 1]; char searchstr[MENU_MAXENTRY + 1];
char dispstr[MENU_MAXENTRY*2 + 1]; char dispstr[MENU_MAXENTRY*2 + 1];
char promptstr[MENU_MAXENTRY + 1]; char promptstr[MENU_MAXENTRY + 1];
@ -55,7 +62,6 @@ struct menu_ctx {
int entry; int entry;
int num; int num;
int flags; int flags;
struct geom geom;
void (*match)(struct menu_q *, struct menu_q *, char *); void (*match)(struct menu_q *, struct menu_q *, char *);
void (*print)(struct menu *, int); void (*print)(struct menu *, int);
}; };
@ -88,7 +94,7 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
TAILQ_INIT(&resultq); TAILQ_INIT(&resultq);
xu_ptr_getpos(sc->rootwin, &xsave, &ysave); xu_ptr_get(sc->rootwin, &xsave, &ysave);
(void)memset(&mc, 0, sizeof(mc)); (void)memset(&mc, 0, sizeof(mc));
mc.sc = sc; mc.sc = sc;
@ -108,27 +114,35 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
else else
mc.searchstr[0] = '\0'; mc.searchstr[0] = '\0';
XSelectInput(X_Dpy, sc->menu.win, MENUMASK); mc.win = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
XMapRaised(X_Dpy, sc->menu.win); Conf.bwidth,
sc->xftcolor[CWM_COLOR_MENU_FG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
mc.xftdraw = XftDrawCreate(X_Dpy, mc.win,
sc->visual, sc->colormap);
if (XGrabPointer(X_Dpy, sc->menu.win, False, MENUGRABMASK, XSelectInput(X_Dpy, mc.win, MENUMASK);
XMapRaised(X_Dpy, mc.win);
if (XGrabPointer(X_Dpy, mc.win, False, MENUGRABMASK,
GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION], GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION],
CurrentTime) != GrabSuccess) { CurrentTime) != GrabSuccess) {
XUnmapWindow(X_Dpy, sc->menu.win); XftDrawDestroy(mc.xftdraw);
return(NULL); XDestroyWindow(X_Dpy, mc.win);
return NULL;
} }
XGetInputFocus(X_Dpy, &focuswin, &focusrevert); XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
XSetInputFocus(X_Dpy, sc->menu.win, RevertToPointerRoot, CurrentTime); XSetInputFocus(X_Dpy, mc.win, RevertToPointerRoot, CurrentTime);
/* make sure keybindings don't remove keys from the menu stream */ /* make sure keybindings don't remove keys from the menu stream */
XGrabKeyboard(X_Dpy, sc->menu.win, True, XGrabKeyboard(X_Dpy, mc.win, True,
GrabModeAsync, GrabModeAsync, CurrentTime); GrabModeAsync, GrabModeAsync, CurrentTime);
for (;;) { for (;;) {
mc.changed = 0; mc.changed = 0;
XWindowEvent(X_Dpy, sc->menu.win, MENUMASK, &e); XWindowEvent(X_Dpy, mc.win, MENUMASK, &e);
switch (e.type) { switch (e.type) {
case KeyPress: case KeyPress:
@ -159,18 +173,19 @@ out:
mi = NULL; mi = NULL;
} }
XftDrawDestroy(mc.xftdraw);
XDestroyWindow(X_Dpy, mc.win);
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime); XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
/* restore if user didn't move */ /* restore if user didn't move */
xu_ptr_getpos(sc->rootwin, &xcur, &ycur); xu_ptr_get(sc->rootwin, &xcur, &ycur);
if (xcur == mc.geom.x && ycur == mc.geom.y) if (xcur == mc.geom.x && ycur == mc.geom.y)
xu_ptr_setpos(sc->rootwin, xsave, ysave); xu_ptr_set(sc->rootwin, xsave, ysave);
XUngrabPointer(X_Dpy, CurrentTime);
XMoveResizeWindow(X_Dpy, sc->menu.win, 0, 0, 1, 1); XUngrabPointer(X_Dpy, CurrentTime);
XUnmapWindow(X_Dpy, sc->menu.win);
XUngrabKeyboard(X_Dpy, CurrentTime); XUngrabKeyboard(X_Dpy, CurrentTime);
return(mi); return mi;
} }
static struct menu * static struct menu *
@ -198,7 +213,7 @@ menu_complete_path(struct menu_ctx *mc)
menuq_clear(&menuq); menuq_clear(&menuq);
return(mr); return mr;
} }
static struct menu * static struct menu *
@ -213,7 +228,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
wchar_t wc; wchar_t wc;
if (menu_keycode(&e->xkey, &ctl, chr) < 0) if (menu_keycode(&e->xkey, &ctl, chr) < 0)
return(NULL); return NULL;
switch (ctl) { switch (ctl) {
case CTL_ERASEONE: case CTL_ERASEONE:
@ -254,7 +269,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
mi->dummy = 1; mi->dummy = 1;
} }
mi->abort = 0; mi->abort = 0;
return(mi); return mi;
case CTL_WIPE: case CTL_WIPE:
mc->searchstr[0] = '\0'; mc->searchstr[0] = '\0';
mc->changed = 1; mc->changed = 1;
@ -269,7 +284,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
if ((mc->flags & CWM_MENU_FILE) && if ((mc->flags & CWM_MENU_FILE) &&
(strncmp(mc->searchstr, mi->text, (strncmp(mc->searchstr, mi->text,
strlen(mi->text))) == 0) strlen(mi->text))) == 0)
return(menu_complete_path(mc)); return menu_complete_path(mc);
/* /*
* Put common prefix of the results into searchstr * Put common prefix of the results into searchstr
@ -294,7 +309,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
mi->text[0] = '\0'; mi->text[0] = '\0';
mi->dummy = 1; mi->dummy = 1;
mi->abort = 1; mi->abort = 1;
return(mi); return mi;
default: default:
break; break;
} }
@ -312,7 +327,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
mc->listing = 0; mc->listing = 0;
} }
return(NULL); return NULL;
} }
static void static void
@ -340,7 +355,7 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
XftTextExtentsUtf8(X_Dpy, sc->xftfont, XftTextExtentsUtf8(X_Dpy, sc->xftfont,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr), &extents); (const FcChar8*)mc->dispstr, strlen(mc->dispstr), &extents);
mc->geom.w = extents.xOff; mc->geom.w = extents.xOff;
mc->geom.h = sc->xftfont->height + 1; mc->geom.h = sc->xftfont->ascent + sc->xftfont->descent;
mc->num = 1; mc->num = 1;
TAILQ_FOREACH(mi, resultq, resultentry) { TAILQ_FOREACH(mi, resultq, resultentry) {
@ -349,11 +364,11 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
(const FcChar8*)mi->print, (const FcChar8*)mi->print,
MIN(strlen(mi->print), MENU_MAXENTRY), &extents); MIN(strlen(mi->print), MENU_MAXENTRY), &extents);
mc->geom.w = MAX(mc->geom.w, extents.xOff); mc->geom.w = MAX(mc->geom.w, extents.xOff);
mc->geom.h += sc->xftfont->height + 1; mc->geom.h += sc->xftfont->ascent + sc->xftfont->descent;
mc->num++; mc->num++;
} }
area = screen_area(sc, mc->geom.x, mc->geom.y, CWM_GAP); area = screen_area(sc, mc->geom.x, mc->geom.y, 1);
area.w += area.x - Conf.bwidth * 2; area.w += area.x - Conf.bwidth * 2;
area.h += area.y - Conf.bwidth * 2; area.h += area.y - Conf.bwidth * 2;
@ -375,28 +390,28 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
} }
if (mc->geom.x != xsave || mc->geom.y != ysave) if (mc->geom.x != xsave || mc->geom.y != ysave)
xu_ptr_setpos(sc->rootwin, mc->geom.x, mc->geom.y); xu_ptr_set(sc->rootwin, mc->geom.x, mc->geom.y);
XClearWindow(X_Dpy, sc->menu.win); XClearWindow(X_Dpy, mc->win);
XMoveResizeWindow(X_Dpy, sc->menu.win, mc->geom.x, mc->geom.y, XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y,
mc->geom.w, mc->geom.h); mc->geom.w, mc->geom.h);
n = 1; n = 1;
XftDrawStringUtf8(sc->menu.xftdraw, XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont, &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, sc->xftfont->ascent, 0, sc->xftfont->ascent,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr)); (const FcChar8*)mc->dispstr, strlen(mc->dispstr));
TAILQ_FOREACH(mi, resultq, resultentry) { TAILQ_FOREACH(mi, resultq, resultentry) {
int y = n * (sc->xftfont->height + 1) + sc->xftfont->ascent + 1; int y = n * (sc->xftfont->ascent + sc->xftfont->descent);
/* Stop drawing when menu doesn't fit inside the screen. */ /* Stop drawing when menu doesn't fit inside the screen. */
if (mc->geom.y + y > area.h) if (mc->geom.y + y >= area.h)
break; break;
XftDrawStringUtf8(sc->menu.xftdraw, XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont, &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, y, 0, y + sc->xftfont->ascent,
(const FcChar8*)mi->print, strlen(mi->print)); (const FcChar8*)mi->print, strlen(mi->print));
n++; n++;
} }
@ -410,7 +425,7 @@ menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
{ {
struct screen_ctx *sc = mc->sc; struct screen_ctx *sc = mc->sc;
struct menu *mi; struct menu *mi;
int color, i = 1; int color, i = 1, y;
TAILQ_FOREACH(mi, resultq, resultentry) TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++) if (entry == i++)
@ -418,14 +433,13 @@ menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
if (mi == NULL) if (mi == NULL)
return; return;
y = entry * (sc->xftfont->ascent + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG; color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
XftDrawRect(sc->menu.xftdraw, &sc->xftcolor[color], 0, XftDrawRect(mc->xftdraw, &sc->xftcolor[color], 0, y,
(sc->xftfont->height + 1) * entry, mc->geom.w, mc->geom.w, sc->xftfont->ascent + sc->xftfont->descent);
(sc->xftfont->height + 1) + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT; color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
XftDrawStringUtf8(sc->menu.xftdraw, XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[color], sc->xftfont, &sc->xftcolor[color], sc->xftfont, 0, y + sc->xftfont->ascent,
0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1,
(const FcChar8*)mi->print, strlen(mi->print)); (const FcChar8*)mi->print, strlen(mi->print));
} }
@ -463,7 +477,7 @@ menu_handle_release(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
mi->text[0] = '\0'; mi->text[0] = '\0';
mi->dummy = 1; mi->dummy = 1;
} }
return(mi); return mi;
} }
static int static int
@ -472,18 +486,18 @@ menu_calc_entry(struct menu_ctx *mc, int x, int y)
struct screen_ctx *sc = mc->sc; struct screen_ctx *sc = mc->sc;
int entry; int entry;
entry = y / (sc->xftfont->height + 1); entry = y / (sc->xftfont->ascent + sc->xftfont->descent);
/* in bounds? */ /* in bounds? */
if (x < 0 || x > mc->geom.w || y < 0 || if (x < 0 || x > mc->geom.w || y < 0 ||
y > (sc->xftfont->height + 1) * mc->num || y > (sc->xftfont->ascent + sc->xftfont->descent) * mc->num ||
entry < 0 || entry >= mc->num) entry < 0 || entry >= mc->num)
entry = -1; entry = -1;
if (entry == 0) if (entry == 0)
entry = -1; entry = -1;
return(entry); return entry;
} }
static int static int
@ -566,12 +580,12 @@ menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
} }
if (*ctl != CTL_NONE) if (*ctl != CTL_NONE)
return(0); return 0;
if (XLookupString(ev, chr, 32, &ks, NULL) < 0) if (XLookupString(ev, chr, 32, &ks, NULL) < 0)
return(-1); return -1;
return(0); return 0;
} }
void void
@ -603,34 +617,3 @@ menuq_clear(struct menu_q *mq)
free(mi); free(mi);
} }
} }
void
menu_windraw(struct screen_ctx *sc, Window win, const char *fmt, ...)
{
va_list ap;
int i;
char *text;
XGlyphInfo extents;
va_start(ap, fmt);
i = vasprintf(&text, fmt, ap);
va_end(ap);
if (i < 0 || text == NULL)
err(1, "vasprintf");
XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
strlen(text), &extents);
XReparentWindow(X_Dpy, sc->menu.win, win, 0, 0);
XMoveResizeWindow(X_Dpy, sc->menu.win, 0, 0,
extents.xOff, sc->xftfont->height);
XMapWindow(X_Dpy, sc->menu.win);
XClearWindow(X_Dpy, sc->menu.win);
XftDrawStringUtf8(sc->menu.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
sc->xftfont, 0, sc->xftfont->ascent + 1,
(const FcChar8*)text, strlen(text));
free(text);
}

64
parse.y
View File

@ -22,7 +22,7 @@
%{ %{
#include <sys/types.h> #include <sys/types.h>
#include <sys/queue.h> #include "queue.h"
#include <ctype.h> #include <ctype.h>
#include <err.h> #include <err.h>
@ -73,7 +73,7 @@ typedef struct {
%token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE %token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
%token FONTNAME STICKY GAP %token FONTNAME STICKY GAP
%token AUTOGROUP COMMAND IGNORE WM %token AUTOGROUP COMMAND IGNORE WM
%token YES NO BORDERWIDTH MOVEAMOUNT %token YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE
%token COLOR SNAPDIST %token COLOR SNAPDIST
%token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER %token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
%token GROUPBORDER UNGROUPBORDER %token GROUPBORDER UNGROUPBORDER
@ -83,7 +83,7 @@ typedef struct {
%token <v.string> STRING %token <v.string> STRING
%token <v.number> NUMBER %token <v.number> NUMBER
%type <v.number> yesno %type <v.number> yesno
%type <v.string> string %type <v.string> string numberstring
%% %%
grammar : /* empty */ grammar : /* empty */
@ -106,6 +106,17 @@ string : string STRING {
| STRING | STRING
; ;
numberstring : NUMBER {
char *s;
if (asprintf(&s, "%lld", $1) == -1) {
yyerror("string: asprintf");
YYERROR;
}
$$ = s;
}
| STRING
;
yesno : YES { $$ = 1; } yesno : YES { $$ = 1; }
| NO { $$ = 0; } | NO { $$ = 0; }
; ;
@ -124,6 +135,20 @@ main : FONTNAME STRING {
} }
conf->bwidth = $2; conf->bwidth = $2;
} }
| HTILE NUMBER {
if ($2 < 0 || $2 > 99) {
yyerror("invalid htile percent");
YYERROR;
}
conf->htile = $2;
}
| VTILE NUMBER {
if ($2 < 0 || $2 > 99) {
yyerror("invalid vtile percent");
YYERROR;
}
conf->vtile = $2;
}
| MOVEAMOUNT NUMBER { | MOVEAMOUNT NUMBER {
if ($2 < 0 || $2 > INT_MAX) { if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid movemount"); yyerror("invalid movemount");
@ -197,7 +222,7 @@ main : FONTNAME STRING {
conf->gap.left = $4; conf->gap.left = $4;
conf->gap.right = $5; conf->gap.right = $5;
} }
| BINDKEY STRING string { | BINDKEY numberstring string {
if (!conf_bind_key(conf, $2, $3)) { if (!conf_bind_key(conf, $2, $3)) {
yyerror("invalid bind-key: %s %s", $2, $3); yyerror("invalid bind-key: %s %s", $2, $3);
free($2); free($2);
@ -207,7 +232,7 @@ main : FONTNAME STRING {
free($2); free($2);
free($3); free($3);
} }
| UNBINDKEY STRING { | UNBINDKEY numberstring {
if (!conf_bind_key(conf, $2, NULL)) { if (!conf_bind_key(conf, $2, NULL)) {
yyerror("invalid unbind-key: %s", $2); yyerror("invalid unbind-key: %s", $2);
free($2); free($2);
@ -215,7 +240,7 @@ main : FONTNAME STRING {
} }
free($2); free($2);
} }
| BINDMOUSE STRING string { | BINDMOUSE numberstring string {
if (!conf_bind_mouse(conf, $2, $3)) { if (!conf_bind_mouse(conf, $2, $3)) {
yyerror("invalid bind-mouse: %s %s", $2, $3); yyerror("invalid bind-mouse: %s %s", $2, $3);
free($2); free($2);
@ -225,7 +250,7 @@ main : FONTNAME STRING {
free($2); free($2);
free($3); free($3);
} }
| UNBINDMOUSE STRING { | UNBINDMOUSE numberstring {
if (!conf_bind_mouse(conf, $2, NULL)) { if (!conf_bind_mouse(conf, $2, NULL)) {
yyerror("invalid unbind-mouse: %s", $2); yyerror("invalid unbind-mouse: %s", $2);
free($2); free($2);
@ -318,6 +343,7 @@ lookup(char *s)
{ "fontname", FONTNAME}, { "fontname", FONTNAME},
{ "gap", GAP}, { "gap", GAP},
{ "groupborder", GROUPBORDER}, { "groupborder", GROUPBORDER},
{ "htile", HTILE},
{ "ignore", IGNORE}, { "ignore", IGNORE},
{ "inactiveborder", INACTIVEBORDER}, { "inactiveborder", INACTIVEBORDER},
{ "menubg", MENUBG}, { "menubg", MENUBG},
@ -331,6 +357,7 @@ lookup(char *s)
{ "unbind-mouse", UNBINDMOUSE}, { "unbind-mouse", UNBINDMOUSE},
{ "ungroupborder", UNGROUPBORDER}, { "ungroupborder", UNGROUPBORDER},
{ "urgencyborder", URGENCYBORDER}, { "urgencyborder", URGENCYBORDER},
{ "vtile", VTILE},
{ "wm", WM}, { "wm", WM},
{ "yes", YES} { "yes", YES}
}; };
@ -360,7 +387,7 @@ lgetc(int quotec)
if (parsebuf) { if (parsebuf) {
/* Read character from the parsebuffer instead of input. */ /* Read character from the parsebuffer instead of input. */
if (parseindex >= 0) { if (parseindex >= 0) {
c = parsebuf[parseindex++]; c = (unsigned char)parsebuf[parseindex++];
if (c != '\0') if (c != '\0')
return (c); return (c);
parsebuf = NULL; parsebuf = NULL;
@ -369,7 +396,7 @@ lgetc(int quotec)
} }
if (pushback_index) if (pushback_index)
return (pushback_buffer[--pushback_index]); return ((unsigned char)pushback_buffer[--pushback_index]);
if (quotec) { if (quotec) {
if ((c = getc(file->stream)) == EOF) { if ((c = getc(file->stream)) == EOF) {
@ -410,10 +437,10 @@ lungetc(int c)
if (parseindex >= 0) if (parseindex >= 0)
return (c); return (c);
} }
if (pushback_index < MAXPUSHBACK-1) if (pushback_index + 1 >= MAXPUSHBACK)
return (pushback_buffer[pushback_index++] = c);
else
return (EOF); return (EOF);
pushback_buffer[pushback_index++] = c;
return (c);
} }
int int
@ -426,7 +453,7 @@ findeol(void)
/* skip to either EOF or the first real EOL */ /* skip to either EOF or the first real EOL */
while (1) { while (1) {
if (pushback_index) if (pushback_index)
c = pushback_buffer[--pushback_index]; c = (unsigned char)pushback_buffer[--pushback_index];
else else
c = lgetc(0); c = lgetc(0);
if (c == '\n') { if (c == '\n') {
@ -469,7 +496,8 @@ yylex(void)
} else if (c == '\\') { } else if (c == '\\') {
if ((next = lgetc(quotec)) == EOF) if ((next = lgetc(quotec)) == EOF)
return (0); return (0);
if (next == quotec || c == ' ' || c == '\t') if (next == quotec || next == ' ' ||
next == '\t')
c = next; c = next;
else if (next == '\n') { else if (next == '\n') {
file->lineno++; file->lineno++;
@ -499,7 +527,7 @@ yylex(void)
if (c == '-' || isdigit(c)) { if (c == '-' || isdigit(c)) {
do { do {
*p++ = c; *p++ = c;
if ((unsigned)(p-buf) >= sizeof(buf)) { if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long"); yyerror("string too long");
return (findeol()); return (findeol());
} }
@ -522,8 +550,8 @@ yylex(void)
} else { } else {
nodigits: nodigits:
while (p > buf + 1) while (p > buf + 1)
lungetc(*--p); lungetc((unsigned char)*--p);
c = *--p; c = (unsigned char)*--p;
if (c == '-') if (c == '-')
return (c); return (c);
} }
@ -538,7 +566,7 @@ nodigits:
if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') { if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') {
do { do {
*p++ = c; *p++ = c;
if ((unsigned)(p-buf) >= sizeof(buf)) { if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long"); yyerror("string too long");
return (findeol()); return (findeol());
} }

118
screen.c
View File

@ -24,6 +24,7 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -32,15 +33,13 @@
#include "calmwm.h" #include "calmwm.h"
static struct geom screen_apply_gap(struct screen_ctx *, struct geom); static struct geom screen_apply_gap(struct screen_ctx *, struct geom);
static void screen_scan(struct screen_ctx *);
void void
screen_init(int which) screen_init(int which)
{ {
struct screen_ctx *sc; struct screen_ctx *sc;
Window *wins, w0, w1, active = None; XSetWindowAttributes attr;
XSetWindowAttributes rootattr;
unsigned int nwins, w;
int i;
sc = xmalloc(sizeof(*sc)); sc = xmalloc(sizeof(*sc));
@ -60,43 +59,54 @@ screen_init(int which)
xu_ewmh_net_supported(sc); xu_ewmh_net_supported(sc);
xu_ewmh_net_supported_wm_check(sc); xu_ewmh_net_supported_wm_check(sc);
conf_group(sc);
sc->group_last = sc->group_active;
screen_update_geometry(sc); screen_update_geometry(sc);
for (i = 0; i < Conf.ngroups; i++)
group_init(sc, i);
xu_ewmh_net_desktop_names(sc); xu_ewmh_net_desktop_names(sc);
xu_ewmh_net_wm_desktop_viewport(sc); xu_ewmh_net_number_of_desktops(sc);
xu_ewmh_net_wm_number_of_desktops(sc);
xu_ewmh_net_showing_desktop(sc); xu_ewmh_net_showing_desktop(sc);
xu_ewmh_net_virtual_roots(sc); xu_ewmh_net_virtual_roots(sc);
active = xu_ewmh_get_net_active_window(sc);
rootattr.cursor = Conf.cursor[CF_NORMAL]; attr.cursor = Conf.cursor[CF_NORMAL];
rootattr.event_mask = SubstructureRedirectMask | attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
SubstructureNotifyMask | PropertyChangeMask | EnterWindowMask | EnterWindowMask | PropertyChangeMask | ButtonPressMask;
LeaveWindowMask | ColormapChangeMask | BUTTONMASK; XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr);
XChangeWindowAttributes(X_Dpy, sc->rootwin,
(CWEventMask | CWCursor), &rootattr);
/* Deal with existing clients. */
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (w = 0; w < nwins; w++)
(void)client_init(wins[w], sc, (active == wins[w]));
XFree(wins);
}
screen_updatestackingorder(sc);
if (Conf.xrandr) if (Conf.xrandr)
XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask); XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
screen_scan(sc);
screen_updatestackingorder(sc);
TAILQ_INSERT_TAIL(&Screenq, sc, entry); TAILQ_INSERT_TAIL(&Screenq, sc, entry);
XSync(X_Dpy, False); XSync(X_Dpy, False);
} }
static void
screen_scan(struct screen_ctx *sc)
{
struct client_ctx *cc, *active = NULL;
Window *wins, w0, w1, rwin, cwin;
unsigned int nwins, i, mask;
int rx, ry, wx, wy;
XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin,
&rx, &ry, &wx, &wy, &mask);
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (i = 0; i < nwins; i++) {
if ((cc = client_init(wins[i], sc)) != NULL)
if (cc->win == cwin)
active = cc;
}
XFree(wins);
}
if (active)
client_set_active(active);
}
struct screen_ctx * struct screen_ctx *
screen_find(Window win) screen_find(Window win)
{ {
@ -104,10 +114,10 @@ screen_find(Window win)
TAILQ_FOREACH(sc, &Screenq, entry) { TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->rootwin == win) if (sc->rootwin == win)
return(sc); return sc;
} }
warnx("%s: failure win 0x%lu\n", __func__, win); warnx("%s: failure win 0x%lx", __func__, win);
return(NULL); return NULL;
} }
void void
@ -141,11 +151,11 @@ region_find(struct screen_ctx *sc, int x, int y)
break; break;
} }
} }
return(rc); return rc;
} }
struct geom struct geom
screen_area(struct screen_ctx *sc, int x, int y, enum apply_gap apply_gap) screen_area(struct screen_ctx *sc, int x, int y, int apply_gap)
{ {
struct region_ctx *rc; struct region_ctx *rc;
struct geom area = sc->view; struct geom area = sc->view;
@ -159,7 +169,7 @@ screen_area(struct screen_ctx *sc, int x, int y, enum apply_gap apply_gap)
} }
if (apply_gap) if (apply_gap)
area = screen_apply_gap(sc, area); area = screen_apply_gap(sc, area);
return(area); return area;
} }
void void
@ -217,6 +227,7 @@ screen_update_geometry(struct screen_ctx *sc)
} }
xu_ewmh_net_desktop_geometry(sc); xu_ewmh_net_desktop_geometry(sc);
xu_ewmh_net_desktop_viewport(sc);
xu_ewmh_net_workarea(sc); xu_ewmh_net_workarea(sc);
} }
@ -228,7 +239,7 @@ screen_apply_gap(struct screen_ctx *sc, struct geom geom)
geom.w -= (sc->gap.left + sc->gap.right); geom.w -= (sc->gap.left + sc->gap.right);
geom.h -= (sc->gap.top + sc->gap.bottom); geom.h -= (sc->gap.top + sc->gap.bottom);
return(geom); return geom;
} }
/* Bring back clients which are beyond the screen. */ /* Bring back clients which are beyond the screen. */
@ -253,3 +264,44 @@ screen_assert_clients_within(struct screen_ctx *sc)
} }
} }
} }
void
screen_prop_win_create(struct screen_ctx *sc, Window win)
{
sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win,
sc->visual, sc->colormap);
XMapWindow(X_Dpy, sc->prop.win);
}
void
screen_prop_win_destroy(struct screen_ctx *sc)
{
XftDrawDestroy(sc->prop.xftdraw);
XDestroyWindow(X_Dpy, sc->prop.win);
}
void
screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...)
{
va_list ap;
char *text;
XGlyphInfo extents;
va_start(ap, fmt);
xvasprintf(&text, fmt, ap);
va_end(ap);
XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
strlen(text), &extents);
XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height);
XClearWindow(X_Dpy, sc->prop.win);
XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
sc->xftfont, 0, sc->xftfont->ascent + 1,
(const FcChar8*)text, strlen(text));
free(text);
}

View File

@ -46,13 +46,13 @@ match_substr(char *sub, char *str, int zeroidx)
unsigned int n, flen; unsigned int n, flen;
if (sub == NULL || str == NULL) if (sub == NULL || str == NULL)
return(0); return 0;
len = strlen(str); len = strlen(str);
sublen = strlen(sub); sublen = strlen(sub);
if (sublen > len) if (sublen > len)
return(0); return 0;
if (zeroidx) if (zeroidx)
flen = 0; flen = 0;
@ -61,9 +61,9 @@ match_substr(char *sub, char *str, int zeroidx)
for (n = 0; n <= flen; n++) for (n = 0; n <= flen; n++)
if (strncasecmp(sub, str + n, sublen) == 0) if (strncasecmp(sub, str + n, sublen) == 0)
return(1); return 1;
return(0); return 0;
} }
void void
@ -94,7 +94,7 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
} }
/* Match on window resource class. */ /* Match on window resource class. */
if ((tier < 0) && match_substr(search, cc->ch.res_class, 0)) if ((tier < 0) && match_substr(search, cc->res_class, 0))
tier = 2; tier = 2;
if (tier < 0) if (tier < 0)

10
util.c
View File

@ -24,6 +24,7 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -52,7 +53,7 @@ u_exec(char *argstr)
{ {
#define MAXARGLEN 20 #define MAXARGLEN 20
char *args[MAXARGLEN], **ap = args; char *args[MAXARGLEN], **ap = args;
char **end = &args[MAXARGLEN - 1], *tmp; char **end = &args[MAXARGLEN - 2], *tmp;
char *s = argstr; char *s = argstr;
while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) { while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) {
@ -91,12 +92,12 @@ u_argv(char * const *argv)
char *p; char *p;
if (argv == 0) if (argv == 0)
return(NULL); return NULL;
for (i = 0; argv[i]; i++) for (i = 0; argv[i]; i++)
siz += strlen(argv[i]) + 1; siz += strlen(argv[i]) + 1;
if (siz == 0) if (siz == 0)
return(NULL); return NULL;
p = xmalloc(siz); p = xmalloc(siz);
strlcpy(p, argv[0], siz); strlcpy(p, argv[0], siz);
@ -104,7 +105,7 @@ u_argv(char * const *argv)
strlcat(p, " ", siz); strlcat(p, " ", siz);
strlcat(p, argv[i], siz); strlcat(p, argv[i], siz);
} }
return(p); return p;
} }
static void static void
@ -134,5 +135,6 @@ log_debug(int level, const char *func, const char *msg, ...)
va_start(ap, msg); va_start(ap, msg);
xasprintf(&fmt, "debug%d: %s: %s", level, func, msg); xasprintf(&fmt, "debug%d: %s: %s", level, func, msg);
log_msg(fmt, ap); log_msg(fmt, ap);
free(fmt);
va_end(ap); va_end(ap);
} }

117
xevents.c
View File

@ -69,24 +69,28 @@ void (*xev_handlers[LASTEvent])(XEvent *) = {
}; };
static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R, static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R,
XK_Control_L, XK_Control_R }; XK_Control_L, XK_Control_R, XK_ISO_Level3_Shift };
static void static void
xev_handle_maprequest(XEvent *ee) xev_handle_maprequest(XEvent *ee)
{ {
XMapRequestEvent *e = &ee->xmaprequest; XMapRequestEvent *e = &ee->xmaprequest;
struct client_ctx *cc = NULL, *old_cc; struct screen_ctx *sc;
struct client_ctx *cc, *old_cc;
LOG_DEBUG3("window: 0x%lx", e->window); LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window);
if ((old_cc = client_current()) != NULL) if ((sc = screen_find(e->parent)) == NULL)
client_ptrsave(old_cc); return;
if ((old_cc = client_current(sc)) != NULL)
client_ptr_save(old_cc);
if ((cc = client_find(e->window)) == NULL) if ((cc = client_find(e->window)) == NULL)
cc = client_init(e->window, NULL, 0); cc = client_init(e->window, NULL);
if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE))) if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
client_ptrwarp(cc); client_ptr_warp(cc);
} }
static void static void
@ -99,10 +103,10 @@ xev_handle_unmapnotify(XEvent *ee)
if ((cc = client_find(e->window)) != NULL) { if ((cc = client_find(e->window)) != NULL) {
if (e->send_event) { if (e->send_event) {
client_set_wm_state(cc, WithdrawnState); xu_set_wm_state(cc->win, WithdrawnState);
} else { } else {
if (!(cc->flags & CLIENT_HIDDEN)) if (!(cc->flags & CLIENT_HIDDEN))
client_delete(cc); client_remove(cc);
} }
} }
} }
@ -116,7 +120,7 @@ xev_handle_destroynotify(XEvent *ee)
LOG_DEBUG3("window: 0x%lx", e->window); LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL) if ((cc = client_find(e->window)) != NULL)
client_delete(cc); client_remove(cc);
} }
static void static void
@ -187,10 +191,10 @@ xev_handle_propertynotify(XEvent *ee)
if ((cc = client_find(e->window)) != NULL) { if ((cc = client_find(e->window)) != NULL) {
switch (e->atom) { switch (e->atom) {
case XA_WM_NORMAL_HINTS: case XA_WM_NORMAL_HINTS:
client_getsizehints(cc); client_get_sizehints(cc);
break; break;
case XA_WM_NAME: case XA_WM_NAME:
client_setname(cc); client_set_name(cc);
break; break;
case XA_WM_HINTS: case XA_WM_HINTS:
client_wm_hints(cc); client_wm_hints(cc);
@ -198,17 +202,19 @@ xev_handle_propertynotify(XEvent *ee)
break; break;
case XA_WM_TRANSIENT_FOR: case XA_WM_TRANSIENT_FOR:
client_transient(cc); client_transient(cc);
client_draw_border(cc);
if (cc->gc)
group_movetogroup(cc, cc->gc->num);
break; break;
default: default:
/* do nothing */ if (e->atom == ewmh[_NET_WM_NAME])
client_set_name(cc);
break; break;
} }
} else { } else {
TAILQ_FOREACH(sc, &Screenq, entry) { if (e->atom == ewmh[_NET_DESKTOP_NAMES]) {
if (sc->rootwin == e->window) { if ((sc = screen_find(e->window)) != NULL)
if (e->atom == ewmh[_NET_DESKTOP_NAMES]) xu_ewmh_net_desktop_names(sc);
xu_ewmh_net_desktop_names(sc);
}
} }
} }
} }
@ -224,7 +230,7 @@ xev_handle_enternotify(XEvent *ee)
Last_Event_Time = e->time; Last_Event_Time = e->time;
if ((cc = client_find(e->window)) != NULL) if ((cc = client_find(e->window)) != NULL)
client_setactive(cc); client_set_active(cc);
} }
static void static void
@ -235,7 +241,11 @@ xev_handle_buttonpress(XEvent *ee)
struct screen_ctx *sc; struct screen_ctx *sc;
struct bind_ctx *mb; struct bind_ctx *mb;
LOG_DEBUG3("window: 0x%lx", e->window); LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((sc = screen_find(e->root)) == NULL)
return;
e->state &= ~IGNOREMODMASK; e->state &= ~IGNOREMODMASK;
@ -243,22 +253,17 @@ xev_handle_buttonpress(XEvent *ee)
if (e->button == mb->press.button && e->state == mb->modmask) if (e->button == mb->press.button && e->state == mb->modmask)
break; break;
} }
if (mb == NULL) if (mb == NULL)
return; return;
mb->cargs->xev = CWM_XEV_BTN; mb->cargs->xev = CWM_XEV_BTN;
switch (mb->context) { switch (mb->context) {
case CWM_CONTEXT_CC: case CWM_CONTEXT_CC:
if (((cc = client_find(e->window)) == NULL) && if (((cc = client_find(e->window)) == NULL) &&
(cc = client_current()) == NULL) ((cc = client_current(sc)) == NULL))
return; return;
(*mb->callback)(cc, mb->cargs); (*mb->callback)(cc, mb->cargs);
break; break;
case CWM_CONTEXT_SC: case CWM_CONTEXT_SC:
if (e->window != e->root)
return;
if ((sc = screen_find(e->window)) == NULL)
return;
(*mb->callback)(sc, mb->cargs); (*mb->callback)(sc, mb->cargs);
break; break;
case CWM_CONTEXT_NONE: case CWM_CONTEXT_NONE:
@ -273,7 +278,8 @@ xev_handle_buttonrelease(XEvent *ee)
XButtonEvent *e = &ee->xbutton; XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc; struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", ee->xbutton.window); LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((cc = client_find(e->window)) != NULL) { if ((cc = client_find(e->window)) != NULL) {
if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) { if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) {
@ -293,7 +299,11 @@ xev_handle_keypress(XEvent *ee)
KeySym keysym, skeysym; KeySym keysym, skeysym;
unsigned int modshift; unsigned int modshift;
LOG_DEBUG3("window: 0x%lx", e->window); LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((sc = screen_find(e->root)) == NULL)
return;
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1); skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
@ -312,20 +322,17 @@ xev_handle_keypress(XEvent *ee)
if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym)) if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym))
break; break;
} }
if (kb == NULL) if (kb == NULL)
return; return;
kb->cargs->xev = CWM_XEV_KEY; kb->cargs->xev = CWM_XEV_KEY;
switch (kb->context) { switch (kb->context) {
case CWM_CONTEXT_CC: case CWM_CONTEXT_CC:
if (((cc = client_find(e->window)) == NULL) && if (((cc = client_find(e->subwindow)) == NULL) &&
(cc = client_current()) == NULL) ((cc = client_current(sc)) == NULL))
return; return;
(*kb->callback)(cc, kb->cargs); (*kb->callback)(cc, kb->cargs);
break; break;
case CWM_CONTEXT_SC: case CWM_CONTEXT_SC:
if ((sc = screen_find(e->window)) == NULL)
return;
(*kb->callback)(sc, kb->cargs); (*kb->callback)(sc, kb->cargs);
break; break;
case CWM_CONTEXT_NONE: case CWM_CONTEXT_NONE:
@ -346,7 +353,8 @@ xev_handle_keyrelease(XEvent *ee)
KeySym keysym; KeySym keysym;
unsigned int i; unsigned int i;
LOG_DEBUG3("window: 0x%lx", e->window); LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((sc = screen_find(e->root)) == NULL) if ((sc = screen_find(e->root)) == NULL)
return; return;
@ -354,7 +362,7 @@ xev_handle_keyrelease(XEvent *ee)
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
for (i = 0; i < nitems(modkeys); i++) { for (i = 0; i < nitems(modkeys); i++) {
if (keysym == modkeys[i]) { if (keysym == modkeys[i]) {
if ((cc = client_current()) != NULL) { if ((cc = client_current(sc)) != NULL) {
if (sc->cycling) { if (sc->cycling) {
sc->cycling = 0; sc->cycling = 0;
client_mtf(cc); client_mtf(cc);
@ -386,14 +394,14 @@ xev_handle_clientmessage(XEvent *ee)
} }
} else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) { } else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) {
if ((cc = client_find(e->window)) != NULL) { if ((cc = client_find(e->window)) != NULL) {
client_send_delete(cc); client_close(cc);
} }
} else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) { } else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) {
if ((cc = client_find(e->window)) != NULL) { if ((cc = client_find(e->window)) != NULL) {
if ((old_cc = client_current()) != NULL) if ((old_cc = client_current(NULL)) != NULL)
client_ptrsave(old_cc); client_ptr_save(old_cc);
client_show(cc); client_show(cc);
client_ptrwarp(cc); client_ptr_warp(cc);
} }
} else if (e->message_type == ewmh[_NET_WM_DESKTOP]) { } else if (e->message_type == ewmh[_NET_WM_DESKTOP]) {
if ((cc = client_find(e->window)) != NULL) { if ((cc = client_find(e->window)) != NULL) {
@ -405,7 +413,9 @@ xev_handle_clientmessage(XEvent *ee)
if (e->data.l[0] == (unsigned long)-1) if (e->data.l[0] == (unsigned long)-1)
group_movetogroup(cc, 0); group_movetogroup(cc, 0);
else else
group_movetogroup(cc, e->data.l[0]); if (e->data.l[0] >= 0 &&
e->data.l[0] < Conf.ngroups)
group_movetogroup(cc, e->data.l[0]);
} }
} else if (e->message_type == ewmh[_NET_WM_STATE]) { } else if (e->message_type == ewmh[_NET_WM_STATE]) {
if ((cc = client_find(e->window)) != NULL) { if ((cc = client_find(e->window)) != NULL) {
@ -414,7 +424,9 @@ xev_handle_clientmessage(XEvent *ee)
} }
} else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) { } else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) {
if ((sc = screen_find(e->window)) != NULL) { if ((sc = screen_find(e->window)) != NULL) {
group_only(sc, e->data.l[0]); if (e->data.l[0] >= 0 &&
e->data.l[0] < Conf.ngroups)
group_only(sc, e->data.l[0]);
} }
} }
} }
@ -422,20 +434,17 @@ xev_handle_clientmessage(XEvent *ee)
static void static void
xev_handle_randr(XEvent *ee) xev_handle_randr(XEvent *ee)
{ {
XRRScreenChangeNotifyEvent *rev = (XRRScreenChangeNotifyEvent *)ee; XRRScreenChangeNotifyEvent *e = (XRRScreenChangeNotifyEvent *)ee;
struct screen_ctx *sc; struct screen_ctx *sc;
int i;
LOG_DEBUG3("new size: %d/%d", rev->width, rev->height); LOG_DEBUG3("size: %d/%d", e->width, e->height);
i = XRRRootToScreen(X_Dpy, rev->root); if ((sc = screen_find(e->root)) == NULL)
TAILQ_FOREACH(sc, &Screenq, entry) { return;
if (sc->which == i) {
XRRUpdateConfiguration(ee); XRRUpdateConfiguration(ee);
screen_update_geometry(sc); screen_update_geometry(sc);
screen_assert_clients_within(sc); screen_assert_clients_within(sc);
}
}
} }
/* /*
@ -476,9 +485,9 @@ xev_process(void)
while (XPending(X_Dpy)) { while (XPending(X_Dpy)) {
XNextEvent(X_Dpy, &e); XNextEvent(X_Dpy, &e);
if (e.type - Conf.xrandr_event_base == RRScreenChangeNotify) if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify)
xev_handle_randr(&e); xev_handle_randr(&e);
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL) else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL))
(*xev_handlers[e.type])(&e); (*xev_handlers[e.type])(&e);
} }
} }

View File

@ -43,7 +43,7 @@ xmalloc(size_t siz)
if ((p = malloc(siz)) == NULL) if ((p = malloc(siz)) == NULL)
err(1, "malloc"); err(1, "malloc");
return(p); return p;
} }
void * void *
@ -58,7 +58,7 @@ xcalloc(size_t no, size_t siz)
if ((p = calloc(no, siz)) == NULL) if ((p = calloc(no, siz)) == NULL)
err(1, "calloc"); err(1, "calloc");
return(p); return p;
} }
void * void *
@ -70,7 +70,7 @@ xreallocarray(void *ptr, size_t nmemb, size_t size)
if (p == NULL) if (p == NULL)
errx(1, "xreallocarray: out of memory (new_size %zu bytes)", errx(1, "xreallocarray: out of memory (new_size %zu bytes)",
nmemb * size); nmemb * size);
return(p); return p;
} }
char * char *
@ -81,7 +81,7 @@ xstrdup(const char *str)
if ((p = strdup(str)) == NULL) if ((p = strdup(str)) == NULL)
err(1, "strdup"); err(1, "strdup");
return(p); return p;
} }
int int
@ -91,11 +91,20 @@ xasprintf(char **ret, const char *fmt, ...)
int i; int i;
va_start(ap, fmt); va_start(ap, fmt);
i = vasprintf(ret, fmt, ap); i = xvasprintf(ret, fmt, ap);
va_end(ap); va_end(ap);
if (i < 0 || *ret == NULL) return i;
err(1, "asprintf"); }
return(i); int
xvasprintf(char **ret, const char *fmt, va_list ap)
{
int i;
i = vasprintf(ret, fmt, ap);
if (i == -1)
err(1, "vasprintf");
return i;
} }

194
xutil.c
View File

@ -32,7 +32,7 @@
#include "calmwm.h" #include "calmwm.h"
void void
xu_ptr_getpos(Window win, int *x, int *y) xu_ptr_get(Window win, int *x, int *y)
{ {
Window w0, w1; Window w0, w1;
int tmp0, tmp1; int tmp0, tmp1;
@ -42,13 +42,13 @@ xu_ptr_getpos(Window win, int *x, int *y)
} }
void void
xu_ptr_setpos(Window win, int x, int y) xu_ptr_set(Window win, int x, int y)
{ {
XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y); XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
} }
int int
xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p) xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p)
{ {
Atom realtype; Atom realtype;
unsigned long n, extra; unsigned long n, extra;
@ -56,16 +56,16 @@ xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type, if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type,
&realtype, &format, &n, &extra, p) != Success || *p == NULL) &realtype, &format, &n, &extra, p) != Success || *p == NULL)
return(-1); return -1;
if (n == 0) if (n == 0)
XFree(*p); XFree(*p);
return(n); return n;
} }
int int
xu_getstrprop(Window win, Atom atm, char **text) { xu_get_strprop(Window win, Atom atm, char **text) {
XTextProperty prop; XTextProperty prop;
char **list; char **list;
int nitems = 0; int nitems = 0;
@ -73,8 +73,10 @@ xu_getstrprop(Window win, Atom atm, char **text) {
*text = NULL; *text = NULL;
XGetTextProperty(X_Dpy, win, &prop, atm); XGetTextProperty(X_Dpy, win, &prop, atm);
if (!prop.nitems) if (!prop.nitems) {
return(0); XFree(prop.value);
return 0;
}
if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list, if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
&nitems) == Success && nitems > 0 && *list) { &nitems) == Success && nitems > 0 && *list) {
@ -90,10 +92,101 @@ xu_getstrprop(Window win, Atom atm, char **text) {
} }
XFreeStringList(list); XFreeStringList(list);
} }
XFree(prop.value); XFree(prop.value);
return(nitems); return nitems;
}
void
xu_send_clientmsg(Window win, Atom proto, Time ts)
{
XClientMessageEvent cm;
(void)memset(&cm, 0, sizeof(cm));
cm.type = ClientMessage;
cm.window = win;
cm.message_type = cwmh[WM_PROTOCOLS];
cm.format = 32;
cm.data.l[0] = proto;
cm.data.l[1] = ts;
XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm);
}
void
xu_get_wm_state(Window win, long *state)
{
long *p;
*state = -1;
if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
(unsigned char **)&p) > 0) {
*state = *p;
XFree(p);
}
}
void
xu_set_wm_state(Window win, long state)
{
long data[] = { state, None };
XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
PropModeReplace, (unsigned char *)data, 2);
}
void
xu_xorcolor(XftColor a, XftColor b, XftColor *r)
{
r->pixel = a.pixel ^ b.pixel;
r->color.red = a.color.red ^ b.color.red;
r->color.green = a.color.green ^ b.color.green;
r->color.blue = a.color.blue ^ b.color.blue;
r->color.alpha = 0xffff;
}
void
xu_atom_init(void)
{
char *cwmhints[] = {
"WM_STATE",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
"WM_PROTOCOLS",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"WM_CHANGE_STATE",
};
char *ewmhints[] = {
"_NET_SUPPORTED",
"_NET_SUPPORTING_WM_CHECK",
"_NET_ACTIVE_WINDOW",
"_NET_CLIENT_LIST",
"_NET_CLIENT_LIST_STACKING",
"_NET_NUMBER_OF_DESKTOPS",
"_NET_CURRENT_DESKTOP",
"_NET_DESKTOP_VIEWPORT",
"_NET_DESKTOP_GEOMETRY",
"_NET_VIRTUAL_ROOTS",
"_NET_SHOWING_DESKTOP",
"_NET_DESKTOP_NAMES",
"_NET_WORKAREA",
"_NET_WM_NAME",
"_NET_WM_DESKTOP",
"_NET_CLOSE_WINDOW",
"_NET_WM_STATE",
"_NET_WM_STATE_STICKY",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_HIDDEN",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_DEMANDS_ATTENTION",
"_NET_WM_STATE_SKIP_PAGER",
"_NET_WM_STATE_SKIP_TASKBAR",
"_CWM_WM_STATE_FREEZE",
};
XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
} }
/* Root Window Properties */ /* Root Window Properties */
@ -128,6 +221,16 @@ xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2); XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
} }
void
xu_ewmh_net_desktop_viewport(struct screen_ctx *sc)
{
long viewports[2] = {0, 0};
/* We don't support large desktops, so this is (0, 0). */
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
}
void void
xu_ewmh_net_workarea(struct screen_ctx *sc) xu_ewmh_net_workarea(struct screen_ctx *sc)
{ {
@ -195,34 +298,8 @@ xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
} }
Window
xu_ewmh_get_net_active_window(struct screen_ctx *sc)
{
long *p;
Window win;
if ((xu_getprop(sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
XA_WINDOW, 32, (unsigned char **)&p)) <= 0)
return(None);
win = (Window)*p;
XFree(p);
return(win);
}
void void
xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *sc) xu_ewmh_net_number_of_desktops(struct screen_ctx *sc)
{
long viewports[2] = {0, 0};
/* We don't support large desktops, so this is (0, 0). */
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
}
void
xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *sc)
{ {
long ndesks = Conf.ngroups; long ndesks = Conf.ngroups;
@ -270,7 +347,7 @@ xu_ewmh_net_desktop_names(struct screen_ctx *sc)
/* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */ /* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */
if ((j = xu_getprop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES], if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) { cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
prop_ret[j - 1] = '\0'; /* paranoia */ prop_ret[j - 1] = '\0'; /* paranoia */
while (i < j) { while (i < j) {
@ -312,8 +389,21 @@ xu_ewmh_net_desktop_names(struct screen_ctx *sc)
} }
/* Application Window Properties */ /* Application Window Properties */
int
xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n)
{
long *p;
if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
(unsigned char **)&p) <= 0)
return 0;
*n = *p;
XFree(p);
return 1;
}
void void
xu_ewmh_net_wm_desktop(struct client_ctx *cc) xu_ewmh_set_net_wm_desktop(struct client_ctx *cc)
{ {
long num = 0xffffffff; long num = 0xffffffff;
@ -329,15 +419,15 @@ xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
{ {
Atom *state, *p = NULL; Atom *state, *p = NULL;
if ((*n = xu_getprop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L, if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
(unsigned char **)&p)) <= 0) (unsigned char **)&p)) <= 0)
return(NULL); return NULL;
state = xreallocarray(NULL, *n, sizeof(Atom)); state = xreallocarray(NULL, *n, sizeof(Atom));
(void)memcpy(state, p, *n * sizeof(Atom)); (void)memcpy(state, p, *n * sizeof(Atom));
XFree((char *)p); XFree((char *)p);
return(state); return state;
} }
void void
@ -345,9 +435,9 @@ xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
Atom first, Atom second) Atom first, Atom second)
{ {
unsigned int i; unsigned int i;
static struct handlers { struct handlers {
int atom; Atom atom;
int property; int flag;
void (*toggle)(struct client_ctx *); void (*toggle)(struct client_ctx *);
} handlers[] = { } handlers[] = {
{ _NET_WM_STATE_STICKY, { _NET_WM_STATE_STICKY,
@ -385,11 +475,11 @@ xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
continue; continue;
switch (action) { switch (action) {
case _NET_WM_STATE_ADD: case _NET_WM_STATE_ADD:
if (!(cc->flags & handlers[i].property)) if (!(cc->flags & handlers[i].flag))
handlers[i].toggle(cc); handlers[i].toggle(cc);
break; break;
case _NET_WM_STATE_REMOVE: case _NET_WM_STATE_REMOVE:
if (cc->flags & handlers[i].property) if (cc->flags & handlers[i].flag)
handlers[i].toggle(cc); handlers[i].toggle(cc);
break; break;
case _NET_WM_STATE_TOGGLE: case _NET_WM_STATE_TOGGLE:
@ -476,13 +566,3 @@ xu_ewmh_set_net_wm_state(struct client_ctx *cc)
XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]); XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
free(atoms); free(atoms);
} }
void
xu_xorcolor(XftColor a, XftColor b, XftColor *r)
{
r->pixel = a.pixel ^ b.pixel;
r->color.red = a.color.red ^ b.color.red;
r->color.green = a.color.green ^ b.color.green;
r->color.blue = a.color.blue ^ b.color.blue;
r->color.alpha = 0xffff;
}