503 Commits

Author SHA1 Message Date
b7a8c11750 update README 2015-01-24 18:31:20 +01:00
e4ccad30ce cvsimport 2015-01-24 17:03:58 +00:00
d8fa58bb62 use malloc over calloc here 2015-01-23 20:26:36 +00:00
5146f661bd First restore net_wm_state(ewmh), then wm_state(iccc); prevents clients
from re-hiding on restart due to flag toggling (note that this is ripe
for re-vamping). Behavior only observed on restarts.

Problem found by, and initial patch from, Henri Kemppainen (thanks!),
though ever so slightly different one applied.
2015-01-23 19:35:11 +00:00
742732a1c8 cvsimport 2015-01-22 11:06:01 +01:00
7936b9b2a7 Switch to limits.h; replace MAXPATHLEN and MAXHOSTNAMELEN with PATH_MAX
and HOST_NAME_MAX+1, respectively.

ok doug@
2015-01-19 14:54:16 +00:00
c412f040df Catch up with src parse.y changes by jsg@:
Don't allow embedded nul characters in strings.
Fixes a pfctl crash with an anchor name containing
an embedded nul found with the afl fuzzer.

pfctl parse.y patch from and ok deraadt@
2015-01-17 02:05:03 +00:00
8d1eb202bf Add gcc format attributes for yyerror(); fix a few yyerror() calls.
Adapted from src parse.y changes by doug@. OK doug@
2015-01-16 18:28:08 +00:00
089ec389dd also sign releases with signify 2014-12-15 16:00:31 +01:00
56fad8271d Fix reference to OpenBSD's CVS repo web interface 2014-12-15 15:51:15 +01:00
9cada703b9 no need for calloc(1,.. here, malloc is fine since we initialize 2014-10-08 15:31:01 +00:00
501aa2bcf3 cvsimport 2014-10-08 15:31:01 +00:00
836c9f49d6 make group_init work like other *_init's 2014-10-08 12:48:51 +00:00
c700f7e19a cvsimport 2014-09-27 19:04:32 +00:00
e9dbd150ea these have nothing to do with 'sticky', but rather group membership; rename. 2014-09-27 19:04:32 +00:00
319d90099d Pass just the group to group_setactive instead of the screen and an
index which then needed to be found in a queue.
2014-09-27 18:57:11 +00:00
845e82015c Update _NET_CURRENT_DESKTOP with the screen's group_active->num. 2014-09-23 14:25:08 +00:00
cb65869d7a Move stuff that doesn't belong in group_init; while here, explicitly
initialize hideall and cycling.
2014-09-23 13:45:48 +00:00
2540b3f4fe cvsimport 2014-09-18 13:56:58 +00:00
cbc7f76074 Move motion time check to the top of each MotionNotify block (and
eliminate from ButtonRelease); further limits the amount of work done
outside the threshold, notably mousefunc_sweep_calc,
screen_find_xinerama and client_snapcalc.
2014-09-18 13:56:58 +00:00
8fd0f43ec2 these client actions are just toggles; less confusing with better names 2014-09-17 18:41:44 +00:00
458dd31b93 ewmh states _NET_WM_STATE_STICKY should not alter position 2014-09-17 18:09:30 +00:00
736d973f46 Use a similarly named check as sticky for hidden check in a group. 2014-09-17 16:32:53 +00:00
7eef4eb63d don't toggle _WM_STATE_HIDDEN here yet 2014-09-17 16:30:21 +00:00
a61812d52d Implement EWMH _NET_WM_STATE_HIDDEN. 2014-09-17 16:00:44 +00:00
74f4a1bad9 Introduce a check to see if a group holds only 'sticky' clients and use
this check to decide if a group is virtually empty.  Rationale: if a
group contains *only* 'sticky' clients, it should be skipped while
cycling through groups.  Apply similar logic to the group menu.

Based on an idea from phessler@, who also tested another version.
2014-09-17 14:31:37 +00:00
4b6dc96398 use similiar style for client flags 2014-09-15 13:00:49 +00:00
26b95de019 Remove incorrect cast in kbfunc_exec. In kbfunc_ssh, reverse logic on
truncation check so it's obvious.
2014-09-11 16:06:26 +00:00
d27fc99784 fold in 'active' into 'flags' 2014-09-10 20:30:38 +00:00
20c1113fdd move the check for an empty queue up during cycle 2014-09-08 21:24:27 +00:00
aac16013d2 name the group client queue appropriately, like other queues 2014-09-08 21:15:14 +00:00
b64ce8558c more style nits and wrapping 2014-09-08 20:37:02 +00:00
bc70374264 since mruq has been folded in, rename mru-named functions 2014-09-08 20:32:40 +00:00
26ba152692 Remove duplicate client queue (mruq); instead, remove and take the
global Clientq and place it inside screen_ctx since every client belongs
to a screen, then use the same per screen clientq to track stacking
order (the sole reason for mruq).
2014-09-08 20:11:22 +00:00
a7f3f29ea9 Now that a group knows its screen, only pass down the group_ctx. 2014-09-08 13:51:29 +00:00
5b46f0f7d8 Add screen_ctx to group_ctx, and populate on init. 2014-09-07 20:57:26 +00:00
3d12b6d1d9 more style nits 2014-09-07 19:27:30 +00:00
be091b3523 screen_fromroot -> screen_find 2014-09-07 17:38:38 +00:00
78e0f21225 Get rid of a redundant array of groups per screen; we already have a
link to the group queue for each screen.
2014-09-07 17:27:20 +00:00
9735927809 update.sh: fix mirror 2014-09-07 17:49:00 +02:00
9e560bd745 cvsimport 2014-09-07 15:47:44 +00:00
f7af0ebd72 Few style nits; extend 'path' to MAXPATHLEN. 2014-09-06 18:50:43 +00:00
977992626a generic sighandler 2014-09-06 16:24:32 +00:00
140f8b9eba style nit 2014-09-06 16:18:08 +00:00
5cd4cce3a0 Move termpath and lockpath into cmdq; side effect is that 'lock' and
'term' now show up in the application menu.
2014-09-06 16:14:35 +00:00
142f52a0aa Rework group names: stash the group name inside the group_ctx as opposed
to having to manage an array outside in screen_ctx for group names and
shortcuts.  Simplifies (and moves bits for) reading, and constructing
data for, EWMH's _NET_DESKTOP_NAMES.
2014-09-06 16:08:58 +00:00
87d604f119 bring menu bits closer together 2014-09-02 14:08:39 +00:00
231abb818a add explicit paran and drop some empty lines bringing like functions closer 2014-09-01 18:17:32 +00:00
becc7f260c Merge group_menu into mousefunc_menu_group. 2014-09-01 18:04:58 +00:00
3b99d53576 Since the flatting out of sc->group_names is only for setting
NET_DESKTOP_NAMES, merge the helper into xu_ewmh_net_desktop_names,
where we actually set the property.
2014-08-25 14:31:22 +00:00
7314a3aefd Implement _NET_WM_STATE_STICKY, bound to CM-s by default; allows any
client to 'stick' to all desktops (ewmh speak) or groups - this
currently has the same affect as setting a client's group to 'nogroup',
with the exception that the client can also be in a group, so when
un-sticking, the client will go back to its original group/desktop.
2014-08-25 12:49:19 +00:00
ed164794cb Continue effort to bring keyboard and mouse functions together; merge
name_to_kbfunc and name_to_mousefunc.
2014-08-24 15:49:58 +00:00
9f36d4af35 gc->hidden has never consistently kept track of a group's state;
group_show() and group_hide() are not the only ways a group can change
state - if all clients in a group are either hidden or unhidden, then
that group's state should change, as well as the various EWMH ways.
Instead of trying to keep track in a wide variety of places, simply
query the clients in a group before needing to take action based on the
group's state.  Solves long standing confusion of when a group is hidden
or not.
2014-08-24 15:37:45 +00:00
fe533fdc8e Fix nogroup regression, where nogroup became an actual group - the
symantics between cwm groups and ewmh got in the way.  Ensure a client
that wants to be in nogroup stays in nogroup (thus stays in view), even
when (re)reading NET_WM_DESKTOP.  Paritially reverts patchset 644
(2014-02-07 13:09 PST) which deals with a NULL cc->group.  All to be
revisited when NET_WM_STATE_STICKY hits cwm.

Reported by many; testing and ok phessler.
2014-08-22 19:04:00 +00:00
b31b09dfc2 Purely mechanical; unify 'num', 'no' and 'shortcut'. 2014-08-20 15:15:29 +00:00
30da2211dd Split off group window restacking. 2014-08-20 13:42:27 +00:00
7f45471538 whitespace 2014-08-20 12:35:39 +00:00
5d22b261a6 fix a misleading comment and function name 2014-08-20 12:33:12 +00:00
6c835fd585 Remove unnecessary memset since reload was replaced with restart;
from Kent Spillner.
2014-08-19 18:39:41 +00:00
01cca3c114 Pull highstack from group_ctx (and useless calculations of); in the one
place that we use highstack, replace that usage with a local variable
(for now until stacking is done properly).
2014-08-19 12:47:51 +00:00
1192007171 Get rid of nhidden in group_ctx; it actually never reported correctly
since nhidden wasn't incremented nor decremeted in all the right places,
thus confusing matters.  We don't need to carry a count around, so just
use a local variable in the one place we need one to supply
XRestackWindows().
2014-08-18 13:57:57 +00:00
870418ff68 README: mention OS X
Thanks to Alexis Hildebrandt for testing.
2014-06-23 16:40:30 +02:00
c7f481e6fa Don't use included strlcpy/strlcat when already defined as macros
OS X 10.9 with Fortify has conflicting prototypes:

In file included from calmwm.c:36:
./calmwm.h:28:8: error: conflicting types for '__builtin___strlcat_chk' /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/secure/_string.h:111:3:
note:
      expanded from macro 'strlcat'
  __builtin___strlcat_chk (dest, src, len, __darwin_obsz (dest))
2014-06-22 22:50:03 +02:00
e9cb5b743f plug memleak (currently unused func); from Tiago Cunha 2014-04-21 12:52:14 +00:00
a483e23e94 cvsimport 2014-04-21 12:52:14 +00:00
1101877305 Make PREFIX, MANPATH and CFLAGS optional variables
This change will make it possible to override PREFIX, MANPATH and
CFLAGS using environment variables. This makes the Makefile a little
more flexible, benefiting both endusers and those porting to package
systems like FreeBSD Ports and pkgsrc.

By braakvaak@aol.com.
2014-03-21 18:47:05 +01:00
4935bb73c0 builds on FreeBSD too 2014-03-14 16:43:40 +01:00
625be76fb9 Makefile: add sign action 2014-03-13 19:24:15 +01:00
2174a1960d Version 5.5 2014-03-13 19:19:19 +01:00
d46b0f8848 Alignment fix; found the hard way on sparc64 by landry@ (and thanks too
for fast access to a box!)

ok deraadt@, matthieu@
2014-02-27 00:52:57 +00:00
7c9565ec41 cvsimport 2014-02-27 00:52:57 +00:00
96af45583a remove TAILQ_END and __dead hacks 2014-02-23 15:49:58 +01:00
6798e3dde3 Make sure we cycle through existing client's group membership and set
the hidden state on the group; important to know group hidden state
after a re-exec or restart of cwm (as easily seen in group menu).
2014-02-08 02:49:30 +00:00
8ddc90665e cvsimport 2014-02-08 02:49:30 +00:00
1208225fbd Replace a few hand rolled loops with like tailq loops. 2014-02-08 02:40:43 +00:00
2a87320bbf all mapped clients now should have a group, so simplify some cases 2014-02-07 21:59:56 +00:00
3cb81ebeea If _NET_WM_DESKTOP is set to -1 during client creation, place the client into
group 0 (nogroup); solves problem initially discovered by oga@nicotinebsd with
tint2.  A clientmessage *after* client creation already handles this case.

Go further and assign every client to a group; in non-sticky mode, group 0
(nogroup) and sticky mode, the active group.  In both cases, autogroup will
override the group assignment.  Removing a group from a client always places
the client back into group 0 (nogroup).  Autogroup can also assign a client to
group 0 (nogroup) to keep a client always visible (unless of course one opts to
hide all clients).
2014-02-07 18:09:54 +00:00
98d8483d35 Some clients set the urgency flag even if they are the active client;
prevent annoying behavior by only setting the cwm urgency flag if the client
is not active; diff from Thomas Adam.
2014-02-06 20:58:46 +00:00
8653141b4b cvsimport 2014-02-03 21:07:47 +00:00
f793964c9e Introduce a region queue and replace screen's XineramaScreenInfo; we
still use Xinerama to populate the regions per screen, but will switch
at a more appropriate time.
2014-02-03 21:07:47 +00:00
8a6dd51fac make this XQueryTree like the other 2014-02-03 20:29:05 +00:00
ad96c16838 Move redundant bits from screen_init (while dealing with existing
clients) directly into client_init, performing the X roundtrip only
once. With the previous change in maprequest, this moves decision making
into one place for creating new clients.
2014-02-03 20:20:39 +00:00
b923524a8e cvsimport 2014-02-02 21:34:05 +00:00
ad76995af7 Move redundant window attr fetch from maprequest directly into
client_init and perform that X roundtrip only once.
2014-02-02 21:34:05 +00:00
ad1b78c6d1 No need to store screen colormap and visual; rather just use the X
macros in the one place they are needed.
2014-02-02 16:29:04 +00:00
5f9c7a21c7 Unlike free(3), XFree(3) can't be passed a NULL pointer. 2014-02-02 16:13:50 +00:00
0a71742af4 The menu already limits entries with MENU_MAXENTRY, so don't bother
holding a command name limit as well.
2014-02-02 15:46:05 +00:00
fd827fd757 Take menu's bwidth into account for position/size; reported by Thomas Adam. 2014-02-01 19:28:46 +00:00
c7d4add2f2 cvsimport 2014-02-01 00:25:04 +00:00
8b63ac62c2 Let the config parser continue parsing even after encountering an error;
original idea (with now-outdated patch) from Thomas Adam.  Since we now
report where errors exist, this now makes more sense.  Expand a bit on
config file parsing in the cwm(1).

Discussed with a few, including sthen; ok sthen.
2014-02-01 00:25:04 +00:00
24f9bfb3ec cvsimport 2014-01-30 22:41:16 +00:00
34477b8a35 re-add lost free from previous 2014-01-30 22:41:16 +00:00
cc1902f57c Switch ignoreq to winname struct since it's basically the same thing;
removes limit on name matching.
2014-01-30 22:17:22 +00:00
7928c1ad7c use the same autogroupwin variable as everywhere else 2014-01-30 15:43:53 +00:00
fdefcecfa0 Move cwm_status around to avoid header fu, and remove extraneous signal
header from xevents.c; noticed by Christian Neukirchen.
2014-01-30 15:41:11 +00:00
7b5b7cc9e9 Remove duplicate mouse functions and instead use the kbfunc ones. No
user visable changes at this point, but they'll merge at an appropriate
time.
2014-01-30 14:40:21 +00:00
1fd3fc4997 cvsimport 2014-01-29 22:30:00 +00:00
2be890489b Minimize trivial differences between a few kb and mb functions. 2014-01-29 22:30:00 +00:00
59fe14bd2f keybinding -> key binding 2014-01-29 21:17:33 +00:00
db0b2fde5c Merge keybinding and mousebinding queues into using the same merged
struct, binding; they were essentially the same accept for what was
'pressed', keysym or button.
2014-01-29 21:13:52 +00:00
4438970b64 Much like we do for keyboard and mouse bindings, remove duplicates for
command name - last match.
2014-01-29 18:43:27 +00:00
1f8f19b4d5 Check command name/path for truncation and provide user feedback during
config parse (and use conf_cmd_add to populate defaults); based on a
discussion with Tiago Cunha.  While this looks ugly, there are likely
some other changes here to come.
2014-01-29 18:34:22 +00:00
2b233f0548 cvsimport 2014-01-28 20:22:21 +00:00
c28467cda5 Check ignore windowname for truncation and provide user feedback during
config parse; based on a discussion with Tiago Cunha.
2014-01-28 20:22:21 +00:00
df15337a9f The EWMH spec states that if the cardinal returned is 0xFFFFFFFF (-1)
then the window should appear on all desktops, which in our case is
assigned to group 0.  Found to fix stalonetray due to the non-ewmh aware
range checking in group_movetogroup(); from Thomas Adam.
2014-01-28 13:40:40 +00:00
51b3fbee52 Move conf_init/clear into main - no behaviour change; from Tiago Cunha. 2014-01-28 00:42:20 +00:00
0608610cc7 move some init up and shed some blank lines 2014-01-27 15:13:09 +00:00
469db5f371 simplify parse_config using assignment; inspired by a very old diff from
Tiago Cunha.
2014-01-27 14:49:40 +00:00
5fd119daea cvsimport 2014-01-24 22:38:02 +00:00
6f10349843 more range checking 2014-01-24 22:38:02 +00:00
c7e2ee5dd5 correct arguments and drop the cast.
sanity check by oga@nicotinebsd
2014-01-24 15:08:06 +00:00
53e77acafc no need to quote within warning 2014-01-23 17:13:38 +00:00
79248a385b If no title is supplied, term uses only the basename for its title.
This is useless when searching for windows launched via the ssh command
menu; supply a more useful title: '[ssh] <hostname>'.

Idea from todd@, ok todd@
2014-01-23 16:51:28 +00:00
3d2eec293a zap whitespace 2014-01-22 22:41:09 +00:00
978a5767ec cwm_argv doesn't need to be global any longer 2014-01-22 22:26:05 +00:00
5121ea5e10 start properly releasing X resources during teardown 2014-01-22 22:14:02 +00:00
77aa5ac45b Somewhat streamline event loop/restart/quit handling; most notable
change allows a restart to trigger proper teardown first, even though
teardown is not (yet) complete.

After some discussion with oga@nicotinebsd.org regarding a more
complicated version/idea.
2014-01-22 21:48:27 +00:00
371902b3c9 cvsimport 2014-01-21 15:42:44 +00:00
ac3162439a Sprinkle a few more const; from Tiago Cunha. 2014-01-21 15:42:44 +00:00
83f9ef884a Save the ptr position before lowering via kbd, so as to be able to cycle
back with the pointer in the right place; matches behaviour when
lowering via the mouse function.
2014-01-20 23:18:47 +00:00
c7adadaf9d merge KBFLAG_NEEDCLIENT and MOUSEBIND_CTX_*; brings kbfunc and mousefunc
bits even closer.
2014-01-20 23:03:51 +00:00
34f43e3f2d Use argument to pass down flags for mousefunc cyclegroup; removes
rcyclegroup wrapper need - now similar to kbfunc.
2014-01-20 22:31:53 +00:00
d91571c567 constify and rename some confusing variables around cmdq. 2014-01-20 21:34:32 +00:00
7263fb4c84 - remove redundant range check for buttons in conf_bind_mouse.
- make conf_bind_kbd return error on non-matches to match what
   conf_bind_mouse does.
 - rename some variables while here for clarity.
 - constify bind and cmd.

from Tiago Cunha.
2014-01-20 19:06:04 +00:00
720b5452aa Add a function that adds an entry to a menuq, normalizing a common code
path; from Tiago Cunha.
2014-01-20 18:58:03 +00:00
86b149ad25 cvsimport 2014-01-03 15:29:06 +00:00
43ccf4eae0 use consistent types 2014-01-03 15:29:06 +00:00
f4c289b9e6 cvsimport 2014-01-03 14:23:50 +00:00
08342471c8 state is long 2014-01-03 14:23:50 +00:00
2843ba1e28 calculate proper menu width/height on the first Expose event; 'jump' (while using gap) noticed by Christian Neukirchen 2014-01-02 22:49:10 +00:00
376e98c2c8 Accept _NET_CURRENT_DESKTOP ClientMessage; from Thomas Adam, though
instead of group toggle, group only.
2014-01-02 22:43:55 +00:00
5a5e7441e1 Accept _NET_WM_DESKTOP clientmessage; from Thomas Adam 2014-01-02 22:26:27 +00:00
10d4dcb127 bring mousefunc closer to kbfunc 2014-01-02 21:30:20 +00:00
c4f6134ebe gc keycode in keybinding since we only deal with keysym now 2014-01-02 21:17:23 +00:00
8420398068 rename for clarity 2014-01-02 21:15:39 +00:00
46b1d6ef1e When a client doesn't specify size hints, nothing prevents a resize to
0x0 - don't allow this situation during mouse resize (check already in
place for kbd resize).

Reported by brynet@
2014-01-02 20:58:20 +00:00
b387351df1 cvsimport 2013-12-17 16:12:18 +00:00
291e0dc088 add update.sh which I use for importing from CVS 2013-12-17 18:34:04 +01:00
9b8dc1ba7f missing stdarg.h include 2013-12-17 16:12:18 +00:00
da2bea3ffa replace with memset 2013-12-17 16:10:43 +00:00
44e2a2505b cvsimport 2013-12-16 19:02:17 +00:00
19fc7f666b Implement support for EWMH's _NET_WM_STATE_FULLSCREEN hint.
Since we already have a form of 'maximize', we need to differentiate
between 'maximize' and the new 'fullscreen' mode.  The 'maximize' mode
will continue to honor gap but now *retains* the border, matching the
'vert/horz maximize' behaviour.  The new 'fullscreen' mode supports and
follows the _NET_WM_STATE_FULLSCREEN hint, allowing the client perform
additional window modifications; in this mode, cwm(1) will *ignore* gap,
remove borders and freeze(move/resize) the client.  Additionally,
'fullscreen' mode will remember various combinations of previous states.

* default keybinding changes: CM-f 'fullscreen', CM-m 'maximize' (re-map
  as desired).

Positive feedback from a few, testing and ok sthen@
2013-12-16 19:02:17 +00:00
75b69c0b04 cvsimport 2013-12-13 22:39:13 +00:00
f98f4615c0 Teach screen_find_xinerama() to apply gap only when told to do so;
adjust callers.  Needed for an upcoming feature.
2013-12-13 22:39:13 +00:00
5732b65139 Instead of using work area, use the Xinerama area for snap calculations;
based on a patch from Thomas Adam with appropriate adjustments for gap.
2013-12-13 21:51:56 +00:00
ddb67559f6 cvsimport 2013-12-13 15:56:44 +00:00
91a29396e8 we need the save-set when re-exec'ing so as to not lose State on our hidden clients 2013-12-13 15:56:44 +00:00
40858be0ae stray space and sort while here 2013-12-13 14:45:47 +00:00
34ae428cec Add support for XUrgency and matching _NET_WM_STATE_DEMANDS_ATTENTION
ewmh hint; urgencyborder is configurable.  The urgency flag will stick,
even while on a client in a non-viewable group, until the client
receives focus (where the border is reset).  Initial diff from Thomas
Adam with some changes/enhancements from me.
2013-12-13 14:40:52 +00:00
0cad4ef6e0 cvsimport 2013-12-13 12:57:57 +00:00
fe177b7c61 Make sure we really take work area gap into account with snap calculations;
from Dominik Honnef via Christian Neukirchen.
2013-12-12 21:50:50 +00:00
b276a2ef00 ICCCM explicitly states that server time (CurrentTime) should *not* be
used for focus events, but rather the timestamp of the generated event.
Track the last event timestamp and send it down for a WM_TAKE_FOCUS
ClientMessage.  I suspect we should do this for clients that don't
announce this Atom as well, though the raciness gets us into a bind.

Solves focus order issue since WM_TAKE_FOCUS; fix verified by sthen@

ok sthen@
2013-12-12 20:15:07 +00:00
140028e905 cvsimport 2013-12-11 22:14:23 +00:00
e767ac9c65 we don't need to manage the save-set since we're not reparenting; left-over from pwin 2013-12-11 22:14:23 +00:00
09e07ddaaf since we are drawing in unhide, no need to during client setup 2013-12-11 17:23:31 +00:00
68f365cddb apply mwm hints later 2013-12-11 15:46:47 +00:00
7e0749b0b1 Add client wrapper for XWMHints to support XA_WM_HINTS in PropertyNotify
events; based off a diff from Thomas Adam.
2013-12-11 15:41:11 +00:00
0d9b1becff Remove extra work and simplify client state handling. 2013-12-11 14:16:09 +00:00
23a1abdd8a Stash Class and WM Hints in client_ctx 2013-12-11 14:09:21 +00:00
1d68f0683a Redraw client border when unhiding; during a hide, we just unset
the active flag but never redraw since it'll be in IconicState.

Behaviour reported by sthen@
2013-12-10 21:27:37 +00:00
2951979832 cvsimport 2013-12-08 13:51:38 +00:00
7fad5224d4 If not using sticky mode (the default), clients aren't automagically
assigned a group, thus cc->group will be NULL - fix the client group
shortcut in menu lists; crash reported by Christian Neukirchen.
2013-12-08 13:51:38 +00:00
27513daee2 Revert "use u_char for buffers in yylex"
This reverts commit 03b19f1487.

This merely generates pointer signedness warnings for strtonum() and
lookup() and problems with the const char * prototype of xstrdup().
2013-12-06 23:16:54 +01:00
5dc65d9242 When we receive a NotionNotify event, there's no need to (incorrectly
might I add) redraw the top menu selection.
2013-12-02 20:01:19 +00:00
873763f4c6 cvsimport 2013-12-02 20:01:19 +00:00
161ed1801a Always highlight the first menu item (helpful to see selected items when
not using a mouse and therefore not generating Expose/MotionNotify
events); from Thomas Adam.
2013-12-02 19:49:26 +00:00
d1b232fe81 Prepend the group shortcut in the client search menu; from Thomas Adam.
Likewise, prepend shortcut in unhide menu.
2013-12-02 19:30:27 +00:00
6b72168d92 Since we use IconicState as our hidden state, check for the hidden flag
and unmanage the client if we're not hidden (basically if NormalState)
during an UnmapNotify event.

Resolves an issue with mplayer going fullscreen while not using NetWM
hints; behaviour regression reported by Ido Admon.
2013-12-02 14:30:12 +00:00
5e67a12262 like gap, make snapdist per screen 2013-11-27 18:34:34 +00:00
1b6ef8e9ed alter -r1.145 getsizehints to deal with clients that don't have
WM_NORMAL_HINTS.
2013-11-27 17:04:35 +00:00
abf52049b9 Remove the option to bind a key by keycode with brackets; it never
worked (and no one complained!).  While it's fairly easy to fix, users
should be using keysym names and not keycodes.

Discussed at length months ago with todd@, matthieu@ and Owain.
2013-11-27 16:24:17 +00:00
250f98bf15 simplify/unfold 2013-11-27 14:20:32 +00:00
fa25915a75 Rewrite active/inactive client handling in client_setactive();
client_leave() served no real purpose, likewise no reason to handle
LeaveNotify events since an EnterNotify will process the next active
client (and we don't have anything important to process anyway), so
xev_handle_leavenotify() goes as well.  Allows a simplification of
client_mtf() and client_cycle_leave() for clarity.  While here, unify a
few client_current() checks.

No intended behaviour change.
2013-11-27 00:01:23 +00:00
03b19f1487 Log message:
use u_char for buffers in yylex, for ctype calls, as done in all other
parse.y in the tree
found by millert@, ok okan@
2013-11-25 18:21:55 +00:00
3c78191e2e cvsimport 2013-11-12 21:25:00 +00:00
2937b2066f Alter the r1.35 of event.c race fix. Remove the forward looking event
queue check (removing the need for a server grab/ungrab) - if the client
is going away, let it fall all the way through to a DestroyNotify event.
There's no longer a need for us to manually destroy a client ourselves
(removing yet another server grab/ungrab).  Instead, when the
UnmapNotify event is synthetic, simply set the state to Withdrawn (as
per ICCCM), else Iconic (in our case 'hidden').

Verified with test case from the 2009 race which was the original reason
for r1.35 of event.c.
2013-11-12 21:25:00 +00:00
bda68b0924 Put back the border draw call in client_resize; since we are adding and
removing the border on maximized clients we need to redraw.

Also noticed by Tim van der Molen
2013-11-11 12:51:15 +00:00
3bb928a1c2 stash WMProtocols in flags 2013-11-08 17:35:12 +00:00
c1bc6d37b3 quick keyboard focus fix for clients that neither populate wmhints nor wmprotocols, like rdesktop; focus needs to be re-visited 2013-11-05 00:55:42 +00:00
2037332bb7 cvsimport 2013-11-02 19:13:56 +00:00
b5915eb989 The only reason we need to keep XSizeHints in our client_ctx is for
flags, so just add one to hints; consolidates sizehints and shrinks.
Additionally don't abuse PSize for XGetWMNormalHints() failures.
2013-11-02 19:13:56 +00:00
b8f53666bd x/y from XSizeHints are obsolete (and have been for a long time), so
instead use x/y from XWindowAttributes when USPosition|PPosition are
set.
2013-11-01 21:54:20 +00:00
caf8b315ab cvsimport 2013-11-01 14:07:19 +00:00
06eb13dfd9 re-add support for WM_TAKE_FOCUS, and additionally this time only call
XSetInputFocus() for clients that have the InputHint; latter fix
discovered by Valery Masiutsin with a PoC patch - solves keyboard input
focus loss for java apps.
2013-11-01 14:07:19 +00:00
0c99788f66 cvsimport 2013-10-25 19:46:16 +00:00
6e17b41f56 A client_delete should behave differently depending on whether the
triggering event was unmap (with pending destroy) log destroy; we only
need to grab/ungrab the server lock, set WithdrawnState and
XRemoveFromSaveSet clients coming from an unmap event - doing so for
clients coming from destroy are already gone, hence we generate errors.
2013-10-25 19:46:16 +00:00
6867821abe shuffle 2013-10-20 02:00:02 +00:00
5b733a152d wrap key ungrab like btn ungrab, for it'll be used again 2013-10-20 01:55:32 +00:00
9ff288baef revert 1.138 (WM_TAKE_FOCUS) for now 2013-10-20 01:35:47 +00:00
a2013ee9dd Using xu_btn_ungrab() buttons during client_leave doesn't work (error
BadValue) when the modifier is already AnyModifier .  Instead alter
xu_btn_ungrab() to ungrab AnyButton/AnyModifier and call it only when a
client is coming into focus in client_setactive(), instead of iterating
over ignore mods - matches how we handle key grabs.
2013-10-19 19:39:34 +00:00
083a023f2c I believe we redraw the border in too many cases; likely a leftover from
the cc->pwin days - don't redraw on every unhide, resize, and mouse
move/resize Expose event (note, all Expose events trigger a redraw
anyway).

Tested with some finicky X apps I could think of, though I'm sure others
will find more - if so, and they 'lose' the border, please report!
2013-10-19 18:59:22 +00:00
a70b2d81af For clients that support WM_TAKE_FOCUS in their WM_PROTOCOLS property, send
a ClientMessage event.
2013-10-19 00:24:54 +00:00
a88b8c1363 a few err->errx since we don't have error messages here; from Tiago Cunha 2013-10-17 13:59:10 +00:00
0d6e8a9146 Makefile: rm -f is enough 2013-10-16 23:05:51 +02:00
bfd3f5e5e1 trying parsing a XLFD string first, then by pattern 2013-10-07 13:40:26 +00:00
f3477539bb cvsimport 2013-10-07 13:40:26 +00:00
6ac51b8e41 unify type; no change 2013-10-03 13:51:57 +00:00
0dec9e849d cvsimport 2013-07-16 14:30:48 +00:00
a439012b58 we no longer need to construct an array for _NET_SUPPORTED. 2013-07-16 14:30:48 +00:00
fbb9d32d0a don't need the size here 2013-07-16 14:22:25 +00:00
ca416b6b82 max -> nitems 2013-07-16 14:04:44 +00:00
ae4ab06559 shuffle down protoypes 2013-07-15 23:53:19 +00:00
4ef96a2772 collapse lines 2013-07-15 23:51:59 +00:00
4119b5c065 simplify atom handling; allows us to limit to one round-trip to server
for gathering Atoms.
2013-07-15 14:50:44 +00:00
93bfc4a5c1 cvsimport 2013-07-10 14:15:58 +00:00
bd7b8163bb bring buttonpress and keypress event handlers slightly closer together 2013-07-10 14:15:58 +00:00
9571038358 type fixes 2013-07-10 14:11:42 +00:00
dd10412804 since the root and event window are the same in the case of a button
event on the screen's root window, there's no need to pass down the
entire XButtonEvent event, at least to group_menu(), the only callback
which takes an argument at this point; instead use the already populated
screen.
2013-07-09 01:24:49 +00:00
9bf2498d7e add support for mouse based group {,r}cycle; from Rodrigo Mosconi. not
bound by default.
2013-07-08 18:39:20 +00:00
1f244fe29c move kbfunc and mousefunc closer together 2013-07-08 18:19:22 +00:00
a493734066 clarify kbd vs mouse functions 2013-07-08 16:32:51 +00:00
912ec90ea1 move duplicate kbd and mouse modifier parsing to a generic function;
from Tiago Cunha
2013-07-08 16:10:55 +00:00
fe439f2b96 replace a few (x)malloc with (x)calloc to prevent potential integer
overflows; from Tiago Cunha
2013-07-08 15:48:16 +00:00
7a7e5cd592 whitespace and style fixes; from Tiago Cunha and one from me. 2013-07-08 15:46:16 +00:00
ba9baaf671 cvsimport 2013-06-23 17:57:50 +00:00
fbcdce6be9 properly fix (and re-organize) selfont selection. 2013-06-23 17:57:50 +00:00
9a12ca3520 cvsimport 2013-06-20 02:33:57 +00:00
7004700ce0 when selfont is configured, make sure we continue and configure the rest
of the screen (quick fix); discovered the hard way by Rodrigo Mosconi.
2013-06-20 02:33:57 +00:00
234b8214df move Cursors into conf. 2013-06-17 17:11:10 +00:00
23d84adb5e now that we have conf_screen, which configures individual screens
*after* config parsing, we no longer need to split up display/screen
initialization, so collapse.
2013-06-17 14:08:51 +00:00
8b00f438be allow mouse button4 and button5; from Rodrigo Mosconi 2013-06-17 00:57:47 +00:00
6059073a5f move synthetic responses and have client_msg only work with WM_PROTOCOLS,
since that's all ClientMessageEvent is for anyway.
2013-06-10 21:37:30 +00:00
b365ceac0e Check for, and honour, CWStackMode and CWSibling change requests during a
ConfigureRequest event.  Additionally, honour a border width change;
detailed report and patch from Mike Small.
2013-06-10 20:11:43 +00:00
c30653b1c3 cvsimport 2013-06-03 20:33:17 +00:00
09f3275561 Unlike other parse.y variants, cwm's allowed 'command xx /path/to/xx' without
quotes. Reinstate this support lost in recent sync diff and add a comment to
show that it's intentional. ok okan@
2013-06-03 20:33:17 +00:00
5515a365ba cvsimport 2013-05-29 22:41:51 +02:00
692d341dfc fix type in a ClientMessage (xu_sendmsg). 2013-05-27 23:20:45 +00:00
be1a7a3f5c alter conf_grab(_kbd) to first ungrab AnyKey/AnyModifier, then proceed
to grab keys in keybindingq.  we don't need to ungrab/grab on every
addition to the queue, just once with a complete keybindingq; simplify
grabbing keys per screen (during init) and during a MappingNotify.

while here, change conf_grab_{kbd,mouse} to require only a Window.
2013-05-23 16:52:39 +00:00
7cc1c7344f since we don't have any screens yet, there's no reason to conf_{,un}grab
here; just build keybindingq.
2013-05-22 20:23:21 +00:00
d1eadee922 get rid of long standing XXX: now that we configure screens based on
config options, add the keybinding GrabKey calls here
2013-05-22 16:54:09 +00:00
532f132194 move validation of pointer Button into conf_mousebind so we check
validity during the parse phase and not bother adding it to the queue,
instead of each time a client needs to grab (when it's too late);
furthermore, make this a config error, stop parsing and load the
default config.
2013-05-22 16:32:15 +00:00
866f5af9c7 sync with src changes 2013-05-22 13:02:14 +00:00
421bf22ac7 handle _NET_WM_STATE ClientMessage; from Alexander Polakov. 2013-05-21 00:29:20 +00:00
c2042cd523 stray whitespace 2013-05-20 21:32:00 +00:00
ad8020e17d handle _NET_ACTIVE_WINDOW ClientMessage; from Alexander Polakov. 2013-05-20 21:19:15 +00:00
284af4ca0c add support for _NET_WM_STATE_MAXIMIZED_{HORZ,VERT}; from Alexander Polakov.
while I'm unsure of the final look of _NET_WM_STATE, there's no reason
to delay this support.
2013-05-20 21:13:58 +00:00
270df196ac - configure menuwin with the screen, then create the xft drawable using
the menu window since that's the only place on which we draw
- elminate the need to change the drawable on every font draw
2013-05-20 20:21:04 +00:00
e14123210a no reason to pass around *sc when it's already in menu_ctx. 2013-05-20 20:04:36 +00:00
5b4c55616a move the 2 small font helper functions to xutil.c 2013-05-19 23:38:20 +00:00
e41c84c752 move the rest of xft init into screen_conf, since most of it is based on
config parameters.
2013-05-19 23:16:29 +00:00
dac00a232f - switch border colors to Xft
- merge border/menu color structures/functions since they now both use Xft
- switch xu_xorcolor to operating on XftColor instead of just
  XRenderColor (basically adding pixel)
- if color name allocation fails, revert back to default (this, along
  with font validation should occur during config parse, but we don't
  have screens setup yet - likely to change at some point)
2013-05-19 23:09:59 +00:00
c84145661e add support for _NET_CLOSE_WINDOW 2013-05-19 17:05:52 +00:00
556a50d8f7 treat WM_CHANGE_STATE like other atoms 2013-05-19 17:03:55 +00:00
01bfdd7b42 use XGetWMProtocols and simplify WM_PROTOCOL handling 2013-05-19 17:02:04 +00:00
3253f5a4a1 simplify 2013-05-19 17:01:29 +00:00
be72620432 if -> ifdef 2013-05-14 13:39:53 +00:00
c637a54048 - let callers of font_draw figure out (and pass) the color instead of an
'active' flag.
- use strlen() inside of font_draw; the only instance where it wasn't
  used happened to be ignored on a subsequent draw.
2013-05-14 12:35:56 +00:00
112f616d1d swap x/y calculations in kbd move/resize to match those in the respective mouse functions 2013-05-11 22:03:17 +00:00
2b926784ba new -> init 2013-05-11 22:01:07 +00:00
3a38686137 replace conf_{gap,color,font} with conf_screen since really we are
configuring the screen *after* parsing, just as we do a conf_client on
client manage.
2013-05-11 21:46:27 +00:00
9b9e95d742 more type fixes for mask/button 2013-05-10 16:32:47 +00:00
d0fe80b1f4 spacing 2013-05-10 16:10:40 +00:00
e05c0a2c60 int type fixes 2013-05-10 16:05:34 +00:00
457938fbd7 fix KeySym type 2013-05-10 15:44:43 +00:00
5f06e5a8ca border width/color makes sense for some, so put it back 2013-05-07 01:35:41 +00:00
1a06f09fa0 negative values for borderwith, moveamount, snapdist and gap are
configuration errors, so warn and load defaults.
2013-05-06 19:09:19 +00:00
1db7cc2a18 remove group in client_delete directly. 2013-05-06 16:03:11 +00:00
27854d9061 border width/color makes no sense on menuwin 2013-05-06 16:00:34 +00:00
e91c716262 - no need to position and size the menu window before an Expose event
since we'll be calculating required size later anyway; allows us to
  consolidate all prompt/display/search string building goop into
  menu_draw.
- reset the pos/size of menuwin when leaving a menu.
- reverse the 'prompt' NULL check to be consistent with 'initial' one
  and fix a whitespace nit while here.
2013-05-02 20:18:35 +00:00
0d209028be zap leftover debug printf 2013-05-02 19:41:52 +00:00
9de81f3d2e get rid of cc->name in the resize box and make dimensions more readable. 2013-05-02 19:33:17 +00:00
bb56bf4c4b only redraw the name/size box when the client resizes, not every time
there's movement; should slightly help with resize syncs.
2013-05-02 19:30:10 +00:00
5acf6c147f no need for font_{ascent,descent,height} wrappers; limit font_width to
just requiring xftfont.
2013-05-02 17:25:15 +00:00
843f18aaee cvsimport 2013-04-30 21:12:20 +00:00
5ab3b373d3 group conf_* init functions 2013-04-30 21:12:20 +00:00
ff6f5301f0 missing proto 2013-04-30 21:11:07 +00:00
6e8f1f2955 type fix 2013-04-30 21:10:23 +00:00
94e341725b use an int in screen_init and avoid needing to cast for screen number (which). 2013-04-29 00:56:47 +00:00
006a29e617 mechanical xu_{get,set}state -> xu_{get,set}_wm_state change 2013-04-17 13:57:06 +00:00
302690624e slightly rework WM_STATE set/get to make it less ambigious; will be more clear
on what needs to change to make it right in the end.
2013-04-17 13:52:20 +00:00
1dbcc394ae zap extra space 2013-04-17 13:31:47 +00:00
a899d267fe add conf_ignore and move group_make_autogroup to conf_autogroup to match. 2013-04-17 13:30:38 +00:00
6f185bb03c cvsimport 2013-04-14 16:13:17 +00:00
16ed8bf8e4 only a window is required to set WM_STATE. also un-confuse xu_ptr_getpos
by using 'win' instead of 'rootwin' so as not to imply only the root
window is queried, rather any window.
2013-04-14 16:13:17 +00:00
f3dfc4968c we handle WM_STATE here, so remove misleading comment. 2013-04-12 20:54:27 +00:00
ef1b78f464 we already set window state in client_hide or client_unhide right before, so
there's no need to do it again.
2013-04-12 20:45:57 +00:00
b5f6bd205c makes no sense to set an error handler which uses X_Dpy before XOpenDisplay. 2013-04-12 14:49:16 +00:00
6296efadaa push Screenq into screen_init 2013-04-12 14:46:30 +00:00
90f95416c6 _NET_WM_NAME is UTF8_STRING type 2013-04-10 19:08:09 +00:00
58c1d48d10 plug memleak; always need to menuq_clear even when a selection is made. 2013-04-08 15:43:04 +00:00
62a685d82a reverse logic to make it like the others 2013-04-08 13:05:27 +00:00
28b54db221 consistency 2013-04-08 13:02:31 +00:00
8c47a12a86 missing prototype 2013-04-08 00:56:21 +00:00
a957ed7c7d cvsimport 2013-04-05 17:36:02 +00:00
f18d2c94c2 add missing proto; replace magic number 2013-04-05 17:36:02 +00:00
670207f79b zap stray whitespace 2013-04-05 17:07:25 +00:00
c5a76f1d5b - no reason to breakout ClassHints work, so fold into client_{new,delete},
but keep _MOTIF_WM_HINTS separate.
- simplify fetching app/class hint.
- fix _MOTIF_WM_HINTS Atom type.
2013-04-03 20:22:55 +00:00
9cad4c73b3 honor PATH search order for exec; from Andres Perera. 2013-04-03 19:28:00 +00:00
ec4474a33a move XUngrabServer to the end of client_new() to avoid races where clients,
such as those using sdl, attempt to manage the clients themselves when the
clients aren't fully ready.  other wm's grab the xserver during the whole
client setup process, so match.

behavior found by jsg.
2013-04-03 19:20:50 +00:00
cfc19dba7e cvsimport 2013-03-09 21:55:56 +00:00
fbb0df4155 replace handrolled for loop with TAILQ_FOREACH; from andres.p@zoho.com 2013-03-09 21:55:56 +00:00
9c6226faa8 cvsimport 2013-01-13 13:55:12 +00:00
47aa485fa2 put back r1.68 which allows an empty group to be sticky; behavior
change noticed by Thomas Pfaff and diagnosis why we need to
group_setactive in this case by Alexander Polakov.  replace XXX with
a useful comment.
2013-01-13 13:55:12 +00:00
62acbee4b3 set the initial group to '1', missed by recent off-by-one group
numbering re-work; discovered the hard way by sthen@.

ok sthen@
2013-01-10 15:28:11 +00:00
3a7596968b add per-group vert/horiz tiling support; introduces 2 new bind commands,
'vtile' and 'htile'; from Alexander Polakov.
2013-01-08 15:16:04 +00:00
bf9d981597 teach screen_find_xinerama() about gap and adjust (simplify) callers;
menu becomes gap-aware for free.
2013-01-08 04:12:51 +00:00
e7b85cfb2f fix menu/client placement in panning setups; XineramaQueryScreens gives
us the width of the psuedo screen, but here we need the edge instead
(xmax/ymax); just re-use w/h here for now.
2013-01-07 21:53:23 +00:00
4ffe56b9a3 revert previous 2013-01-07 21:45:24 +00:00
7c4ed94757 unbreak xinerama support from r1.41 for panning setups 2013-01-07 20:32:19 +00:00
104c7d5de3 cvsimport 2013-01-06 01:01:26 +00:00
c426254da5 use cc->bwidth in client_vmax since we've yet to reset it (matches
client_hmax); from Jan Stary
2013-01-06 01:01:26 +00:00
6d123cd96d rename {h,v}max functions for consistency; from Jan Stary 2013-01-04 16:30:03 +00:00
579f981718 really these are just border colors, so adjust the define 2013-01-04 16:27:58 +00:00
96adffcd9d spacing 2013-01-04 16:23:51 +00:00
698530155d get rid of struct color 2013-01-04 16:23:04 +00:00
c139df129c pass the screen workarea, as opposed to viewarea, allowing client
snapping to honor gap.
2013-01-02 21:41:14 +00:00
2ac65bd288 re-work client_snapcalc() so it takes client and edge dimensions with
snapdist; allows for simplier snap calculations.

required for an upcoming diff for honoring gap.
2013-01-02 21:37:21 +00:00
6e5dda99a6 merge in Xinerama screen query; no functional change. 2013-01-02 18:11:23 +00:00
ce8ef02ed2 have screen_find_xinerama() return struct geom *reliably* instead of
XineramaScreenInfo; simplifies goop around the callers.
2013-01-02 16:26:34 +00:00
e492ed8e41 more variable consistency 2013-01-02 02:19:20 +00:00
2d28b71a1b info->xine, for consistent variable names 2013-01-02 02:02:08 +00:00
afb3648440 after we toggle a group hidden, it makes no sense to set it as active
(in sticky or non-stick mode), regardless of existing clients assigned
to that group; oga marked this bit XXX in -r1.34 for it didn't seem make
sense then either.

pulled from a diff from from Alexander Polakov.
2013-01-01 14:50:01 +00:00
ccbb1f1529 least intrusive way to plug a memleak when unbinding a duplicate key for
kbfunc_cmdexec; from Tiago Cunha
2013-01-01 14:33:52 +00:00
6cb334e503 replace emptystring with strdup here as well so we know we are free'ing
a malloc'd variable everytime; from Tiago Cunha
2013-01-01 14:26:29 +00:00
0957fbbee2 make num of groups no longer off-by-one; from Alexander Polakov
note that a re-exec of cwm will not rewrite the group number atom of
*existing* clients, so they will remain off-by-one until each client has
its atom updated, or of course a restart of X.
2013-01-01 14:19:56 +00:00
f77166194f cvsimport 2012-12-19 15:21:34 +00:00
dc1e3a8843 gc unused cmd 'flags' variable 2012-12-19 15:21:34 +00:00
c565b790ed expand CHILDMASK 2012-12-18 18:39:55 +00:00
86eaf5e973 use MOUSEMASK since we already have it 2012-12-18 18:35:36 +00:00
31e9fe4dcd remove unused SEARCHMASK 2012-12-18 17:44:18 +00:00
e5d59c2d7b define LockMask|Mod2Mask; no functional change 2012-12-18 17:37:39 +00:00
b848a2b236 simplify config file setup; with Tiago Cunha 2012-12-18 00:14:41 +00:00
479e9f769c put a default known_hosts into conf 2012-12-17 23:54:57 +00:00
75f65f399e pull user home directory via getenv or getpwuid and stash it so we don't
need to do this everytime; with Tiago Cunha
2012-12-17 23:03:41 +00:00
fe44d1dfce cmap -> colormap in client_ctx 2012-12-17 18:35:26 +00:00
af13c08108 stash the default screen visual and colormap in screen_ctx 2012-12-17 18:34:06 +00:00
82e8ec4245 replace client highlight with a client flag 2012-12-17 17:48:57 +00:00
8e67d1389c make client_mtf static 2012-12-17 14:58:46 +00:00
3a3e0383b2 create and use menuq_clear() helper; from Tiago Cunha 2012-12-17 14:32:39 +00:00
d651c1cc3b current_client() returns _curcc, so use it where appropriate 2012-12-17 14:26:29 +00:00
980c6de1ff screen font -> xftfont 2012-12-17 14:20:52 +00:00
96cd288a2a knf 2012-12-17 02:53:29 +00:00
6900cd3612 non-trivial menu drawing rewrite, moving to Xft and solving various
font/color drawing issues; from Alexander Polakov
2012-12-17 02:28:45 +00:00
a3aaad2c18 zap extra lines 2012-11-29 16:50:03 +00:00
560acb43fc specific last match for autogroup; few iterations with Kent Spillner. 2012-11-29 04:25:49 +00:00
7edabe94f2 x_setupscreen -> screen_init; no functional change. 2012-11-29 03:54:46 +00:00
5947879dfb remove dead extern 2012-11-29 03:34:19 +00:00
878eb6bf05 cvsimport 2012-11-28 14:32:44 +00:00
3e151f8c76 add xasprintf() for upcoming changes. 2012-11-28 14:32:44 +00:00
2b9d921eda ever since the 9wm code bits were removed or replaced, this file has
been a no-op, for each source file has a complete license marker (ISC).

no objections from oga, who did the 9wm rewrite/remove work in 2008.
2012-11-28 14:25:05 +00:00
fad4798e5b replace hand rolled font_make() with XftFontOpenName() and merge into
font_init().
2012-11-28 14:14:44 +00:00
b4315a3fda Use vendored queue.h everywhere 2012-11-26 16:27:22 +01:00
9088b86b14 Vendor OpenBSD queue.h,v 1.36 2012-11-26 16:26:41 +01:00
319d4e179e cvsimport 2012-11-26 16:17:45 +01:00
93f64ffc55 add some checks 2012-11-16 14:15:48 +00:00
7b00e3fe76 variable name consistency; from Thomas Pfaff 2012-11-14 21:31:53 +00:00
587d623e4a tab-complete buglet fix: once exec_path is completed, allow for
subsequent completion; from Alexander Polakov
2012-11-14 21:12:24 +00:00
28224ff830 sort 2012-11-09 03:52:02 +00:00
04d4ed7b7e fix some warnings; inspired by a diff from Thordur Bjornsson. 2012-11-08 20:18:19 +00:00
c9c0a5fdbb style nit; from Tiago Cunha. 2012-11-07 21:10:32 +00:00
55edbe460d add comment why we mouse unbind, just like kbd 2012-11-07 21:04:55 +00:00
7f1851b8f6 now that we have FOREACH_SAFE queue macros, use them where appropriate;
from Tiago Cunha.
2012-11-07 21:01:48 +00:00
bd25218333 plug a leak when using 'unmap' for kbd/mouse bindings; from Tiago Cunha. 2012-11-07 20:37:55 +00:00
76b0874b4c get rid of the xfree() wrapper around free(); from Tiago Cunha. 2012-11-07 20:34:39 +00:00
dfb6aed82a clarify windowname and windowclass values are pulled from the WM_CLASS
property; from Kent Spillner
2012-11-07 14:59:07 +00:00
94db8ca2ed zap trailing space 2012-11-07 14:58:26 +00:00
438c0332a1 missing headers; from Thordur Bjornsson. 2012-11-07 14:49:46 +00:00
c53c1af2db spacing 2012-11-07 14:40:51 +00:00
4b84287d19 tab completion support for menus; from Alexander Polakov.
ok sthen@ on an older incarnation
2012-11-07 14:39:44 +00:00
e27f649ae7 cvsimport 2012-11-01 01:10:47 +00:00
dabc05034f try to clarify xprop(1) output, with respect to WM_CLASS, without going
overboard; suggested idea by jmc
2012-11-01 01:10:47 +00:00
620af2e52d no longer a reason to carry conf_path in struct conf, so dice. 2012-10-31 22:06:24 +00:00
4fb420514d replace 'reload' with 'restart', which merely re-exec's cwm using the
existing argv; same idea with respect to argv saving as Alexander
Polakov.  reload support was half-complete and is getting in the way.

agreed to by many
2012-10-31 19:30:19 +00:00
c42043ede7 cvsimport 2012-10-29 19:46:03 +00:00
c256052308 on reload, run each client through conf_client to pick up potential
ignore and bwidth changes; also add a hack for existing maximized
windows so they don't inherit a new bwidth.

based on a diff from, and discussion with, Tiago Cunha.
2012-10-29 19:46:03 +00:00
56358be755 clarify autogroup option; based on diffs from kspillner@acm.org with
feedback from jmc
2012-10-28 20:13:02 +00:00
742bcec522 comment what this whole bit does, not just part of it. 2012-10-23 16:13:59 +00:00
710347df1b add a height to struct menu and use it in the most obvious of places. 2012-10-23 16:08:59 +00:00
637c52abf8 treat menu width the same as the height is treated when deciding its
max size and location; partially from a diff from Alexander Polakov.
2012-10-23 15:50:15 +00:00
c21d56e5a1 Stop drawing when menu doesn't fit inside the screen; picked from a
larger diff from Alexander Polakov.
2012-10-23 15:32:38 +00:00
a2d0cbcf97 cvsimport 2012-09-12 15:09:33 +00:00
871fdf5671 revert previous for now until a way to test more than a few encodings is found 2012-09-12 15:09:33 +00:00
637edf38d5 be explicit in which key masks we allow during keypress and buttonpress
events; from Alexander Polakov - makes non-us layout reliable.
2012-09-10 13:28:04 +00:00
3a45879d76 replace with xu_getprop wrapper used everywhere else. 2012-09-09 20:52:57 +00:00
86524e21dd extend client_resize so that it can know when to reset max flags and
bwidth; this allows a client to be resized from a max state, which now
gets treated like a non-max'd client.  based on a diff that does part of
this in a different way from Alexander Polakov.
2012-09-09 19:47:47 +00:00
b18e365cb3 cvsimport 2012-08-07 14:05:49 +00:00
7071261bde support multibyte input to menu code; from Alexander Polakov with a tiny tweak. 2012-08-07 14:05:49 +00:00
1545eb0837 cvsimport 2012-07-18 21:53:22 +00:00
013497c79c Further simplify Xinerama init and re-init on XRR events.
While testing for Xinerama during setup was done display-wide, each time
XineramaQueryScreens() is called either in start-up or due to an XRR
event, the library re-tests for the Xinerama extension anyway before
moving on; so the initial test is redundant and allows another global to
go away with one other change: always fill in sc->xinerama (and _no),
regardless of the success of malloc in XineramaQueryScreens(), and use
it to see if Xinerama dimensions exist when asked by client and/or menu
code.
2012-07-18 21:53:22 +00:00
3f956098d9 unify various max routines to match client placement and partially menu ones
when calculating screen dimensions.
2012-07-16 01:53:14 +00:00
57af364a21 check value 2012-07-16 01:42:00 +00:00
e42d1c7f01 move the HasXinerama check into screen_find_xinerama and simplify logic;
moves closer to logic in menu code.
2012-07-16 01:36:30 +00:00
0e8815dfb0 re-use geom struct in client_ctx (saved)geometry. 2012-07-13 17:01:04 +00:00
186a78ff1e convert xmax/ymax uses to view geometry. 2012-07-13 15:21:35 +00:00
cc08aef0df introduce screen "view" area and "work" area (gap applied) to simplify
various blocks that require understanding the screen geometry.
2012-07-13 14:18:04 +00:00
2450e309ea fix atom for setting WM_STATE. 2012-07-08 02:55:01 +00:00
6faef40c55 fix atom for retrieving WM_STATE between re-exec's. 2012-07-08 02:50:41 +00:00
c8a17ef536 remove a redundant assignment and another one up. 2012-07-08 01:00:24 +00:00
cb893d0aa9 querying for Xinerama should be done per display, not per screen, so
move chuck to display init; allows some shuffling to occur limiting
screen_init_xinerama()'s scope while keeping order intact.
2012-07-06 14:18:00 +00:00
315f25f7ab Add a couple examples of keybindings using keysyms, as discussed
with okan@ and sthen@.

OK okan@, sthen@
2012-07-06 08:41:29 +00:00
1124ee5cc1 the display's width and height are updated after an XRandR event so we
don't need to pass down the new values to screen_update_geometry(); so
just read the width/height values directly for both uses of
screen_update_geometry().  prep for further changes in this area.
2012-07-05 17:35:13 +00:00
287a5b2aad assign and use screen ctx where appropriate and consistently. 2012-07-04 23:42:03 +00:00
3681b26914 use the screen ctx since we already have it assigned. 2012-07-04 18:07:12 +00:00
8c7964468f fix missing atom (WM_PROTOCOLS) wrt new style handing; without this, the
window manager didn't know if a client supported CLIENT_PROTO_DELETE and
thus used a hammer, XKillClient.

behaviour reported by Tim van der Molen.
2012-07-04 18:00:13 +00:00
f98e123bfc re-implement atom handing; makes for a normalized and more consistent
separation between cwm and ewmh.  seen by a few.
2012-07-03 13:49:03 +00:00
8f587e6c80 cvsimport 2012-05-20 15:27:16 +02:00
956c47dbeb get rid of more stray lines/spaces 2012-05-16 21:57:21 +00:00
490ef6a7df instead of using the menu window for _NET_SUPPORTING_WM_CHECK, create a
dummy one to use instead; allows us to not have to wait for menu_init(),
so re-shuffle _NET_SUPPORTED slightly.

ok sthen@
2012-05-16 01:17:14 +00:00
b4d582c6ef fix comment 2012-05-16 01:10:11 +00:00
ac82403a51 cycle through other common cycling modifiers; based on a diff from
Alexander Polakov.

ok sthen@
2012-05-16 01:09:17 +00:00
dd5bfdb064 convert from deprecated XKeycodeToKeysym to XkbKeycodeToKeysym
ok sthen@
2012-05-16 01:04:36 +00:00
57766d8606 altpersist->cycling to be more clear that this is for cycling mod
persistence; based on a diff from Alexander Polakov, in preparation for
looping through other potential cycling modifiers.
2012-05-13 15:17:13 +00:00
fb4936d0f7 knf, some from a diff from Tiago Cunha. 2012-05-13 15:15:54 +00:00
63b59e44c1 wrap long lines; from Tiago Cunha. 2012-05-10 00:39:47 +00:00
1d8839e8ad fix section order: move AUTHORS below HISTORY; from Tiago Cunha
ok jmc@
2012-05-09 18:37:39 +00:00
26c391b3dd kill useless .Pp
ok jmc@
2012-05-07 21:25:29 +00:00
0bb88fa567 define YYSTYPE_IS_DECLARED to work with bison 2.3 2012-05-04 17:46:42 +02:00
3bf492d2e9 compile with -D_GNU_SOURCE to get all relevant prototypes 2012-05-04 17:44:27 +02:00
601779351c Version 5.1 2012-05-02 14:26:57 +02:00
5f79bace21 Add release target to Makefile 2012-05-02 14:26:36 +02:00
de5efc6a54 Define compatibility macros only if they are needed 2012-05-02 14:26:14 +02:00
fd9b83a232 check if we're in the group already, else multiple calls to
group_movetogroup() on one client will still increment nhidden if the
group is hidden.

found the hard way by Thomas Jeunet and fix from Alexander Polakov -
thanks to both!

ok oga@
2011-12-29 20:48:38 +00:00
1603d18560 cvsimport 2011-12-29 20:48:38 +00:00
96ebac8b04 fix pkg-config packages 2011-12-12 16:17:39 +01:00
22c2bc618b put snapdist in the correct location; as an option, not a bind; Tiago Cunha.
ok oga@
2011-11-06 02:03:47 +00:00
cbe46b651f cvsimport 2011-11-06 02:03:47 +00:00
fded46ba9f rename variable to reduce potential for name-space collision.
bikesheding and ok oga@.
2011-10-17 18:18:38 +00:00
901a75d3c6 cvsimport 2011-10-17 18:18:38 +00:00
f60f630b81 use xfree instead of free since strings is allocated with xmalloc; from
dhill

ok oga@
2011-10-12 15:43:50 +00:00
58077c157b cvsimport 2011-10-12 15:43:50 +00:00
ce34d0066e Add README 2011-10-05 17:48:47 +02:00
7c7a5bdcdc rewrite Makefile 2011-10-05 17:48:33 +02:00
ba3dfcf7bd move client to group (movetogroup) and hide client only if group is
already hidden (suggested behavior from Alexander Polakov).

ok sthen oga
2011-09-19 07:23:03 +00:00
54c1b90725 cvsimport 2011-09-19 07:23:03 +00:00
22f366830e fix spelling I keep getting wrong for some unknown reason; found by and
diff from Alexander Polakov.
2011-09-13 09:17:30 +00:00
3c67ec8ccc cvsimport 2011-09-13 09:17:30 +00:00
796b32123d add WM_TRANSIENT_FOR event support: moves dialogs, toolbars and such to
the group of the main application window; based on a diff from Alexander
Polakov with CLIENT_IGNORE flag suggestion from oga@.

ok oga@
2011-09-13 08:41:57 +00:00
b96caa16e6 repair groupcycle (broke after cycle-in-group support added more flags);
found by and fix from Thomas Pfaff.

ok oga@
2011-09-13 08:37:49 +00:00
b1d81b7eb6 cvsimport 2011-09-08 12:35:33 +00:00
82d31aec1d allow configurable menu font color; from Alexander Polakov with a tweak
from me.

ok oga@
2011-09-08 12:35:33 +00:00
a262f8e80c allow menufg/menubg to be configurable; from Alexander Polakov.
ok oga@
2011-09-08 12:07:03 +00:00
840323558d reinit menu on reload; from Alexander Polakov. needed for catching
upcoming menu config changes.

ok oga@
2011-09-08 12:00:49 +00:00
d85b3adc0c restore a comment and add another for clarity. 2011-09-05 07:37:55 +00:00
oga
44d8b1d3ac Make flavours of maximisation additive.
i.e. horiz-max + vertmax = full maximisation.  full - horiz = vertmax.
etc.

Martynas wrote something like this once, so I did okan, this version
seems to finally deal with the corner cases.

ok okan@.
2011-09-04 16:59:31 +00:00
b852a73a60 split off window hints from geometry so we don't need to carry them all
around when dealing with {,h,v}max.  same idea from oga.
2011-09-03 09:42:33 +00:00
325129c6ba simplify color initialization.
ok oga@
2011-09-03 09:25:39 +00:00
142a36a0c0 Add {r,}cycleingroup to cycle through clients belonging to the same
group as the active client (as opposed to all unhidden clients); from
Alexander Polakov, with a tiny tweak requested by oga.

ok oga@
2011-09-03 09:20:58 +00:00
b51f8e6a99 "defaultfont" is unclear (and confusing while reading code) when it also
applies to the user supplied font, so rename.

ok oga@
2011-09-03 09:17:16 +00:00
a4683b55f8 zap unused macro. ok oga@ 2011-08-29 09:10:49 +00:00
0dcf7efb8e restore mouse move via the keyboard, noticed by todd@. while the check
for cc was wrong due to the fact that cc->sc is always filled in during
the event, we don't even need it - just operate on the focused screen's
root window regardless.

ok todd@ oga@
2011-08-29 09:09:45 +00:00
oga
be3b8a0748 A while ago I wrote some code to not warp to ignored windows on map (rev
1.52), not realising that the previous (less efficient) fix had already
been commited (rev 1.50).

Had this in my tree for ages to remove the previous code. Effectively
reverts rev 1.50.

ok okan@
2011-08-22 16:34:34 +00:00
2dc8df110c revert r1.11 of parse.y and create logic in conf_setup instead to deal
with the various scenarios of when to attempt a parse of the config,
load defaults, and when to warn and/or exit.  triggered by bogus warning
first noticed by sobrado@.

ok oga@
2011-08-22 16:18:05 +00:00
513c35dca9 cvsimport 2011-08-04 20:53:37 +02:00
912dd46a7e Re-draw borders after reloading, not before; from Alexander Polakov.
ok oga@
2011-07-26 08:51:24 +00:00
30dbdf4178 use the menu border; from Alexander Polakov, but with the existing define.
ok oga@
2011-07-25 15:41:05 +00:00
f51f3cb9c2 We are inconsistent when it comes to function returns, so just go all
the way with the cwm specific parts.

ok oga@
2011-07-25 15:10:24 +00:00
69ac0624cc Clarify defines and make them not look like non-local ones; started by a
small diff from Thomas Pfaff.

ok oga@
2011-07-23 13:09:11 +00:00
c585dfbf32 correct spelling; from Alexander Polakov. 2011-07-14 11:39:53 +00:00
3db2d84fa8 UTF8-ify, from Alexander Polakov, but without setlocale(), after
feedback from stsp@ - thanks!

'go for it' oga@
2011-06-27 12:46:54 +00:00
bcf90f5f34 warn if we can't parse the config file on start, just like we do on reload.
part of a larger diff that was ok oga@
2011-06-25 13:37:05 +00:00
0573d184f7 Since cwm(1) uses the same parser as others in src, document how
comments and line extensions work.  Taken from sthen@'s recent
commit in src.

ok sthen@
2011-06-25 13:12:56 +00:00
d8747ae702 add 'normal' cursor and shuffle the others we use to make a bit more
sense and to be slightly less un-expected.  from Alexander Polakov.
re-use 'normal' cursor now instead XC_hand1 for menu selection.  i
really wish X had real docs and made sense.

ok on earlier diff with '?' removed (but it's back now) oga@
2011-06-24 06:52:23 +00:00
8f1c583361 New option to raise a client via the mouse (unbound by default);
opposing action, lower, already exists and bound.  Both keyboard
mappings already exist.

'no opinion either way' oga@
2011-06-24 06:09:26 +00:00
34ba6cdbcc introduce a new config option to snap to the screen edge. 'snapdist'
keyword taken from a diff from Sviatoslav Chagaev to do the same thing,
but implemented in a completely way (based on some very old code from
mk@).  default set to 0, so no behavior change.

ok oga@ (who would also like to take it further...)
2011-06-24 06:06:24 +00:00
b230e611be re-do various bits of {,h,v}max for simplicity and less flag handling.
as a bonus, flipping between various combinations of vmax+hmax provides
a more predictable behavior.

commitski oga@
2011-06-24 06:01:47 +00:00
c566b31bd0 replace the non-working check to see if another wm is running with a
method that actually works.  checking for icccm compliant wm's should
also be done first, but that's another diff (noted by oga).

ok oga@
2011-06-24 05:58:51 +00:00
d39f5b9183 more nitems usage.
ok oga@
2011-06-24 05:54:30 +00:00
c8ea76a965 re-order sweep draw so that we map into the client window after
re-parenting; from Sviatoslav Chagaev.

ok oga@
2011-06-24 05:51:25 +00:00
c2a8363dd9 struct XftFont already has height which we can use directly instead of
calculating ourselves, so do so.

ok oga@
2011-06-24 05:45:57 +00:00
f6fcd0eb5d alter a few function returns and prototypes; found by lint.
ok oga@
2011-06-24 05:40:09 +00:00
69cc58555d Correct extern declaration and while here, rename a variable to be
pedantic.

from Thomas Pfaff.

ok oga@
2011-06-24 05:33:41 +00:00
89d8a14b6d collapse client and group {r,}cycle defines since they are really
the same thing.

ok oga@
2011-06-24 05:30:42 +00:00
9a6a2d3cf3 switch to installing source manuals:
* cope with new bsd.man.mk
* update imake configuration

ports use of imake remains unchanged
2011-06-23 22:48:59 +00:00
f067809af4 cvsimport 2011-05-15 17:58:47 +00:00
64d8bb630a fix botched client and group cycle defines; noticed by Thomas Pfaff.
"no cookie! fix it" - oga@
2011-05-15 17:58:47 +00:00
fa64bc2f32 remove left over XSync; ok oga@ 2011-05-13 12:53:19 +00:00
c8cd94882d tag and comment cleanup; ok oga@ 2011-05-11 13:53:51 +00:00
a90a0b5705 cvsimport 2011-05-07 17:15:37 +00:00
43450c8fd7 introduce a new 'freeze' flag (CMS-f by default) which may be applied to
any window, after which all move/resize requests will be ignored,
essentially freezing the window in place.

there's a possibility to merge this with the 'ignore' concept, pending
on how ignore+freeze should behave (really more ewmh stuff), but punting
for now since ponies are on the line.

requested and tested by thib at k2k11 with ponies, unicorns and rainbows.

'save the unicorns' todd@, ok oga@
2011-05-07 17:15:37 +00:00
oga
760c6b5522 On map, don't warp to windows that are marked as ignored.
Requested by Christian Neukirchen last august. He provided a patch but the one I
wrote was significantly simpler (1 - 2 + in the whole diff).

makes sense to okan@.
2011-05-06 19:39:44 +00:00
604a5d07a4 bump window resize back to 60hz. instead, we should not need to sync
every time we move/resize a window, so remove XSync in both mouse move
and resize events.  tested by Brynet as well.

ok oga@
2011-05-05 19:52:52 +00:00
8ed968601e re-org. ok oga@ 2011-05-05 16:40:37 +00:00
c62c0ee9db make the menu window aware of xinerama info; this makes the menu stay
within the current screen, like other cwm window placements and
mutations (vmax/hmax/max) - from Sviatoslav Chagaev.

ok oga@
2011-05-05 15:32:24 +00:00
2bac9a6be9 cvsimport 2011-05-03 19:28:18 +02:00
349b3295b8 revert the XSync timing change for client move only; first noticed by
brynet.

sure oga@
2011-03-23 07:27:32 +00:00
5972c7a507 warp the pointer back, iff we don't move the mouse, once we are done
with the menu; idea and initial from Sviatoslav Chagaev.

discussion with and ok oga@
2011-03-22 13:50:40 +00:00
e28a7f832e we should re-focus the client and ungrab the ptr even if we don't have a
mouse based match (i.e. in the kbd no match case).  update the comment
as well.

ok oga@
2011-03-22 11:09:52 +00:00
9c587d9725 fix nousance of always highlighting the first entry even when there is
no match (seen in the ctrl-a case)

ok oga@
2011-03-22 11:05:13 +00:00
765479fc86 move the default case to the end.
ok oga@
2011-03-22 11:03:05 +00:00
a50bfb613d reduce the number of times we sync during a window resize.
ok oga@
2011-03-22 10:59:08 +00:00
2c706e60c7 introduce nitems macro, with the appropriate ifndef.
ok oga@
2011-03-22 10:57:31 +00:00
8f88cd474a if virt/horz un-maximizing, restore only the un-maximized axis
coordinates; allows moving a client and not restoring the maximized axis
coordinates.

picked from a larger martynas diff from ages ago.

ok oga@
2011-03-22 10:56:08 +00:00
9b4985439e (0,0) is also inside in the screen; from Sviatoslav Chagaev.
ok oga@
2011-03-22 10:54:42 +00:00
3ff303658c while this piece deals with client argv's in a seemingly inefficient way
(noticed by Tim Peniket), since we don't ever do anything with cliarg,
remove the hunk and cliarg completely.

ok oga@
2011-03-22 10:52:37 +00:00
a12fb346a2 remove XXX and move a configure event out of the event handler functions.
reminded by a similiar diff from Thomas Pfaff.

ok oga@
2011-03-22 10:49:46 +00:00
00b502b1da move the single keycode function directly into the menu code.
ok oga@
2011-03-22 10:47:59 +00:00
e03323d22e xcb is no longer optional. 2011-03-08 20:48:59 +00:00
9eaece0541 cvsimport 2011-02-18 11:50:01 +00:00
64e62989d0 if we are saving the pointer location for a specific client, make sure
that if it is not already inbounds, put it in the default location,
which happens to be inbounds.

behavior noticed by Thomas Pfaff while maximizing and un-maximizing a
window, leaving the pointer behind and causing client_cycle() to be a
bit lost.

ok oga@
2011-02-13 20:09:57 +00:00
0884d38e3b we lose track of highstack somewhere, so recompute it before we need it.
fixes a crash reported by christian neukirchen.  ok okan
2011-02-13 17:25:20 +00:00
5bc15cdb58 cvsimport 2010-12-24 23:42:39 +01:00
cd23316a72 in MotionNotify geom.x, geom.y calculations take into account bwidth.
fixes the annoying bug where windows would go +bwidth pixels right,
+bwidth pixels down.  ok okan@
2010-12-14 11:08:47 +00:00
111a88129d Explicitely link libxcb when needed for static arches. 2010-11-27 14:25:47 +00:00
10622cf40d Add protoypes, fixes segfaults on x86 2010-09-26 14:51:23 +02:00
a64855f9cc Improve CFLAGS 2010-09-26 14:51:02 +02:00
e6d9effa8d Use yacc instead of byacc 2010-09-26 01:46:05 +02:00
b11c12e99b Merge branch 'origin' into linux 2010-09-26 01:43:12 +02:00
c9a14a8f40 cvsimport 2010-07-30 12:28:19 +00:00
e0c9657773 cvsimport 2010-05-22 22:32:08 +00:00
oga
f769df540d cvsimport 2010-04-12 16:17:46 +00:00
d90ab51111 Add missing functions 2010-02-21 19:18:12 +01:00
50aff37f50 Import linux.patch
Patch largely based on cwmbuild.sh from http://tamentis.com/hacks/cwm/
written by Bertrand Janin, updated by Christian Neukirchen.
2010-02-21 19:15:27 +01:00
5fa1a97034 Initial import of cwm-3.
tested by sturm@, ok matthieu@
2007-04-27 17:58:48 +00:00
29 changed files with 4481 additions and 3042 deletions

14
LICENSE
View File

@ -1,14 +0,0 @@
Copyright (c) 2004,2005 Marius Aamodt Eriksen <marius@monkey.org>
Copyright (c) 2004 Andy Adamson <dros@monkey.org>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,26 +1,53 @@
# $OpenBSD$
.include <bsd.xconf.mk>
# cwm makefile for BSD make and GNU make
# uses pkg-config, DESTDIR and PREFIX
PROG= cwm
PREFIX?= /usr/local
SRCS= calmwm.c screen.c xmalloc.c client.c menu.c \
search.c util.c xutil.c conf.c input.c xevents.c group.c \
kbfunc.c mousefunc.c font.c parse.y
search.c util.c xutil.c conf.c xevents.c group.c \
kbfunc.c mousefunc.c parse.y
CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 -I${.CURDIR}
OBJS= calmwm.o screen.o xmalloc.o client.o menu.o \
search.o util.o xutil.o conf.o xevents.o group.o \
kbfunc.o mousefunc.o strlcpy.o strlcat.o y.tab.o \
strtonum.o fgetln.o
CFLAGS+= -Wall
CPPFLAGS+= `pkg-config --cflags fontconfig x11 xft xinerama xrandr`
LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lXau -lXdmcp \
-lfontconfig -lexpat -lfreetype -lz -lXinerama -lXrandr -lXext
CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE
MANDIR= ${X11BASE}/man/cat
MAN= cwm.1 cwmrc.5
LDFLAGS+= `pkg-config --libs fontconfig x11 xft xinerama xrandr`
CLEANFILES= cwm.cat1 cwmrc.cat5
MANPREFIX?= ${PREFIX}/share/man
obj: _xenocara_obj
all: ${PROG}
.include <bsd.prog.mk>
.include <bsd.xorg.mk>
clean:
rm -f ${OBJS} ${PROG} y.tab.c
y.tab.c: parse.y
yacc parse.y
${PROG}: ${OBJS} y.tab.o
${CC} ${OBJS} ${LDFLAGS} -o ${PROG}
.c.o:
${CC} -c ${CFLAGS} ${CPPFLAGS} $<
install: ${PROG}
install -d ${DESTDIR}${PREFIX}/bin ${DESTDIR}${MANPREFIX}/man1 ${DESTDIR}${MANPREFIX}/man5
install -m 755 cwm ${DESTDIR}${PREFIX}/bin
install -m 644 cwm.1 ${DESTDIR}${MANPREFIX}/man1
install -m 644 cwmrc.5 ${DESTDIR}${MANPREFIX}/man5
release:
VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \
git archive --prefix=cwm-$$VERSION/ -o cwm-$$VERSION.tar.gz HEAD
sign:
VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \
gpg --armor --detach-sign 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

94
README Normal file
View File

@ -0,0 +1,94 @@
This is a port of OpenBSD's excellent cwm[0] to Linux and other
Unices.
cwm is a window manager for X11 which contains many features that
concentrate on the efficiency and transparency of window
management. cwm also aims to maintain the simplest and most
pleasant aesthetic.
This port requires pkg-config, Xft, Xinerama and Xrandr. The included Makefile
should work with both GNU make and BSD make. It has been built successfully on
OpenBSD, FreeBSD, NetBSD, OS X 10.9 and Linux.
This version actively tracks changes in the OpenBSD CVS repository.
Releases are roughly coordinated.
The revision controlled version is at https://github.com/chneukirchen/cwm
Releases can be found at http://chneukirchen.org/releases
You are welcome to join the IRC channel ##cwm on Freenode to talk about cwm.
ChangeLog:
2012-05-02: First public release 5.1 of portable cwm.
2014-04-13: Second public release 5.5 of portable cwm.
User visible changes (for a full list including smaller bug fixes, see
http://www.openbsd.org/plus.html ff.)
Changes made between OpenBSD 5.1 and 5.2
* Fixed cwm(1) atom (WM_PROTOCOLS) style handing; closing a window will no
longer close entire application if the client supports CLIENT_PROTO_DELETE.
* Re-implement atom handing for more consistent separation of cwm(1) and
Extended Window Manager Hints.
* cwm(1) better integrated into the freedesktop.org Window Manager
Specification Project.
Changes made between OpenBSD 5.2 and 5.3
* Set the initial cwm(1) group to "1".
* Added cwm(1) per-group vert/horiz tiling support with new bind commands
"vtile" and "htile."
* Made cwm(1) screen font an Xft(3) font.
* Specific last match for autogroup in cwmrc(5).
* Tab completion support for cwm(1) menus.
* Allow cwm(1) clients to be resized from a max state.
* Multibyte input to cwm(1) menu code now possible.
Changes made between OpenBSD 5.3 and 5.4
* Added support for mouse based group {,r}cycle to cwmrc(5).
* Allow mouse button4 and button5 in cwmrc(5).
* Made cwm(1) check for, and honour, CWStackMode and CWSibling change requests
during a ConfigureRequest event.
* Honour PATH search order for cwm(1)'s exec.
Changes made between OpenBSD 5.5 and 5.4
* Only set the cwm(1) urgency flag if the client is not active.
* Allow the cwm(1) config parser continue parsing even after encountering an
error.
* cwm(1) now follows the EWMH spec: if the cardinal returned is 0xFFFFFFFF (-1)
then the window should appear on all desktops.
* Made cwm(1) supply a more useful title for windows launched via the ssh(1)
command menu ("[ssh] ").
* Allowed cwm(1) to accept _NET_WM_DESKTOP and _NET_CURRENT_DESKTOP
ClientMessage.
* Implemented cwm(1) support for _NET_WM_STATE_FULLSCREEN hint, with keybinding
changes: CM-f "fullscreen", CM-m "maximize".
* Instead of using the work area, use the Xinerama area for cwm(1) snap
calculations.
* Save-set when cwm(1) is re-exec'ing so as to not lose State on our hidden
clients.
* Added cwmrc(5) support for XUrgency and matching
_NET_WM_STATE_DEMANDS_ATTENTION ewmh hint, with configurable urgencyborder.
* Prepend the group shortcut in the cwm(1) client search menu;
prepend shortcut in unhide menu.
* If not hidden during an UnmapNotify event, cwm(1) will now un-manage the
client.
* Like "gap", made cwm(1) "snapdist" per-screen.
* Removed cwmrc(5) option to bind a key by keycode with brackets, which never
worked. Users should be using keysym names not keycodes.
* Re-added cwm(1) support for WM_TAKE_FOCUS. Solves keyboard input focus loss
for java apps.
* For cwm(1) clients that support WM_TAKE_FOCUS in their WM_PROTOCOLS property,
send a ClientMessage event.
2015-01-24: Third public release 5.6 of portable cwm.
* Support building on FreeBSD and OS X.
* Support for sticky windows (_NET_WM_STATE_STICKY).
* Internal cleanups and bug fixes.
--Christian Neukirchen <chneukirchen@gmail.com>
[0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/

252
calmwm.c
View File

@ -15,53 +15,60 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
Display *X_Dpy;
Cursor Cursor_move;
Cursor Cursor_resize;
Cursor Cursor_select;
Cursor Cursor_default;
Cursor Cursor_question;
Time Last_Event_Time = CurrentTime;
Atom cwmh[CWMH_NITEMS];
Atom ewmh[EWMH_NITEMS];
struct screen_ctx_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct client_ctx_q Clientq = TAILQ_HEAD_INITIALIZER(Clientq);
int HasXinerama, HasRandr, Randr_ev;
int Starting;
int HasRandr, Randr_ev;
struct conf Conf;
const char *homedir;
volatile sig_atomic_t cwm_status;
static void sigchld_cb(int);
static void dpy_init(const char *);
static void sighdlr(int);
static int x_errorhandler(Display *, XErrorEvent *);
static void x_setup(void);
static void x_setupscreen(struct screen_ctx *, u_int);
static void x_init(const char *);
static void x_restart(char **);
static void x_teardown(void);
static int x_wmerrorhandler(Display *, XErrorEvent *);
int
main(int argc, char **argv)
{
const char *conf_file = NULL;
char *display_name = NULL;
char *conf_path, *display_name = NULL;
char **cwm_argv;
int ch;
struct passwd *pw;
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
warnx("no locale support");
mbtowc(NULL, NULL, MB_CUR_MAX);
cwm_argv = argv;
while ((ch = getopt(argc, argv, "c:d:")) != -1) {
switch (ch) {
case 'c':
@ -77,185 +84,150 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (signal(SIGCHLD, sigchld_cb) == SIG_ERR)
if (signal(SIGCHLD, sighdlr) == SIG_ERR)
err(1, "signal");
Starting = 1;
dpy_init(display_name);
if ((homedir = getenv("HOME")) == NULL || *homedir == '\0') {
pw = getpwuid(getuid());
if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0')
homedir = pw->pw_dir;
else
homedir = "/";
}
bzero(&Conf, sizeof(Conf));
conf_setup(&Conf, conf_file);
xu_getatoms();
x_setup();
Starting = 0;
if (conf_file == NULL)
xasprintf(&conf_path, "%s/%s", homedir, CONFFILE);
else
conf_path = xstrdup(conf_file);
xev_loop();
if (access(conf_path, R_OK) != 0) {
if (conf_file != NULL)
warn("%s", conf_file);
free(conf_path);
conf_path = NULL;
}
conf_init(&Conf);
if (conf_path && (parse_config(conf_path, &Conf) == -1))
warnx("config file %s has errors", conf_path);
free(conf_path);
x_init(display_name);
cwm_status = CWM_RUNNING;
while (cwm_status == CWM_RUNNING)
xev_process();
x_teardown();
if (cwm_status == CWM_RESTART)
x_restart(cwm_argv);
return (0);
return(0);
}
static void
dpy_init(const char *dpyname)
x_init(const char *dpyname)
{
int i;
if ((X_Dpy = XOpenDisplay(dpyname)) == NULL)
errx(1, "unable to open display \"%s\"",
XDisplayName(dpyname));
errx(1, "unable to open display \"%s\"", XDisplayName(dpyname));
XSetErrorHandler(x_wmerrorhandler);
XSelectInput(X_Dpy, DefaultRootWindow(X_Dpy), SubstructureRedirectMask);
XSync(X_Dpy, False);
XSetErrorHandler(x_errorhandler);
HasRandr = XRRQueryExtension(X_Dpy, &Randr_ev, &i);
conf_atoms();
conf_cursor(&Conf);
for (i = 0; i < ScreenCount(X_Dpy); i++)
screen_init(i);
}
static void
x_setup(void)
x_restart(char **args)
{
struct screen_ctx *sc;
struct keybinding *kb;
int i;
for (i = 0; i < ScreenCount(X_Dpy); i++) {
sc = xcalloc(1, sizeof(*sc));
x_setupscreen(sc, i);
TAILQ_INSERT_TAIL(&Screenq, sc, entry);
}
/*
* XXX key grabs weren't done before, since Screenq was empty,
* do them here for now (this needs changing).
*/
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_grab(&Conf, kb);
Cursor_move = XCreateFontCursor(X_Dpy, XC_fleur);
Cursor_resize = XCreateFontCursor(X_Dpy, XC_bottom_right_corner);
Cursor_select = XCreateFontCursor(X_Dpy, XC_hand1);
Cursor_default = XCreateFontCursor(X_Dpy, XC_X_cursor);
Cursor_question = XCreateFontCursor(X_Dpy, XC_question_arrow);
(void)setsid();
(void)execvp(args[0], args);
}
static void
x_teardown(void)
{
struct screen_ctx *sc;
unsigned int i;
TAILQ_FOREACH(sc, &Screenq, entry)
XFreeGC(X_Dpy, sc->gc);
conf_clear(&Conf);
TAILQ_FOREACH(sc, &Screenq, entry) {
for (i = 0; i < CWM_COLOR_NITEMS; i++)
XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which),
&sc->xftcolor[i]);
XftDrawDestroy(sc->xftdraw);
XftFontClose(X_Dpy, sc->xftfont);
XUnmapWindow(X_Dpy, sc->menuwin);
XDestroyWindow(X_Dpy, sc->menuwin);
XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin);
}
XUngrabPointer(X_Dpy, CurrentTime);
XUngrabKeyboard(X_Dpy, CurrentTime);
for (i = 0; i < CF_NITEMS; i++)
XFreeCursor(X_Dpy, Conf.cursor[i]);
XSync(X_Dpy, False);
XSetInputFocus(X_Dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
XCloseDisplay(X_Dpy);
}
static void
x_setupscreen(struct screen_ctx *sc, u_int which)
static int
x_wmerrorhandler(Display *dpy, XErrorEvent *e)
{
Window *wins, w0, w1;
XWindowAttributes winattr;
XSetWindowAttributes rootattr;
int fake;
u_int nwins, i;
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, sc->which);
errx(1, "root window unavailable - perhaps another wm is running?");
conf_gap(&Conf, sc);
screen_update_geometry(sc, DisplayWidth(X_Dpy, sc->which),
DisplayHeight(X_Dpy, sc->which));
conf_color(&Conf, sc);
group_init(sc);
font_init(sc);
conf_font(&Conf, sc);
TAILQ_INIT(&sc->mruq);
/* Initialize menu window. */
menu_init(sc);
xu_setwmname(sc);
rootattr.event_mask = ChildMask|PropertyChangeMask|EnterWindowMask|
LeaveWindowMask|ColormapChangeMask|ButtonMask;
XChangeWindowAttributes(X_Dpy, sc->rootwin,
CWEventMask, &rootattr);
/* Deal with existing clients. */
XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins);
for (i = 0; i < nwins; i++) {
XGetWindowAttributes(X_Dpy, wins[i], &winattr);
if (winattr.override_redirect ||
winattr.map_state != IsViewable)
continue;
client_new(wins[i], sc, winattr.map_state != IsUnmapped);
}
XFree(wins);
screen_updatestackingorder(sc);
if (XineramaQueryExtension(X_Dpy, &fake, &fake) == 1 &&
((HasXinerama = XineramaIsActive(X_Dpy)) == 1))
HasXinerama = 1;
if (HasRandr)
XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
/*
* initial setup of xinerama screens, if we're using RandR then we'll
* redo this whenever the screen changes since a CTRC may have been
* added or removed
*/
screen_init_xinerama(sc);
XSync(X_Dpy, False);
return(0);
}
static int
x_errorhandler(Display *dpy, XErrorEvent *e)
{
#ifdef DEBUG
{
char msg[80], number[80], req[80];
char msg[80], number[80], req[80];
XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg));
snprintf(number, sizeof(number), "%d", e->request_code);
XGetErrorDatabaseText(X_Dpy, "XRequest", number,
"<unknown>", req, sizeof(req));
XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg));
(void)snprintf(number, sizeof(number), "%d", e->request_code);
XGetErrorDatabaseText(X_Dpy, "XRequest", number,
"<unknown>", req, sizeof(req));
warnx("%s(0x%x): %s", req, (u_int)e->resourceid, msg);
}
warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg);
#endif
if (Starting &&
e->error_code == BadAccess &&
e->request_code == X_GrabKey)
errx(1, "root window unavailable - perhaps another "
"wm is running?");
return (0);
return(0);
}
static void
sigchld_cb(int which)
sighdlr(int sig)
{
pid_t pid;
int save_errno = errno;
int status;
int save_errno = errno, status;
/* Collect dead children. */
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
switch (sig) {
case SIGCHLD:
/* Collect dead children. */
while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
break;
}
errno = save_errno;
}
__dead void
void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-c file] [-d display]\n", __progname);
(void)fprintf(stderr, "usage: %s [-c file] [-d display]\n",
__progname);
exit(1);
}

859
calmwm.h
View File

@ -15,12 +15,32 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#ifndef _CALMWM_H_
#define _CALMWM_H_
#include <sys/param.h>
#include <stdio.h>
#include "queue.h"
/* prototypes for portable-included functions */
char *fgetln(FILE *, size_t *);
long long strtonum(const char *, long long, long long, const char **);
#ifdef strlcat
#define HAVE_STRLCAT
#else
size_t strlcat(char *, const char *, size_t);
#endif
#ifdef strlcpy
#define HAVE_STRLCPY
#else
size_t strlcpy(char *, const char *, size_t);
#endif
#include <X11/XKBlib.h>
#include <X11/Xatom.h>
#include <X11/Xft/Xft.h>
#include <X11/Xlib.h>
@ -31,40 +51,102 @@
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
#define CALMWM_MAXNAMELEN 256
#undef MIN
#undef MAX
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
#define CONFFILE ".cwmrc"
#define WMNAME "CWM"
#define ChildMask (SubstructureRedirectMask|SubstructureNotifyMask)
#define ButtonMask (ButtonPressMask|ButtonReleaseMask)
#define MouseMask (ButtonMask|PointerMotionMask)
#define KeyMask (KeyPressMask|ExposureMask)
#define MenuMask (ButtonMask|ButtonMotionMask|ExposureMask| \
PointerMotionMask)
#define MenuGrabMask (ButtonMask|ButtonMotionMask|StructureNotifyMask|\
PointerMotionMask)
#define SearchMask (KeyPressMask|ExposureMask)
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define MENUMASK (MOUSEMASK|ButtonMotionMask|ExposureMask)
#define MENUGRABMASK (MOUSEMASK|ButtonMotionMask|StructureNotifyMask)
#define KEYMASK (KeyPressMask|ExposureMask)
#define IGNOREMODMASK (LockMask|Mod2Mask)
enum cwmcolor {
CWM_COLOR_BORDOR_ACTIVE,
/* kb movement */
#define CWM_MOVE 0x0001
#define CWM_RESIZE 0x0002
#define CWM_PTRMOVE 0x0004
#define CWM_BIGMOVE 0x0008
#define CWM_UP 0x0010
#define CWM_DOWN 0x0020
#define CWM_LEFT 0x0040
#define CWM_RIGHT 0x0080
/* exec */
#define CWM_EXEC_PROGRAM 0x0001
#define CWM_EXEC_WM 0x0002
/* cycle */
#define CWM_CYCLE 0x0001
#define CWM_RCYCLE 0x0002
#define CWM_INGROUP 0x0004
/* menu */
#define CWM_MENU_DUMMY 0x0001
#define CWM_MENU_FILE 0x0002
#define ARG_CHAR 0x0001
#define ARG_INT 0x0002
#define CWM_TILE_HORIZ 0x0001
#define CWM_TILE_VERT 0x0002
#define CWM_GAP 0x0001
#define CWM_NOGAP 0x0002
#define CWM_WIN 0x0001
#define CWM_QUIT 0x0000
#define CWM_RUNNING 0x0001
#define CWM_RESTART 0x0002
union arg {
char *c;
int i;
};
union press {
KeySym keysym;
unsigned int button;
};
enum cursor_font {
CF_DEFAULT,
CF_MOVE,
CF_NORMAL,
CF_QUESTION,
CF_RESIZE,
CF_NITEMS
};
enum color {
CWM_COLOR_BORDER_ACTIVE,
CWM_COLOR_BORDER_INACTIVE,
CWM_COLOR_BORDER_URGENCY,
CWM_COLOR_BORDER_GROUP,
CWM_COLOR_BORDER_UNGROUP,
CWM_COLOR_FG_MENU,
CWM_COLOR_BG_MENU,
CWM_COLOR_MAX
CWM_COLOR_MENU_FG,
CWM_COLOR_MENU_BG,
CWM_COLOR_MENU_FONT,
CWM_COLOR_MENU_FONT_SEL,
CWM_COLOR_NITEMS
};
struct color {
unsigned long pixel;
char *name;
struct geom {
int x;
int y;
int w;
int h;
};
struct gap {
int top;
int bottom;
@ -72,496 +154,433 @@ struct gap {
int right;
};
struct client_ctx;
TAILQ_HEAD(cycle_entry_q, client_ctx);
TAILQ_HEAD(group_ctx_q, group_ctx);
TAILQ_HEAD(client_ctx_q, client_ctx);
#define CALMWM_NGROUPS 9
struct group_ctx {
TAILQ_ENTRY(group_ctx) entry;
struct client_ctx_q clients;
int shortcut;
int hidden;
int nhidden;
int highstack;
};
struct screen_ctx {
TAILQ_ENTRY(screen_ctx) entry;
u_int which;
Window rootwin;
Window menuwin;
struct color color[CWM_COLOR_MAX];
GC gc;
int altpersist;
int xmax;
int ymax;
struct gap gap;
struct cycle_entry_q mruq;
XftDraw *xftdraw;
XftColor xftcolor;
XftFont *font;
u_int fontheight;
int xinerama_no;
XineramaScreenInfo *xinerama;
struct group_ctx *group_active;
struct group_ctx groups[CALMWM_NGROUPS];
int group_hideall;
struct group_ctx_q groupq;
char **group_names;
int group_nonames;
};
TAILQ_HEAD(screen_ctx_q, screen_ctx);
#define CLIENT_PROTO_DELETE 0x01
#define CLIENT_PROTO_TAKEFOCUS 0x02
#define CLIENT_MAXNAMEQLEN 5
#define CLIENT_HIDDEN 0x01
#define CLIENT_IGNORE 0x02
#define CLIENT_DOMAXIMIZE 0x04
#define CLIENT_MAXIMIZED 0x08
#define CLIENT_DOVMAXIMIZE 0x10
#define CLIENT_VMAXIMIZED 0x20
#define CLIENT_DOHMAXIMIZE 0x40
#define CLIENT_HMAXIMIZED 0x80
#define CLIENT_HIGHLIGHT_GROUP 1
#define CLIENT_HIGHLIGHT_UNGROUP 2
struct winname {
TAILQ_ENTRY(winname) entry;
char *name;
};
TAILQ_HEAD(winname_q, winname);
TAILQ_HEAD(ignore_q, winname);
struct client_ctx {
TAILQ_ENTRY(client_ctx) entry;
TAILQ_ENTRY(client_ctx) searchentry;
TAILQ_ENTRY(client_ctx) group_entry;
TAILQ_ENTRY(client_ctx) mru_entry;
TAILQ_ENTRY(client_ctx) entry;
TAILQ_ENTRY(client_ctx) group_entry;
struct screen_ctx *sc;
Window win;
XSizeHints *size;
Colormap cmap;
u_int bwidth;
Colormap colormap;
unsigned int bwidth; /* border width */
struct geom geom, savegeom, fullgeom;
struct {
int x, y, width, height, basew, baseh,
minw, minh, maxw, maxh, incw, inch;
float mina, maxa;
} geom, savegeom;
long flags; /* defined hints */
int basew; /* desired width */
int baseh; /* desired height */
int minw; /* minimum width */
int minh; /* minimum height */
int maxw; /* maximum width */
int maxh; /* maximum height */
int incw; /* width increment progression */
int inch; /* height increment progression */
float mina; /* minimum aspect ratio */
float maxa; /* maximum aspect ratio */
} hint;
struct {
int x,y;
int x; /* x position */
int y; /* y position */
} ptr;
#define CLIENT_HIDDEN 0x0001
#define CLIENT_IGNORE 0x0002
#define CLIENT_VMAXIMIZED 0x0004
#define CLIENT_HMAXIMIZED 0x0008
#define CLIENT_FREEZE 0x0010
#define CLIENT_GROUP 0x0020
#define CLIENT_UNGROUP 0x0040
#define CLIENT_INPUT 0x0080
#define CLIENT_WM_DELETE_WINDOW 0x0100
#define CLIENT_WM_TAKE_FOCUS 0x0200
#define CLIENT_URGENCY 0x0400
#define CLIENT_FULLSCREEN 0x0800
#define CLIENT_STICKY 0x1000
#define CLIENT_ACTIVE 0x2000
int xproto;
#define CLIENT_HIGHLIGHT (CLIENT_GROUP | CLIENT_UNGROUP)
#define CLIENT_MAXFLAGS (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED)
#define CLIENT_MAXIMIZED (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED)
int flags;
int state;
char *name;
int stackingorder;
struct winname_q nameq;
size_t nameqlen;
#define CLIENT_MAXNAMEQLEN 5
int nameqlen;
char *name;
char *label;
int active;
int highlight;
char *matchname;
struct group_ctx *group;
int stackingorder;
char *app_class;
char *app_name;
char *app_cliarg;
XClassHint ch;
XWMHints *wmh;
};
TAILQ_HEAD(client_ctx_q, client_ctx);
extern const char *shortcut_to_name[];
struct group_ctx {
TAILQ_ENTRY(group_ctx) entry;
struct screen_ctx *sc;
char *name;
int num;
struct client_ctx_q clientq;
};
TAILQ_HEAD(group_ctx_q, group_ctx);
/* Autogroups */
struct autogroupwin {
TAILQ_ENTRY(autogroupwin) entry;
char *class;
char *name;
int num;
};
TAILQ_HEAD(autogroupwin_q, autogroupwin);
#define CWM_MOVE 0x01
#define CWM_RESIZE 0x02
#define CWM_PTRMOVE 0x04
#define CWM_BIGMOVE 0x08
#define CWM_UP 0x10
#define CWM_DOWN 0x20
#define CWM_LEFT 0x40
#define CWM_RIGHT 0x80
/*
* Match a window.
*/
#define CONF_MAX_WINTITLE 256
struct winmatch {
TAILQ_ENTRY(winmatch) entry;
char title[CONF_MAX_WINTITLE];
struct region_ctx {
TAILQ_ENTRY(region_ctx) entry;
int num;
struct geom area;
};
TAILQ_HEAD(region_ctx_q, region_ctx);
TAILQ_HEAD(winmatch_q, winmatch);
/* for cwm_exec */
#define CWM_EXEC_PROGRAM 0x1
#define CWM_EXEC_WM 0x2
/* for alt-tab */
#define CWM_CYCLE 0x0
#define CWM_RCYCLE 0x1
/* for group cycle */
#define CWM_CYCLEGROUP 0x0
#define CWM_RCYCLEGROUP 0x1
#define KBFLAG_NEEDCLIENT 0x01
#define KBTOGROUP(X) ((X) - 1)
union arg {
char *c;
int i;
struct screen_ctx {
TAILQ_ENTRY(screen_ctx) entry;
int which;
Window rootwin;
Window menuwin;
int cycling;
int hideall;
int snapdist;
struct geom view; /* viewable area */
struct geom work; /* workable area, gap-applied */
struct gap gap;
struct client_ctx_q clientq;
struct region_ctx_q regionq;
#define CALMWM_NGROUPS 10
struct group_ctx_q groupq;
struct group_ctx *group_active;
XftColor xftcolor[CWM_COLOR_NITEMS];
XftDraw *xftdraw;
XftFont *xftfont;
};
TAILQ_HEAD(screen_ctx_q, screen_ctx);
struct keybinding {
TAILQ_ENTRY(keybinding) entry;
struct binding {
TAILQ_ENTRY(binding) entry;
void (*callback)(struct client_ctx *, union arg *);
union arg argument;
int modmask;
int keysym;
int keycode;
unsigned int modmask;
union press press;
int flags;
int argtype;
};
TAILQ_HEAD(keybinding_q, binding);
TAILQ_HEAD(mousebinding_q, binding);
struct cmd {
TAILQ_ENTRY(cmd) entry;
int flags;
char image[MAXPATHLEN];
char label[256];
/* (argv) */
TAILQ_ENTRY(cmd) entry;
char *name;
char path[PATH_MAX];
};
struct mousebinding {
int modmask;
int button;
int context;
void (*callback)(struct client_ctx *, void *);
TAILQ_ENTRY(mousebinding) entry;
};
#define MOUSEBIND_CTX_ROOT 1
#define MOUSEBIND_CTX_WIN 2
TAILQ_HEAD(keybinding_q, keybinding);
TAILQ_HEAD(cmd_q, cmd);
TAILQ_HEAD(mousebinding_q, mousebinding);
/* Global configuration */
struct conf {
struct keybinding_q keybindingq;
struct autogroupwin_q autogroupq;
struct winmatch_q ignoreq;
char conf_path[MAXPATHLEN];
struct cmd_q cmdq;
struct mousebinding_q mousebindingq;
#define CONF_STICKY_GROUPS 0x0001
int flags;
#define CONF_BWIDTH 1
int bwidth;
#define CONF_MAMOUNT 1
int mamount;
struct gap gap;
#define CONF_COLOR_ACTIVEBORDER "#CCCCCC"
#define CONF_COLOR_INACTIVEBORDER "#666666"
#define CONF_COLOR_GROUPBORDER "blue"
#define CONF_COLOR_UNGROUPBORDER "red"
#define CONF_COLOR_MENUFG "black"
#define CONF_COLOR_MENUBG "white"
struct color color[CWM_COLOR_MAX];
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
#define DEFAULTFONTNAME "sans-serif:pixelsize=14:bold"
char *DefaultFontName;
};
/* Menu stuff */
#define MENU_MAXENTRY 50
struct menu {
TAILQ_ENTRY(menu) entry;
TAILQ_ENTRY(menu) resultentry;
TAILQ_ENTRY(menu) entry;
TAILQ_ENTRY(menu) resultentry;
#define MENU_MAXENTRY 200
char text[MENU_MAXENTRY + 1];
char print[MENU_MAXENTRY + 1];
void *ctx;
short dummy;
short abort;
};
TAILQ_HEAD(menu_q, menu);
enum ctltype {
CTL_NONE = -1,
CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
CTL_ABORT, CTL_ALL
struct conf {
struct keybinding_q keybindingq;
struct mousebinding_q mousebindingq;
struct autogroupwin_q autogroupq;
struct ignore_q ignoreq;
struct cmd_q cmdq;
#define CONF_STICKY_GROUPS 0x0001
int flags;
#define CONF_BWIDTH 1
int bwidth;
#define CONF_MAMOUNT 1
int mamount;
#define CONF_SNAPDIST 0
int snapdist;
struct gap gap;
char *color[CWM_COLOR_NITEMS];
char known_hosts[PATH_MAX];
#define CONF_FONT "sans-serif:pixelsize=14:bold"
char *font;
Cursor cursor[CF_NITEMS];
};
/* MWM hints */
struct mwm_hints {
u_long flags;
u_long functions;
u_long decorations;
unsigned long flags;
unsigned long functions;
unsigned long decorations;
};
#define MWM_NUMHINTS 3
#define MWM_NUMHINTS 3
#define PROP_MWM_HINTS_ELEMENTS 3
#define MWM_HINTS_DECORATIONS (1 << 1)
#define MWM_DECOR_ALL (1 << 0)
#define MWM_DECOR_BORDER (1 << 1)
#define MWM_HINTS_DECORATIONS (1<<1)
#define MWM_DECOR_ALL (1<<0)
#define MWM_DECOR_BORDER (1<<1)
int input_keycodetrans(KeyCode, u_int, enum ctltype *,
char *);
extern Display *X_Dpy;
extern Time Last_Event_Time;
extern struct screen_ctx_q Screenq;
extern struct conf Conf;
extern const char *homedir;
extern int HasRandr, Randr_ev;
__dead void usage(void);
enum {
WM_STATE,
WM_DELETE_WINDOW,
WM_TAKE_FOCUS,
WM_PROTOCOLS,
_MOTIF_WM_HINTS,
UTF8_STRING,
WM_CHANGE_STATE,
CWMH_NITEMS
};
enum {
_NET_SUPPORTED,
_NET_SUPPORTING_WM_CHECK,
_NET_ACTIVE_WINDOW,
_NET_CLIENT_LIST,
_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,
#define _NET_WM_STATES_NITEMS 6
_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,
EWMH_NITEMS
};
enum {
_NET_WM_STATE_REMOVE,
_NET_WM_STATE_ADD,
_NET_WM_STATE_TOGGLE
};
extern Atom cwmh[CWMH_NITEMS];
extern Atom ewmh[EWMH_NITEMS];
void usage(void);
struct client_ctx *client_find(Window);
struct client_ctx *client_new(Window, struct screen_ctx *, int);
int client_delete(struct client_ctx *);
void client_setactive(struct client_ctx *, int);
void client_resize(struct client_ctx *);
void client_lower(struct client_ctx *);
void client_raise(struct client_ctx *);
void client_move(struct client_ctx *);
void client_leave(struct client_ctx *);
void client_send_delete(struct client_ctx *);
struct client_ctx *client_current(void);
void client_hide(struct client_ctx *);
void client_unhide(struct client_ctx *);
void client_setname(struct client_ctx *);
void client_warp(struct client_ctx *);
void client_ptrwarp(struct client_ctx *);
void client_ptrsave(struct client_ctx *);
void client_draw_border(struct client_ctx *);
void client_maximize(struct client_ctx *);
void client_vertmaximize(struct client_ctx *);
void client_horizmaximize(struct client_ctx *);
void client_map(struct client_ctx *);
void client_mtf(struct client_ctx *);
struct client_ctx *client_cycle(struct screen_ctx *, int);
void client_getsizehints(struct client_ctx *);
void client_applysizehints(struct client_ctx *);
void client_config(struct client_ctx *);
struct client_ctx *client_current(void);
void client_cycle(struct screen_ctx *, int);
void client_cycle_leave(struct screen_ctx *);
void client_delete(struct client_ctx *);
void client_draw_border(struct client_ctx *);
struct client_ctx *client_find(Window);
long client_get_wm_state(struct client_ctx *);
void client_getsizehints(struct client_ctx *);
void client_hide(struct client_ctx *);
void client_htile(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 *);
struct client_ctx *client_init(Window, struct screen_ctx *);
void client_ptrsave(struct client_ctx *);
void client_ptrwarp(struct client_ctx *);
void client_raise(struct client_ctx *);
void client_resize(struct client_ctx *, int);
void client_send_delete(struct client_ctx *);
void client_set_wm_state(struct client_ctx *, long);
void client_setactive(struct client_ctx *);
void client_setname(struct client_ctx *);
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_hmaximize(struct client_ctx *);
void client_toggle_maximize(struct client_ctx *);
void client_toggle_sticky(struct client_ctx *);
void client_toggle_vmaximize(struct client_ctx *);
void client_transient(struct client_ctx *);
void client_unhide(struct client_ctx *);
void client_urgency(struct client_ctx *);
void client_vtile(struct client_ctx *);
void client_warp(struct client_ctx *);
void client_wm_hints(struct client_ctx *);
struct menu *menu_filter(struct screen_ctx *, struct menu_q *,
char *, char *, int,
void (*)(struct menu_q *, struct menu_q *, char *),
void (*)(struct menu *, int));
void menu_init(struct screen_ctx *);
void group_alltoggle(struct screen_ctx *);
void group_autogroup(struct client_ctx *);
void group_cycle(struct screen_ctx *, int);
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_sticky(struct group_ctx *);
void group_init(struct screen_ctx *, int);
void group_movetogroup(struct client_ctx *, int);
void group_only(struct screen_ctx *, int);
void group_show(struct group_ctx *);
void group_toggle_membership_enter(struct client_ctx *);
void group_toggle_membership_leave(struct client_ctx *);
void group_update_names(struct screen_ctx *);
/* XXX should be xu_ */
void xev_reconfig(struct client_ctx *);
void search_match_client(struct menu_q *, struct menu_q *,
char *);
void search_match_exec(struct menu_q *, struct menu_q *,
char *);
void search_match_exec_path(struct menu_q *,
struct menu_q *, char *);
void search_match_path_any(struct menu_q *, struct menu_q *,
char *);
void search_match_text(struct menu_q *, struct menu_q *,
char *);
void search_print_client(struct menu *, int);
void xev_loop(void);
void xu_getatoms(void);
int xu_ptr_grab(Window, int, Cursor);
void xu_btn_grab(Window, int, u_int);
int xu_ptr_regrab(int, Cursor);
void xu_btn_ungrab(Window, int, u_int);
void xu_ptr_ungrab(void);
void xu_ptr_setpos(Window, int, int);
void xu_ptr_getpos(Window, int *, int *);
void xu_key_grab(Window, int, int);
void xu_key_ungrab(Window, int, int);
void xu_sendmsg(Window, Atom, long);
int xu_getprop(Window, Atom, Atom, long, u_char **);
int xu_getstrprop(Window, Atom, char **);
void xu_setstate(struct client_ctx *, int);
int xu_getstate(struct client_ctx *, int *);
unsigned long xu_getcolor(struct screen_ctx *, char *);
void xu_freecolor(struct screen_ctx *, unsigned long);
void xu_setwmname(struct screen_ctx *);
int u_spawn(char *);
void u_exec(char *);
void xfree(void *);
void *xmalloc(size_t);
void *xcalloc(size_t, size_t);
char *xstrdup(const char *);
struct screen_ctx *screen_fromroot(Window);
struct screen_ctx *screen_find(Window);
struct geom screen_find_xinerama(struct screen_ctx *,
int, int, int);
void screen_init(int);
void screen_update_geometry(struct screen_ctx *);
void screen_updatestackingorder(struct screen_ctx *);
void screen_update_geometry(struct screen_ctx *, int, int);
void screen_init_xinerama(struct screen_ctx *);
XineramaScreenInfo *screen_find_xinerama(struct screen_ctx *, int, int);
void conf_setup(struct conf *, const char *);
void conf_client(struct client_ctx *);
void conf_grab(struct conf *, struct keybinding *);
void conf_ungrab(struct conf *, struct keybinding *);
void conf_bindname(struct conf *, char *, char *);
void conf_mousebind(struct conf *, char *, char *);
void conf_grab_mouse(struct client_ctx *);
void conf_reload(struct conf *);
void conf_gap(struct conf *, struct screen_ctx *);
void conf_font(struct conf *, struct screen_ctx *);
void conf_color(struct conf *, struct screen_ctx *);
void conf_init(struct conf *);
void conf_clear(struct conf *);
void conf_cmd_add(struct conf *, char *, char *, int);
int parse_config(const char *, struct conf *);
void kbfunc_client_lower(struct client_ctx *, union arg *);
void kbfunc_client_raise(struct client_ctx *, union arg *);
void kbfunc_client_search(struct client_ctx *, union arg *);
void kbfunc_client_hide(struct client_ctx *, union arg *);
void kbfunc_client_cycle(struct client_ctx *, union arg *);
void kbfunc_client_rcycle(struct client_ctx *, union arg *);
void kbfunc_cmdexec(struct client_ctx *, union arg *);
void kbfunc_client_label(struct client_ctx *, union arg *);
void kbfunc_client_cyclegroup(struct client_ctx *,
union arg *);
void kbfunc_client_delete(struct client_ctx *, union arg *);
void kbfunc_client_group(struct client_ctx *, union arg *);
void kbfunc_client_grouponly(struct client_ctx *,
union arg *);
void kbfunc_client_cyclegroup(struct client_ctx *,
union arg *);
void kbfunc_client_nogroup(struct client_ctx *,
union arg *);
void kbfunc_client_grouptoggle(struct client_ctx *,
union arg *);
void kbfunc_client_hide(struct client_ctx *, union arg *);
void kbfunc_client_label(struct client_ctx *, union arg *);
void kbfunc_client_lower(struct client_ctx *, union arg *);
void kbfunc_client_moveresize(struct client_ctx *,
union arg *);
void kbfunc_client_movetogroup(struct client_ctx *,
union arg *);
void kbfunc_client_maximize(struct client_ctx *,
void kbfunc_client_nogroup(struct client_ctx *,
union arg *);
void kbfunc_client_vmaximize(struct client_ctx *,
void kbfunc_client_raise(struct client_ctx *, union arg *);
void kbfunc_client_rcycle(struct client_ctx *, union arg *);
void kbfunc_client_search(struct client_ctx *, union arg *);
void kbfunc_client_toggle_freeze(struct client_ctx *,
union arg *);
void kbfunc_client_toggle_fullscreen(struct client_ctx *,
union arg *);
void kbfunc_client_hmaximize(struct client_ctx *,
void kbfunc_client_toggle_hmaximize(struct client_ctx *,
union arg *);
void kbfunc_reload(struct client_ctx *, union arg *);
void kbfunc_quit_wm(struct client_ctx *, union arg *);
void kbfunc_moveresize(struct client_ctx *, union arg *);
void kbfunc_menu_search(struct client_ctx *, union arg *);
void kbfunc_client_toggle_maximize(struct client_ctx *,
union arg *);
void kbfunc_client_toggle_sticky(struct client_ctx *,
union arg *);
void kbfunc_client_toggle_vmaximize(struct client_ctx *,
union arg *);
void kbfunc_cmdexec(struct client_ctx *, union arg *);
void kbfunc_cwm_status(struct client_ctx *, union arg *);
void kbfunc_exec(struct client_ctx *, union arg *);
void kbfunc_lock(struct client_ctx *, union arg *);
void kbfunc_menu_cmd(struct client_ctx *, union arg *);
void kbfunc_ssh(struct client_ctx *, union arg *);
void kbfunc_term(struct client_ctx *, union arg *);
void kbfunc_lock(struct client_ctx *, union arg *);
void kbfunc_tile(struct client_ctx *, union arg *);
void mousefunc_window_resize(struct client_ctx *, void *);
void mousefunc_window_move(struct client_ctx *, void *);
void mousefunc_window_grouptoggle(struct client_ctx *,
void *);
void mousefunc_window_lower(struct client_ctx *, void *);
void mousefunc_window_hide(struct client_ctx *, void *);
void mousefunc_menu_group(struct client_ctx *, void *);
void mousefunc_menu_unhide(struct client_ctx *, void *);
void mousefunc_menu_cmd(struct client_ctx *, void *);
void mousefunc_client_grouptoggle(struct client_ctx *,
union arg *);
void mousefunc_client_move(struct client_ctx *,
union arg *);
void mousefunc_client_resize(struct client_ctx *,
union arg *);
void mousefunc_menu_cmd(struct client_ctx *, union arg *);
void mousefunc_menu_group(struct client_ctx *, union arg *);
void mousefunc_menu_unhide(struct client_ctx *,
union arg *);
void search_match_client(struct menu_q *, struct menu_q *,
char *);
void search_print_client(struct menu *, int);
void search_match_text(struct menu_q *, struct menu_q *,
char *);
void search_match_exec(struct menu_q *, struct menu_q *,
char *);
struct menu *menu_filter(struct screen_ctx *, struct menu_q *,
const char *, const char *, int,
void (*)(struct menu_q *, struct menu_q *, char *),
void (*)(struct menu *, int));
void menuq_add(struct menu_q *, void *, const char *, ...);
void menuq_clear(struct menu_q *);
void group_init(struct screen_ctx *);
void group_make_autogroup(struct conf *, char *, int);
void group_update_names(struct screen_ctx *);
void group_hidetoggle(struct screen_ctx *, int);
void group_only(struct screen_ctx *, int);
void group_cycle(struct screen_ctx *, int);
void group_sticky(struct client_ctx *);
void group_client_delete(struct client_ctx *);
void group_menu(XButtonEvent *);
void group_alltoggle(struct screen_ctx *);
void group_sticky_toggle_enter(struct client_ctx *);
void group_sticky_toggle_exit(struct client_ctx *);
void group_autogroup(struct client_ctx *);
void group_movetogroup(struct client_ctx *, int);
int parse_config(const char *, struct conf *);
int font_ascent(struct screen_ctx *);
int font_descent(struct screen_ctx *);
u_int font_height(struct screen_ctx *);
void font_init(struct screen_ctx *);
int font_width(struct screen_ctx *, const char *, int);
void font_draw(struct screen_ctx *, const char *, int,
Drawable, int, int);
XftFont *font_make(struct screen_ctx *, const char *);
void conf_atoms(void);
void conf_autogroup(struct conf *, int, const char *);
int conf_bind_kbd(struct conf *, const char *,
const char *);
int conf_bind_mouse(struct conf *, const char *,
const char *);
void conf_clear(struct conf *);
void conf_client(struct client_ctx *);
int conf_cmd_add(struct conf *, const char *,
const char *);
void conf_cursor(struct conf *);
void conf_grab_kbd(Window);
void conf_grab_mouse(Window);
void conf_init(struct conf *);
void conf_ignore(struct conf *, const char *);
void conf_screen(struct screen_ctx *);
/* Externs */
void xev_process(void);
extern Display *X_Dpy;
void xu_btn_grab(Window, int, unsigned int);
void xu_btn_ungrab(Window);
int xu_getprop(Window, Atom, Atom, long, unsigned char **);
int xu_getstrprop(Window, Atom, char **);
void xu_key_grab(Window, unsigned int, KeySym);
void xu_key_ungrab(Window);
void xu_ptr_getpos(Window, int *, int *);
int xu_ptr_grab(Window, unsigned int, Cursor);
int xu_ptr_regrab(unsigned int, Cursor);
void xu_ptr_setpos(Window, int, int);
void xu_ptr_ungrab(void);
void xu_xft_draw(struct screen_ctx *, const char *,
int, int, int);
int xu_xft_width(XftFont *, const char *, int);
void xu_xorcolor(XftColor, XftColor, XftColor *);
extern Cursor Cursor_move;
extern Cursor Cursor_resize;
extern Cursor Cursor_select;
extern Cursor Cursor_default;
extern Cursor Cursor_question;
void xu_ewmh_net_supported(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_workarea(struct screen_ctx *);
void xu_ewmh_net_client_list(struct screen_ctx *);
void xu_ewmh_net_active_window(struct screen_ctx *, Window);
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_virtual_roots(struct screen_ctx *);
void xu_ewmh_net_current_desktop(struct screen_ctx *);
void xu_ewmh_net_desktop_names(struct screen_ctx *);
extern struct screen_ctx_q Screenq;
extern struct screen_ctx *curscreen;
void xu_ewmh_net_wm_desktop(struct client_ctx *);
Atom *xu_ewmh_get_net_wm_state(struct client_ctx *, int *);
void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *,
int, Atom , Atom);
void xu_ewmh_set_net_wm_state(struct client_ctx *);
void xu_ewmh_restore_net_wm_state(struct client_ctx *);
extern struct client_ctx_q Clientq;
void u_exec(char *);
void u_spawn(char *);
extern int HasXinerama, HasRandr, Randr_ev;
extern struct conf Conf;
#define WM_STATE cwm_atoms[0]
#define WM_DELETE_WINDOW cwm_atoms[1]
#define WM_TAKE_FOCUS cwm_atoms[2]
#define WM_PROTOCOLS cwm_atoms[3]
#define _MOTIF_WM_HINTS cwm_atoms[4]
#define UTF8_STRING cwm_atoms[5]
/*
* please make all hints below this point netwm hints, starting with
* _NET_SUPPORTED. If you change other hints make sure you update
* CWM_NETWM_START
*/
#define _NET_SUPPORTED cwm_atoms[6]
#define _NET_SUPPORTING_WM_CHECK cwm_atoms[7]
#define _NET_WM_NAME cwm_atoms[8]
#define _NET_ACTIVE_WINDOW cwm_atoms[9]
#define _NET_CLIENT_LIST cwm_atoms[10]
#define _NET_NUMBER_OF_DESKTOPS cwm_atoms[11]
#define _NET_CURRENT_DESKTOP cwm_atoms[12]
#define _NET_DESKTOP_VIEWPORT cwm_atoms[13]
#define _NET_DESKTOP_GEOMETRY cwm_atoms[14]
#define _NET_VIRTUAL_ROOTS cwm_atoms[15]
#define _NET_SHOWING_DESKTOP cwm_atoms[16]
#define _NET_DESKTOP_NAMES cwm_atoms[17]
#define _NET_WM_DESKTOP cwm_atoms[18]
#define _NET_WORKAREA cwm_atoms[19]
#define CWM_NO_ATOMS 20
#define CWM_NETWM_START 6
extern Atom cwm_atoms[CWM_NO_ATOMS];
void *xcalloc(size_t, size_t);
void *xmalloc(size_t);
char *xstrdup(const char *);
int xasprintf(char **, const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
#endif /* _CALMWM_H_ */

1129
client.c

File diff suppressed because it is too large Load Diff

803
conf.c
View File

@ -15,99 +15,159 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#ifndef timespeccmp
#define timespeccmp(tsp, usp, cmp) \
(((tsp)->tv_sec == (usp)->tv_sec) ? \
((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
((tsp)->tv_sec cmp (usp)->tv_sec))
#endif
static const char *conf_bind_getmask(const char *, unsigned int *);
static void conf_cmd_remove(struct conf *, const char *);
static void conf_unbind_kbd(struct conf *, struct binding *);
static void conf_unbind_mouse(struct conf *, struct binding *);
static void conf_mouseunbind(struct conf *, struct mousebinding *);
static void conf_unbind(struct conf *, struct keybinding *);
/* Add an command menu entry to the end of the menu */
void
conf_cmd_add(struct conf *c, char *image, char *label, int flags)
int
conf_cmd_add(struct conf *c, const char *name, const char *path)
{
/* "term" and "lock" have special meanings. */
struct cmd *cmd;
if (strcmp(label, "term") == 0)
strlcpy(c->termpath, image, sizeof(c->termpath));
else if (strcmp(label, "lock") == 0)
strlcpy(c->lockpath, image, sizeof(c->lockpath));
else {
struct cmd *cmd = xmalloc(sizeof(*cmd));
cmd->flags = flags;
strlcpy(cmd->image, image, sizeof(cmd->image));
strlcpy(cmd->label, label, sizeof(cmd->label));
TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry);
}
}
cmd = xmalloc(sizeof(*cmd));
void
conf_gap(struct conf *c, struct screen_ctx *sc)
{
sc->gap = c->gap;
}
void
conf_font(struct conf *c, struct screen_ctx *sc)
{
sc->font = font_make(sc, c->DefaultFontName);
sc->fontheight = font_ascent(sc) + font_descent(sc) + 1;
}
void
conf_color(struct conf *c, struct screen_ctx *sc)
{
int i;
for (i = 0; i < CWM_COLOR_MAX; i++) {
xu_freecolor(sc, sc->color[i].pixel);
sc->color[i].pixel = xu_getcolor(sc, c->color[i].name);
}
}
void
conf_reload(struct conf *c)
{
struct screen_ctx *sc;
struct client_ctx *cc;
if (parse_config(c->conf_path, c) == -1) {
warnx("config file %s has errors, not reloading", c->conf_path);
return;
cmd->name = xstrdup(name);
if (strlcpy(cmd->path, path, sizeof(cmd->path)) >= sizeof(cmd->path)) {
free(cmd->name);
free(cmd);
return(0);
}
TAILQ_FOREACH(cc, &Clientq, entry)
client_draw_border(cc);
TAILQ_FOREACH(sc, &Screenq, entry) {
conf_gap(c, sc);
conf_color(c, sc);
conf_font(c, sc);
}
conf_cmd_remove(c, name);
TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry);
return(1);
}
static struct {
char *key;
char *func;
} kb_binds[] = {
static void
conf_cmd_remove(struct conf *c, const char *name)
{
struct cmd *cmd = NULL, *cmdnxt;
TAILQ_FOREACH_SAFE(cmd, &c->cmdq, entry, cmdnxt) {
if (strcmp(cmd->name, name) == 0) {
TAILQ_REMOVE(&c->cmdq, cmd, entry);
free(cmd->name);
free(cmd);
}
}
}
void
conf_autogroup(struct conf *c, int num, const char *val)
{
struct autogroupwin *aw;
char *p;
aw = xcalloc(1, sizeof(*aw));
if ((p = strchr(val, ',')) == NULL) {
aw->name = NULL;
aw->class = xstrdup(val);
} else {
*(p++) = '\0';
aw->name = xstrdup(val);
aw->class = xstrdup(p);
}
aw->num = num;
TAILQ_INSERT_TAIL(&c->autogroupq, aw, entry);
}
void
conf_ignore(struct conf *c, const char *name)
{
struct winname *wn;
wn = xmalloc(sizeof(*wn));
wn->name = xstrdup(name);
TAILQ_INSERT_TAIL(&c->ignoreq, wn, entry);
}
static const char *color_binds[] = {
"#CCCCCC", /* CWM_COLOR_BORDER_ACTIVE */
"#666666", /* CWM_COLOR_BORDER_INACTIVE */
"#FC8814", /* CWM_COLOR_BORDER_URGENCY */
"blue", /* CWM_COLOR_BORDER_GROUP */
"red", /* CWM_COLOR_BORDER_UNGROUP */
"black", /* CWM_COLOR_MENU_FG */
"white", /* CWM_COLOR_MENU_BG */
"black", /* CWM_COLOR_MENU_FONT */
"", /* CWM_COLOR_MENU_FONT_SEL */
};
void
conf_screen(struct screen_ctx *sc)
{
unsigned int i;
XftColor xc;
Colormap colormap = DefaultColormap(X_Dpy, sc->which);
Visual *visual = DefaultVisual(X_Dpy, sc->which);
sc->gap = Conf.gap;
sc->snapdist = Conf.snapdist;
sc->xftfont = XftFontOpenXlfd(X_Dpy, sc->which, Conf.font);
if (sc->xftfont == NULL) {
sc->xftfont = XftFontOpenName(X_Dpy, sc->which, Conf.font);
if (sc->xftfont == NULL)
errx(1, "XftFontOpenName");
}
for (i = 0; i < nitems(color_binds); i++) {
if (i == CWM_COLOR_MENU_FONT_SEL && *Conf.color[i] == '\0') {
xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_BG],
sc->xftcolor[CWM_COLOR_MENU_FG], &xc);
xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_FONT], xc, &xc);
if (!XftColorAllocValue(X_Dpy, visual, colormap,
&xc.color, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL]))
warnx("XftColorAllocValue: %s", Conf.color[i]);
break;
}
if (XftColorAllocName(X_Dpy, visual, colormap,
Conf.color[i], &xc)) {
sc->xftcolor[i] = xc;
XftColorFree(X_Dpy, visual, colormap, &xc);
} else {
warnx("XftColorAllocName: %s", Conf.color[i]);
XftColorAllocName(X_Dpy, visual, colormap,
color_binds[i], &sc->xftcolor[i]);
}
}
sc->menuwin = 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->xftdraw = XftDrawCreate(X_Dpy, sc->menuwin, visual, colormap);
if (sc->xftdraw == NULL)
errx(1, "XftDrawCreate");
conf_grab_kbd(sc->rootwin);
}
static const struct {
const char *key;
const char *func;
} kbd_binds[] = {
{ "CM-Return", "terminal" },
{ "CM-Delete", "lock" },
{ "M-question", "exec" },
@ -135,10 +195,13 @@ static struct {
{ "M-Right", "cyclegroup" },
{ "M-Left", "rcyclegroup" },
{ "CM-g", "grouptoggle" },
{ "CM-f", "maximize" },
{ "CM-f", "fullscreen" },
{ "CM-m", "maximize" },
{ "CM-s", "sticky" },
{ "CM-equal", "vmaximize" },
{ "CMS-equal", "hmaximize" },
{ "CMS-r", "reload" },
{ "CMS-f", "freeze" },
{ "CMS-r", "restart" },
{ "CMS-q", "quit" },
{ "M-h", "moveleft" },
{ "M-j", "movedown" },
@ -165,7 +228,7 @@ static struct {
{ "CS-Up", "bigptrmoveup" },
{ "CS-Right", "bigptrmoveright" },
},
m_binds[] = {
mouse_binds[] = {
{ "1", "menu_unhide" },
{ "2", "menu_group" },
{ "3", "menu_cmd" },
@ -179,11 +242,11 @@ m_binds[] = {
void
conf_init(struct conf *c)
{
int i;
unsigned int i;
c->flags = 0;
c->bwidth = CONF_BWIDTH;
c->mamount = CONF_MAMOUNT;
c->snapdist = CONF_SNAPDIST;
TAILQ_INIT(&c->ignoreq);
TAILQ_INIT(&c->cmdq);
@ -191,109 +254,76 @@ conf_init(struct conf *c)
TAILQ_INIT(&c->autogroupq);
TAILQ_INIT(&c->mousebindingq);
for (i = 0; i < sizeof(kb_binds) / sizeof(kb_binds[0]); i++)
conf_bindname(c, kb_binds[i].key, kb_binds[i].func);
for (i = 0; i < nitems(kbd_binds); i++)
conf_bind_kbd(c, kbd_binds[i].key, kbd_binds[i].func);
for (i = 0; i < sizeof(m_binds) / sizeof(m_binds[0]); i++)
conf_mousebind(c, m_binds[i].key, m_binds[i].func);
for (i = 0; i < nitems(mouse_binds); i++)
conf_bind_mouse(c, mouse_binds[i].key, mouse_binds[i].func);
/* Default term/lock */
strlcpy(c->termpath, "xterm", sizeof(c->termpath));
strlcpy(c->lockpath, "xlock", sizeof(c->lockpath));
for (i = 0; i < nitems(color_binds); i++)
c->color[i] = xstrdup(color_binds[i]);
c->color[CWM_COLOR_BORDOR_ACTIVE].name =
xstrdup(CONF_COLOR_ACTIVEBORDER);
c->color[CWM_COLOR_BORDER_INACTIVE].name =
xstrdup(CONF_COLOR_INACTIVEBORDER);
c->color[CWM_COLOR_BORDER_GROUP].name =
xstrdup(CONF_COLOR_GROUPBORDER);
c->color[CWM_COLOR_BORDER_UNGROUP].name =
xstrdup(CONF_COLOR_UNGROUPBORDER);
c->color[CWM_COLOR_FG_MENU].name =
xstrdup(CONF_COLOR_MENUFG);
c->color[CWM_COLOR_BG_MENU].name =
xstrdup(CONF_COLOR_MENUBG);
conf_cmd_add(c, "lock", "xlock");
conf_cmd_add(c, "term", "xterm");
c->DefaultFontName = xstrdup(DEFAULTFONTNAME);
(void)snprintf(c->known_hosts, sizeof(c->known_hosts), "%s/%s",
homedir, ".ssh/known_hosts");
c->font = xstrdup(CONF_FONT);
}
void
conf_clear(struct conf *c)
{
struct autogroupwin *ag;
struct keybinding *kb;
struct winmatch *wm;
struct autogroupwin *aw;
struct binding *kb, *mb;
struct winname *wn;
struct cmd *cmd;
struct mousebinding *mb;
int i;
while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) {
TAILQ_REMOVE(&c->cmdq, cmd, entry);
xfree(cmd);
free(cmd->name);
free(cmd);
}
while ((kb = TAILQ_FIRST(&c->keybindingq)) != NULL) {
TAILQ_REMOVE(&c->keybindingq, kb, entry);
xfree(kb);
free(kb);
}
while ((ag = TAILQ_FIRST(&c->autogroupq)) != NULL) {
TAILQ_REMOVE(&c->autogroupq, ag, entry);
xfree(ag->class);
if (ag->name)
xfree(ag->name);
xfree(ag);
while ((aw = TAILQ_FIRST(&c->autogroupq)) != NULL) {
TAILQ_REMOVE(&c->autogroupq, aw, entry);
free(aw->class);
free(aw->name);
free(aw);
}
while ((wm = TAILQ_FIRST(&c->ignoreq)) != NULL) {
TAILQ_REMOVE(&c->ignoreq, wm, entry);
xfree(wm);
while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) {
TAILQ_REMOVE(&c->ignoreq, wn, entry);
free(wn->name);
free(wn);
}
while ((mb = TAILQ_FIRST(&c->mousebindingq)) != NULL) {
TAILQ_REMOVE(&c->mousebindingq, mb, entry);
xfree(mb);
free(mb);
}
for (i = 0; i < CWM_COLOR_MAX; i++)
xfree(c->color[i].name);
for (i = 0; i < CWM_COLOR_NITEMS; i++)
free(c->color[i]);
xfree(c->DefaultFontName);
}
void
conf_setup(struct conf *c, const char *conf_file)
{
struct stat sb;
if (conf_file == NULL) {
char *home = getenv("HOME");
if (home == NULL)
errx(1, "No HOME directory.");
snprintf(c->conf_path, sizeof(c->conf_path), "%s/%s", home,
CONFFILE);
} else
if (stat(conf_file, &sb) == -1 || !(sb.st_mode & S_IFREG))
errx(1, "%s: %s", conf_file, strerror(errno));
else
strlcpy(c->conf_path, conf_file, sizeof(c->conf_path));
conf_init(c);
(void)parse_config(c->conf_path, c);
free(c->font);
}
void
conf_client(struct client_ctx *cc)
{
struct winmatch *wm;
char *wname = cc->name;
struct winname *wn;
int ignore = 0;
TAILQ_FOREACH(wm, &Conf.ignoreq, entry) {
if (strncasecmp(wm->title, wname, strlen(wm->title)) == 0) {
TAILQ_FOREACH(wn, &Conf.ignoreq, entry) {
if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) {
ignore = 1;
break;
}
@ -303,21 +333,21 @@ conf_client(struct client_ctx *cc)
cc->flags |= ignore ? CLIENT_IGNORE : 0;
}
static struct {
char *tag;
static const struct {
const char *tag;
void (*handler)(struct client_ctx *, union arg *);
int flags;
union arg argument;
} name_to_kbfunc[] = {
{ "lower", kbfunc_client_lower, KBFLAG_NEEDCLIENT, {0} },
{ "raise", kbfunc_client_raise, KBFLAG_NEEDCLIENT, {0} },
} name_to_func[] = {
{ "lower", kbfunc_client_lower, CWM_WIN, {0} },
{ "raise", kbfunc_client_raise, CWM_WIN, {0} },
{ "search", kbfunc_client_search, 0, {0} },
{ "menusearch", kbfunc_menu_search, 0, {0} },
{ "hide", kbfunc_client_hide, KBFLAG_NEEDCLIENT, {0} },
{ "menusearch", kbfunc_menu_cmd, 0, {0} },
{ "hide", kbfunc_client_hide, CWM_WIN, {0} },
{ "cycle", kbfunc_client_cycle, 0, {.i = CWM_CYCLE} },
{ "rcycle", kbfunc_client_cycle, 0, {.i = CWM_RCYCLE} },
{ "label", kbfunc_client_label, KBFLAG_NEEDCLIENT, {0} },
{ "delete", kbfunc_client_delete, KBFLAG_NEEDCLIENT, {0} },
{ "label", kbfunc_client_label, CWM_WIN, {0} },
{ "delete", kbfunc_client_delete, CWM_WIN, {0} },
{ "group1", kbfunc_client_group, 0, {.i = 1} },
{ "group2", kbfunc_client_group, 0, {.i = 2} },
{ "group3", kbfunc_client_group, 0, {.i = 3} },
@ -336,120 +366,100 @@ static struct {
{ "grouponly7", kbfunc_client_grouponly, 0, {.i = 7} },
{ "grouponly8", kbfunc_client_grouponly, 0, {.i = 8} },
{ "grouponly9", kbfunc_client_grouponly, 0, {.i = 9} },
{ "movetogroup1", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 1} },
{ "movetogroup2", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 2} },
{ "movetogroup3", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 3} },
{ "movetogroup4", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 4} },
{ "movetogroup5", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 5} },
{ "movetogroup6", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 6} },
{ "movetogroup7", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 7} },
{ "movetogroup8", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 8} },
{ "movetogroup9", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
{.i = 9} },
{ "movetogroup1", kbfunc_client_movetogroup, CWM_WIN, {.i = 1} },
{ "movetogroup2", kbfunc_client_movetogroup, CWM_WIN, {.i = 2} },
{ "movetogroup3", kbfunc_client_movetogroup, CWM_WIN, {.i = 3} },
{ "movetogroup4", kbfunc_client_movetogroup, CWM_WIN, {.i = 4} },
{ "movetogroup5", kbfunc_client_movetogroup, CWM_WIN, {.i = 5} },
{ "movetogroup6", kbfunc_client_movetogroup, CWM_WIN, {.i = 6} },
{ "movetogroup7", kbfunc_client_movetogroup, CWM_WIN, {.i = 7} },
{ "movetogroup8", kbfunc_client_movetogroup, CWM_WIN, {.i = 8} },
{ "movetogroup9", kbfunc_client_movetogroup, CWM_WIN, {.i = 9} },
{ "nogroup", kbfunc_client_nogroup, 0, {0} },
{ "cyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_CYCLEGROUP} },
{ "rcyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_RCYCLEGROUP} },
{ "grouptoggle", kbfunc_client_grouptoggle, KBFLAG_NEEDCLIENT, {0}},
{ "maximize", kbfunc_client_maximize, KBFLAG_NEEDCLIENT, {0} },
{ "vmaximize", kbfunc_client_vmaximize, KBFLAG_NEEDCLIENT, {0} },
{ "hmaximize", kbfunc_client_hmaximize, KBFLAG_NEEDCLIENT, {0} },
{ "reload", kbfunc_reload, 0, {0} },
{ "quit", kbfunc_quit_wm, 0, {0} },
{ "cyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_CYCLE} },
{ "rcyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_RCYCLE} },
{ "cycleingroup", kbfunc_client_cycle, CWM_WIN,
{.i = CWM_CYCLE|CWM_INGROUP} },
{ "rcycleingroup", kbfunc_client_cycle, CWM_WIN,
{.i = CWM_RCYCLE|CWM_INGROUP} },
{ "grouptoggle", kbfunc_client_grouptoggle, CWM_WIN, {0}},
{ "sticky", kbfunc_client_toggle_sticky, CWM_WIN, {0} },
{ "fullscreen", kbfunc_client_toggle_fullscreen, CWM_WIN, {0} },
{ "maximize", kbfunc_client_toggle_maximize, CWM_WIN, {0} },
{ "vmaximize", kbfunc_client_toggle_vmaximize, CWM_WIN, {0} },
{ "hmaximize", kbfunc_client_toggle_hmaximize, CWM_WIN, {0} },
{ "freeze", kbfunc_client_toggle_freeze, CWM_WIN, {0} },
{ "restart", kbfunc_cwm_status, 0, {.i = CWM_RESTART} },
{ "quit", kbfunc_cwm_status, 0, {.i = CWM_QUIT} },
{ "exec", kbfunc_exec, 0, {.i = CWM_EXEC_PROGRAM} },
{ "exec_wm", kbfunc_exec, 0, {.i = CWM_EXEC_WM} },
{ "ssh", kbfunc_ssh, 0, {0} },
{ "terminal", kbfunc_term, 0, {0} },
{ "lock", kbfunc_lock, 0, {0} },
{ "moveup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "moveup", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_UP|CWM_MOVE)} },
{ "movedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "movedown", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_DOWN|CWM_MOVE)} },
{ "moveright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "moveright", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_RIGHT|CWM_MOVE)} },
{ "moveleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "moveleft", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_LEFT|CWM_MOVE)} },
{ "bigmoveup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigmoveup", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_UP|CWM_MOVE|CWM_BIGMOVE)} },
{ "bigmovedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigmovedown", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_DOWN|CWM_MOVE|CWM_BIGMOVE)} },
{ "bigmoveright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigmoveright", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_RIGHT|CWM_MOVE|CWM_BIGMOVE)} },
{ "bigmoveleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigmoveleft", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_LEFT|CWM_MOVE|CWM_BIGMOVE)} },
{ "resizeup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "resizeup", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_UP|CWM_RESIZE)} },
{ "resizedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "resizedown", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_DOWN|CWM_RESIZE)} },
{ "resizeright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "resizeright", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_RIGHT|CWM_RESIZE)} },
{ "resizeleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "resizeleft", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_LEFT|CWM_RESIZE)} },
{ "bigresizeup", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigresizeup", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_UP|CWM_RESIZE|CWM_BIGMOVE)} },
{ "bigresizedown", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigresizedown", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_DOWN|CWM_RESIZE|CWM_BIGMOVE)} },
{ "bigresizeright", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigresizeright", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_RIGHT|CWM_RESIZE|CWM_BIGMOVE)} },
{ "bigresizeleft", kbfunc_moveresize, KBFLAG_NEEDCLIENT,
{ "bigresizeleft", kbfunc_client_moveresize, CWM_WIN,
{.i = (CWM_LEFT|CWM_RESIZE|CWM_BIGMOVE)} },
{ "ptrmoveup", kbfunc_moveresize, 0, {.i = (CWM_UP|CWM_PTRMOVE)} },
{ "ptrmovedown", kbfunc_moveresize, 0, {.i = (CWM_DOWN|CWM_PTRMOVE)} },
{ "ptrmoveleft", kbfunc_moveresize, 0, {.i = (CWM_LEFT|CWM_PTRMOVE)} },
{ "ptrmoveright", kbfunc_moveresize, 0,
{ "ptrmoveup", kbfunc_client_moveresize, 0,
{.i = (CWM_UP|CWM_PTRMOVE)} },
{ "ptrmovedown", kbfunc_client_moveresize, 0,
{.i = (CWM_DOWN|CWM_PTRMOVE)} },
{ "ptrmoveleft", kbfunc_client_moveresize, 0,
{.i = (CWM_LEFT|CWM_PTRMOVE)} },
{ "ptrmoveright", kbfunc_client_moveresize, 0,
{.i = (CWM_RIGHT|CWM_PTRMOVE)} },
{ "bigptrmoveup", kbfunc_moveresize, 0,
{ "bigptrmoveup", kbfunc_client_moveresize, 0,
{.i = (CWM_UP|CWM_PTRMOVE|CWM_BIGMOVE)} },
{ "bigptrmovedown", kbfunc_moveresize, 0,
{ "bigptrmovedown", kbfunc_client_moveresize, 0,
{.i = (CWM_DOWN|CWM_PTRMOVE|CWM_BIGMOVE)} },
{ "bigptrmoveleft", kbfunc_moveresize, 0,
{ "bigptrmoveleft", kbfunc_client_moveresize, 0,
{.i = (CWM_LEFT|CWM_PTRMOVE|CWM_BIGMOVE)} },
{ "bigptrmoveright", kbfunc_moveresize, 0,
{ "bigptrmoveright", kbfunc_client_moveresize, 0,
{.i = (CWM_RIGHT|CWM_PTRMOVE|CWM_BIGMOVE)} },
{ NULL, NULL, 0, {0}},
{ "htile", kbfunc_tile, CWM_WIN, {.i = CWM_TILE_HORIZ} },
{ "vtile", kbfunc_tile, CWM_WIN, {.i = CWM_TILE_VERT} },
{ "window_lower", kbfunc_client_lower, CWM_WIN, {0} },
{ "window_raise", kbfunc_client_raise, CWM_WIN, {0} },
{ "window_hide", kbfunc_client_hide, CWM_WIN, {0} },
{ "window_move", mousefunc_client_move, CWM_WIN, {0} },
{ "window_resize", mousefunc_client_resize, CWM_WIN, {0} },
{ "window_grouptoggle", mousefunc_client_grouptoggle, CWM_WIN, {0} },
{ "menu_group", mousefunc_menu_group, 0, {0} },
{ "menu_unhide", mousefunc_menu_unhide, 0, {0} },
{ "menu_cmd", mousefunc_menu_cmd, 0, {0} },
};
/*
* The following two functions are used when grabbing and ungrabbing keys for
* bindings
*/
/*
* Grab key combination on all screens and add to the global queue
*/
void
conf_grab(struct conf *c, struct keybinding *kb)
{
extern struct screen_ctx_q Screenq;
struct screen_ctx *sc;
TAILQ_FOREACH(sc, &Screenq, entry)
xu_key_grab(sc->rootwin, kb->modmask, kb->keysym);
}
/*
* Ungrab key combination from all screens and remove from global queue
*/
void
conf_ungrab(struct conf *c, struct keybinding *kb)
{
extern struct screen_ctx_q Screenq;
struct screen_ctx *sc;
TAILQ_FOREACH(sc, &Screenq, entry)
xu_key_ungrab(sc->rootwin, kb->modmask, kb->keysym);
}
static struct {
char chr;
int mask;
static const struct {
const char ch;
int mask;
} bind_mods[] = {
{ 'C', ControlMask },
{ 'M', Mod1Mask },
@ -457,202 +467,223 @@ static struct {
{ 'S', ShiftMask },
};
void
conf_bindname(struct conf *c, char *name, char *binding)
static const char *
conf_bind_getmask(const char *name, unsigned int *mask)
{
struct keybinding *current_binding;
char *substring, *tmp;
int iter;
char *dash;
const char *ch;
unsigned int i;
current_binding = xcalloc(1, sizeof(*current_binding));
if ((substring = strchr(name, '-')) != NULL) {
for (iter = 0; iter < (sizeof(bind_mods) /
sizeof(bind_mods[0])); iter++) {
if ((tmp = strchr(name, bind_mods[iter].chr)) !=
NULL && tmp < substring) {
current_binding->modmask |=
bind_mods[iter].mask;
}
}
/* skip past the modifiers */
substring++;
} else {
substring = name;
*mask = 0;
if ((dash = strchr(name, '-')) == NULL)
return(name);
for (i = 0; i < nitems(bind_mods); i++) {
if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash)
*mask |= bind_mods[i].mask;
}
if (substring[0] == '[' &&
substring[strlen(substring)-1] == ']') {
sscanf(substring, "[%d]", &current_binding->keycode);
current_binding->keysym = NoSymbol;
} else {
current_binding->keycode = 0;
current_binding->keysym = XStringToKeysym(substring);
}
/* Skip past modifiers. */
return(dash + 1);
}
if (current_binding->keysym == NoSymbol &&
current_binding->keycode == 0) {
xfree(current_binding);
return;
int
conf_bind_kbd(struct conf *c, const char *bind, const char *cmd)
{
struct binding *kb;
const char *key;
unsigned int i, mask;
kb = xcalloc(1, sizeof(*kb));
key = conf_bind_getmask(bind, &mask);
kb->modmask |= mask;
kb->press.keysym = XStringToKeysym(key);
if (kb->press.keysym == NoSymbol) {
warnx("unknown symbol: %s", key);
free(kb);
return(0);
}
/* We now have the correct binding, remove duplicates. */
conf_unbind(c, current_binding);
conf_unbind_kbd(c, kb);
if (strcmp("unmap", binding) == 0)
return;
for (iter = 0; name_to_kbfunc[iter].tag != NULL; iter++) {
if (strcmp(name_to_kbfunc[iter].tag, binding) != 0)
continue;
current_binding->callback = name_to_kbfunc[iter].handler;
current_binding->flags = name_to_kbfunc[iter].flags;
current_binding->argument = name_to_kbfunc[iter].argument;
conf_grab(c, current_binding);
TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
return;
if (strcmp("unmap", cmd) == 0) {
free(kb);
return(1);
}
current_binding->callback = kbfunc_cmdexec;
current_binding->argument.c = xstrdup(binding);
current_binding->flags = 0;
conf_grab(c, current_binding);
TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
return;
for (i = 0; i < nitems(name_to_func); i++) {
if (strcmp(name_to_func[i].tag, cmd) != 0)
continue;
kb->callback = name_to_func[i].handler;
kb->flags = name_to_func[i].flags;
kb->argument = name_to_func[i].argument;
kb->argtype |= ARG_INT;
TAILQ_INSERT_TAIL(&c->keybindingq, kb, entry);
return(1);
}
kb->callback = kbfunc_cmdexec;
kb->flags = 0;
kb->argument.c = xstrdup(cmd);
kb->argtype |= ARG_CHAR;
TAILQ_INSERT_TAIL(&c->keybindingq, kb, entry);
return(1);
}
static void
conf_unbind(struct conf *c, struct keybinding *unbind)
conf_unbind_kbd(struct conf *c, struct binding *unbind)
{
struct keybinding *key = NULL, *keynxt;
for (key = TAILQ_FIRST(&c->keybindingq);
key != TAILQ_END(&c->keybindingq); key = keynxt) {
keynxt = TAILQ_NEXT(key, entry);
struct binding *key = NULL, *keynxt;
TAILQ_FOREACH_SAFE(key, &c->keybindingq, entry, keynxt) {
if (key->modmask != unbind->modmask)
continue;
if ((key->keycode != 0 && key->keysym == NoSymbol &&
key->keycode == unbind->keycode) ||
key->keysym == unbind->keysym) {
conf_ungrab(c, key);
if (key->press.keysym == unbind->press.keysym) {
TAILQ_REMOVE(&c->keybindingq, key, entry);
xfree(key);
if (key->argtype & ARG_CHAR)
free(key->argument.c);
free(key);
}
}
}
static struct {
char *tag;
void (*handler)(struct client_ctx *, void *);
int context;
} name_to_mousefunc[] = {
{ "window_move", mousefunc_window_move, MOUSEBIND_CTX_WIN },
{ "window_resize", mousefunc_window_resize, MOUSEBIND_CTX_WIN },
{ "window_grouptoggle", mousefunc_window_grouptoggle,
MOUSEBIND_CTX_WIN },
{ "window_lower", mousefunc_window_lower, MOUSEBIND_CTX_WIN },
{ "window_hide", mousefunc_window_hide, MOUSEBIND_CTX_WIN },
{ "menu_group", mousefunc_menu_group, MOUSEBIND_CTX_ROOT },
{ "menu_unhide", mousefunc_menu_unhide, MOUSEBIND_CTX_ROOT },
{ "menu_cmd", mousefunc_menu_cmd, MOUSEBIND_CTX_ROOT },
{ NULL, NULL, 0 },
};
void
conf_mousebind(struct conf *c, char *name, char *binding)
int
conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
{
struct mousebinding *current_binding;
char *substring, *tmp;
const char *errstr;
int iter;
struct binding *mb;
const char *button, *errstr;
unsigned int i, mask;
current_binding = xcalloc(1, sizeof(*current_binding));
mb = xcalloc(1, sizeof(*mb));
button = conf_bind_getmask(bind, &mask);
mb->modmask |= mask;
if ((substring = strchr(name, '-')) != NULL) {
for (iter = 0; iter < (sizeof(bind_mods) /
sizeof(bind_mods[0])); iter++) {
if ((tmp = strchr(name, bind_mods[iter].chr)) !=
NULL && tmp < substring) {
current_binding->modmask |=
bind_mods[iter].mask;
}
}
mb->press.button = strtonum(button, Button1, Button5, &errstr);
if (errstr) {
warnx("button number is %s: %s", errstr, button);
free(mb);
return(0);
}
/* skip past the modifiers */
substring++;
} else
substring = name;
/* We now have the correct binding, remove duplicates. */
conf_unbind_mouse(c, mb);
current_binding->button = strtonum(substring, 1, 3, &errstr);
if (errstr)
warnx("number of buttons is %s: %s", errstr, substring);
if (strcmp("unmap", cmd) == 0) {
free(mb);
return(1);
}
conf_mouseunbind(c, current_binding);
if (strcmp("unmap", binding) == 0)
return;
for (iter = 0; name_to_mousefunc[iter].tag != NULL; iter++) {
if (strcmp(name_to_mousefunc[iter].tag, binding) != 0)
for (i = 0; i < nitems(name_to_func); i++) {
if (strcmp(name_to_func[i].tag, cmd) != 0)
continue;
current_binding->context = name_to_mousefunc[iter].context;
current_binding->callback = name_to_mousefunc[iter].handler;
TAILQ_INSERT_TAIL(&c->mousebindingq, current_binding, entry);
return;
mb->callback = name_to_func[i].handler;
mb->flags = name_to_func[i].flags;
mb->argument = name_to_func[i].argument;
TAILQ_INSERT_TAIL(&c->mousebindingq, mb, entry);
return(1);
}
return(0);
}
static void
conf_mouseunbind(struct conf *c, struct mousebinding *unbind)
conf_unbind_mouse(struct conf *c, struct binding *unbind)
{
struct mousebinding *mb = NULL, *mbnxt;
for (mb = TAILQ_FIRST(&c->mousebindingq);
mb != TAILQ_END(&c->mousebindingq); mb = mbnxt) {
mbnxt = TAILQ_NEXT(mb, entry);
struct binding *mb = NULL, *mbnxt;
TAILQ_FOREACH_SAFE(mb, &c->mousebindingq, entry, mbnxt) {
if (mb->modmask != unbind->modmask)
continue;
if (mb->button == unbind->button) {
if (mb->press.button == unbind->press.button) {
TAILQ_REMOVE(&c->mousebindingq, mb, entry);
xfree(mb);
free(mb);
}
}
}
/*
* Grab the mouse buttons that we need for bindings for this client
*/
static int cursor_binds[] = {
XC_X_cursor, /* CF_DEFAULT */
XC_fleur, /* CF_MOVE */
XC_left_ptr, /* CF_NORMAL */
XC_question_arrow, /* CF_QUESTION */
XC_bottom_right_corner, /* CF_RESIZE */
};
void
conf_grab_mouse(struct client_ctx *cc)
conf_cursor(struct conf *c)
{
struct mousebinding *mb;
int button;
unsigned int i;
for (i = 0; i < nitems(cursor_binds); i++)
c->cursor[i] = XCreateFontCursor(X_Dpy, cursor_binds[i]);
}
void
conf_grab_mouse(Window win)
{
struct binding *mb;
xu_btn_ungrab(win);
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
if (mb->context != MOUSEBIND_CTX_WIN)
continue;
switch(mb->button) {
case 1:
button = Button1;
break;
case 2:
button = Button2;
break;
case 3:
button = Button3;
break;
default:
warnx("strange button in mousebinding\n");
continue;
}
xu_btn_grab(cc->win, mb->modmask, button);
if (mb->flags & CWM_WIN)
xu_btn_grab(win, mb->modmask, mb->press.button);
}
}
void
conf_grab_kbd(Window win)
{
struct binding *kb;
xu_key_ungrab(win);
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
xu_key_grab(win, kb->modmask, kb->press.keysym);
}
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_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",
};
void
conf_atoms(void)
{
XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
}

53
cwm.1
View File

@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: August 24 2009 $
.Dd $Mdocdate$
.Dt CWM 1
.Os
.Sh NAME
@ -52,7 +52,7 @@ Right mouse button.
.Nm
is very simple in its use.
Most of the actions are initiated via key bindings.
The current key bindings are described below;
The default key bindings are described below;
their functionality is described in more detail later.
.Pp
.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact
@ -88,8 +88,14 @@ Toggle group membership of current window.
Cycle through active groups.
.It Ic M-Left
Reverse cycle through active groups.
.It Ic CMS-f
Toggle freezing geometry of current window.
.It Ic CM-s
Toggle stickiness of current window.
.It Ic CM-f
Toggle full-screen size of current window.
Toggle full-screen mode of current window.
.It Ic CM-m
Toggle maximization of current window.
.It Ic CM-=
Toggle vertical maximization of current window.
.It Ic CMS-=
@ -114,13 +120,14 @@ dialog; allows you to switch from
.Nm
to another window manager without restarting the X server.
.It Ic CMS-r
Reload configuration.
Restart the running
.Xr cwm 1 .
.It Ic CMS-q
Quit
.Nm .
.El
.Pp
The mouse bindings are also important, they are:
The default mouse bindings are:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic M-M1
@ -140,8 +147,17 @@ The options for
are as follows:
.Bl -tag -width Ds
.It Fl c Ar file
Specify the config file to use. Defaults to
.Pa ~/.cwmrc .
Specify an alternative configuration file.
By default,
.Nm
loads
.Pa ~/.cwmrc ,
if present.
Any error messages from lines in the configuration file will be sent to
.Em stderr ;
however,
.Nm
will continue to process the rest of the configuration file.
.It Fl d Ar display
Specify the display to use.
.El
@ -256,18 +272,6 @@ option is given.
.El
.Sh SEE ALSO
.Xr cwmrc 5
.Sh AUTHORS
.An -nosplit
.Pp
.Nm
was developed by
.An Marius Aamodt Eriksen Aq marius@monkey.org
with contributions from
.An Andy Adamson Aq dros@monkey.org ,
.An Niels Provos Aq provos@monkey.org ,
and
.An Antti Nyk\(:anen Aq aon@iki.fi .
Ideas, discussion with many others.
.Sh HISTORY
.Nm
was originally inspired by evilwm, but was rewritten from scratch
@ -278,3 +282,14 @@ has since been removed or rewritten.
.Nm
first appeared in
.Ox 4.2 .
.Sh AUTHORS
.An -nosplit
.Nm
was developed by
.An Marius Aamodt Eriksen Aq marius@monkey.org
with contributions from
.An Andy Adamson Aq dros@monkey.org ,
.An Niels Provos Aq provos@monkey.org ,
and
.An Antti Nyk\(:anen Aq aon@iki.fi .
Ideas, discussion with many others.

2
cwm.pub Normal file
View File

@ -0,0 +1,2 @@
untrusted comment: portable cwm release key public key
RWTdOib0PoIM0pmDAPnV2S9/AMRqTOVfTY/KAkFemdH13cqBDHdduTas

120
cwmrc.5
View File

@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: September 25 2010 $
.Dd $Mdocdate$
.Dt CWMRC 5
.Os
.Sh NAME
@ -24,26 +24,48 @@
This manual page describes the
.Xr cwm 1
configuration file.
The following options are accepted in the configuration file:
.Pp
The current line can be extended over multiple lines using a backslash
.Pq Sq \e .
Comments can be put anywhere in the file using a hash mark
.Pq Sq # ,
and extend to the end of the current line.
Care should be taken when commenting out multi-line text:
the comment is effective until the end of the entire block.
.Pp
Arguments containing whitespace should be surrounded by double quotes
.Pq \&" .
.Pp
The following options are accepted:
.Pp
.Bl -tag -width Ds -compact
.It Ic autogroup Ar group windowname
.It Ic autogroup Ar group windowclass
.It Ic autogroup Ar group windowname,windowclass
Control automatic window grouping, based on the name and/or class
properties, where
Automatically add new windows to
.Ar group
if their class property matches
.Ar windowclass ,
or if their name and class properties match
.Ar windowname
and
.Ar windowclass ,
respectively.
The more specific last match wins.
.Ar group
is a number between 0 and 9.
If the group number is 0, then the window will not be grouped; this to
allow for
.Dq sticky
windows in sticky group mode.
If
.Ar group
is 0, matching windows will not be added to any group; this may be
used to override
.Dq sticky group mode .
.Pp
The name and class of a window may be obtained using
The name and class values, respectively, for existing windows
are both set in the WM_CLASS property and may be obtained using
.Xr xprop 1 .
.Pp
.It Ic bind Ar keys command
Cause the creation of a keybinding, or replacement of a default
keybinding.
Cause the creation of a key binding, or replacement of a default
key binding.
The modifier keys come first, followed by a
.Sq - .
.Pp
@ -62,10 +84,8 @@ The Mod4 key (normally the windows key).
.Pp
The
.Sq -
should be followed by either a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h ,
or a numerical keycode value enclosed in
.Dq [] .
should be followed by a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h .
The
.Ar command
may either be one from the
@ -76,7 +96,7 @@ A special
.Ar command
keyword
.Dq unmap
can be used to remove the named keybinding.
can be used to remove the named key binding.
This can be used to remove a binding which conflicts with an
application.
.Pp
@ -87,12 +107,27 @@ Set the window border width to
.It Ic color activeborder Ar color
Set the color of the active border.
.Pp
.It Ic color font Ar color
Set menu font color.
.Pp
.It Ic color selfont Ar color
Set font color for selected menu item.
.Pp
.It Ic color groupborder Ar color
Set the color of the border while grouping a window.
.Pp
.It Ic color inactiveborder Ar color
Set the color of the inactive border.
.Pp
.It Ic color menubg Ar color
Set menu background color.
.Pp
.It Ic color menufg Ar color
Set menu foreground color.
.Pp
.It Ic color urgencyborder Ar color
Set the color of the border of a window indicating urgency.
.Pp
.It Ic color ungroupborder Ar color
Set the color of the border while ungrouping a window.
.Pp
@ -113,7 +148,7 @@ and
.Nm lock
have a special meaning.
They point to the terminal and screen locking programs specified by
keybindings.
key bindings.
The defaults are
.Xr xterm 1
and
@ -172,6 +207,10 @@ Left mouse button.
Middle mouse button.
.It 3
Right mouse button.
.It 4
Scroll up mouse button.
.It 5
Scroll down mouse button.
.El
.Pp
The
@ -185,6 +224,10 @@ Set a default size for the keyboard movement bindings,
in pixels.
The default is 1.
.Pp
.It Ic snapdist Ar pixels
Minimum distance to snap-to adjacent edge, in pixels.
The default is 0.
.Pp
.It Ic sticky Ic yes Ns \&| Ns Ic no
Toggle sticky group mode.
The default behavior for new windows is to not assign any group.
@ -215,10 +258,14 @@ ignore xwi
ignore xapm
ignore xclock
# Keybindings
# Key bindings
bind CM-r label
bind CS-Return "xterm -e top"
bind 4-o unmap
bind CM-equal unmap
bind CMS-equal unmap
bind C4-equal vmaximize
bind C4S-equal hmaximize
bind M-1 grouponly1
bind M-2 grouponly2
bind M-3 grouponly3
@ -226,14 +273,15 @@ bind MS-1 movetogroup1
bind MS-2 movetogroup2
bind MS-3 movetogroup3
# Mousebindings
# Mouse bindings
mousebind M-2 window_lower
mousebind M-3 window_resize
.Ed
.Sh BIND COMMAND LIST
.Bl -tag -width 18n -compact
.It reload
Reload configuration.
.It restart
Restart the running
.Xr cwm 1 .
.It quit
Quit
.Xr cwm 1 .
@ -277,6 +325,10 @@ Reverse cycle through groups.
Forward cycle through windows.
.It rcycle
Reverse cycle through windows.
.It cycleingroup
Forward cycle through windows in current group.
.It rcycleingroup
Reverse cycle through windows in current group.
.It delete
Delete current window.
.It hide
@ -287,12 +339,18 @@ Lower current window.
Raise current window.
.It label
Label current window.
.It freeze
Freeze current window geometry.
.It sticky
Stick current window to all groups (same as assigning to nogroup).
.It fullscreen
Full-screen current window (gap + border removed).
.It maximize
Maximize current window full-screen.
Maximize current window (gap + border honored).
.It vmaximize
Maximize current window vertically.
Vertically maximize current window (gap + border honored).
.It hmaximize
Maximize current window horizontally.
Horizontally maximize current window (gap + border honored).
.It moveup
Move window
.Ar moveamount
@ -389,6 +447,12 @@ pixels right.
Move pointer 10 times
.Ar moveamount
pixels left.
.It htile
Current window is placed at the top of the screen and maximized
horizontally, other windows in its group share remaining screen space.
.It vtile
Current window is placed on the left of the screen and maximized
vertically, other windows in its group share remaining screen space.
.El
.Sh MOUSEBIND COMMAND LIST
.Bl -tag -width 18n -compact
@ -398,10 +462,16 @@ Move current window.
Resize current window.
.It window_lower
Lower current window.
.It window_raise
Raise current window.
.It window_hide
Hide current window.
.It window_grouptoggle
Toggle group membership of current window.
.It cyclegroup
Forward cycle through groups.
.It rcyclegroup
Reverse cycle through groups.
.It menu_group
Launch group list.
.It menu_unhide

106
fgetln.c Normal file
View File

@ -0,0 +1,106 @@
/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Christos Zoulas.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#if !HAVE_FGETLN
#include <stdlib.h>
#ifndef HAVE_NBTOOL_CONFIG_H
/* These headers are required, but included from nbtool_config.h */
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#endif
char *
fgetln(FILE *fp, size_t *len)
{
static char *buf = NULL;
static size_t bufsiz = 0;
char *ptr;
if (buf == NULL) {
bufsiz = BUFSIZ;
if ((buf = malloc(bufsiz)) == NULL)
return NULL;
}
if (fgets(buf, bufsiz, fp) == NULL)
return NULL;
*len = 0;
while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
size_t nbufsiz = bufsiz + BUFSIZ;
char *nbuf = realloc(buf, nbufsiz);
if (nbuf == NULL) {
int oerrno = errno;
free(buf);
errno = oerrno;
buf = NULL;
return NULL;
} else
buf = nbuf;
if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) {
buf[bufsiz] = '\0';
*len = strlen(buf);
return buf;
}
*len = bufsiz;
bufsiz = nbufsiz;
}
*len = (ptr - buf) + 1;
return buf;
}
#endif
#ifdef TEST
int
main(int argc, char *argv[])
{
char *p;
size_t len;
while ((p = fgetln(stdin, &len)) != NULL) {
(void)printf("%zu %s", len, p);
free(p);
}
return 0;
}
#endif

99
font.c
View File

@ -1,99 +0,0 @@
/*
* font.c - cwm font abstraction
*
* Copyright (c) 2005 Marius Eriksen <marius@monkey.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
int
font_ascent(struct screen_ctx *sc)
{
return (sc->font->ascent);
}
int
font_descent(struct screen_ctx *sc)
{
return (sc->font->descent);
}
u_int
font_height(struct screen_ctx *sc)
{
return (sc->fontheight);
}
void
font_init(struct screen_ctx *sc)
{
sc->xftdraw = XftDrawCreate(X_Dpy, sc->rootwin,
DefaultVisual(X_Dpy, sc->which), DefaultColormap(X_Dpy, sc->which));
if (sc->xftdraw == NULL)
errx(1, "XftDrawCreate");
if (!XftColorAllocName(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which), "black", &sc->xftcolor))
errx(1, "XftColorAllocName");
}
int
font_width(struct screen_ctx *sc, const char *text, int len)
{
XGlyphInfo extents;
XftTextExtents8(X_Dpy, sc->font, (const XftChar8*)text,
len, &extents);
return (extents.xOff);
}
void
font_draw(struct screen_ctx *sc, const char *text, int len,
Drawable d, int x, int y)
{
XftDrawChange(sc->xftdraw, d);
/* Really needs to be UTF8'd. */
XftDrawString8(sc->xftdraw, &sc->xftcolor, sc->font, x, y,
(const FcChar8*)text, len);
}
XftFont *
font_make(struct screen_ctx *sc, const char *name)
{
XftFont *fn = NULL;
FcPattern *pat, *patx;
XftResult res;
if ((pat = FcNameParse((const FcChar8*)name)) == NULL)
return (NULL);
if ((patx = XftFontMatch(X_Dpy, sc->which, pat, &res)) != NULL)
fn = XftFontOpenPattern(X_Dpy, patx);
FcPatternDestroy(pat);
return (fn);
}

515
group.c
View File

@ -16,111 +16,91 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static void group_add(struct group_ctx *, struct client_ctx *);
static void group_remove(struct client_ctx *);
static void group_hide(struct screen_ctx *, struct group_ctx *);
static void group_show(struct screen_ctx *, struct group_ctx *);
static void group_fix_hidden_state(struct group_ctx *);
static void group_setactive(struct screen_ctx *, long);
static void group_set_names(struct screen_ctx *);
static void group_assign(struct group_ctx *, struct client_ctx *);
static void group_restack(struct group_ctx *);
static void group_setactive(struct group_ctx *);
const char *shortcut_to_name[] = {
const char *num_to_name[] = {
"nogroup", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine"
};
static void
group_add(struct group_ctx *gc, struct client_ctx *cc)
group_assign(struct group_ctx *gc, struct client_ctx *cc)
{
long no;
if (cc == NULL || gc == NULL)
errx(1, "group_add: a ctx is NULL");
if (cc->group != NULL)
TAILQ_REMOVE(&cc->group->clientq, cc, group_entry);
no = gc->shortcut - 1;
if (cc->group == gc)
return;
cc->group = gc;
if (cc->group != NULL)
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
TAILQ_INSERT_TAIL(&gc->clientq, cc, group_entry);
XChangeProperty(X_Dpy, cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
32, PropModeReplace, (unsigned char *)&no, 1);
TAILQ_INSERT_TAIL(&gc->clients, cc, group_entry);
cc->group = gc;
xu_ewmh_net_wm_desktop(cc);
}
static void
group_remove(struct client_ctx *cc)
{
long no = 0xffffffff;
if (cc == NULL || cc->group == NULL)
errx(1, "group_remove: a ctx is NULL");
XChangeProperty(X_Dpy, cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
32, PropModeReplace, (unsigned char *)&no, 1);
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
cc->group = NULL;
}
static void
group_hide(struct screen_ctx *sc, struct group_ctx *gc)
void
group_hide(struct group_ctx *gc)
{
struct client_ctx *cc;
screen_updatestackingorder(sc);
screen_updatestackingorder(gc->sc);
gc->nhidden = 0;
gc->highstack = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
TAILQ_FOREACH(cc, &gc->clientq, group_entry)
client_hide(cc);
gc->nhidden++;
if (cc->stackingorder > gc->highstack)
gc->highstack = cc->stackingorder;
}
gc->hidden = 1; /* XXX: equivalent to gc->nhidden > 0 */
}
void
group_show(struct group_ctx *gc)
{
struct client_ctx *cc;
TAILQ_FOREACH(cc, &gc->clientq, group_entry)
client_unhide(cc);
group_restack(gc);
group_setactive(gc);
}
static void
group_show(struct screen_ctx *sc, struct group_ctx *gc)
group_restack(struct group_ctx *gc)
{
struct client_ctx *cc;
Window *winlist;
u_int i;
int lastempty = -1;
int i, lastempty = -1;
int nwins = 0, highstack = 0;
winlist = (Window *) xcalloc(sizeof(*winlist), (gc->highstack + 1));
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (cc->stackingorder > highstack)
highstack = cc->stackingorder;
}
winlist = xcalloc((highstack + 1), sizeof(*winlist));
/*
* Invert the stacking order as XRestackWindows() expects them
* top-to-bottom.
*/
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
winlist[gc->highstack - cc->stackingorder] = cc->win;
client_unhide(cc);
/* Invert the stacking order for XRestackWindows(). */
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
winlist[highstack - cc->stackingorder] = cc->win;
nwins++;
}
/* Un-sparseify */
for (i = 0; i <= gc->highstack; i++) {
for (i = 0; i <= highstack; i++) {
if (!winlist[i] && lastempty == -1)
lastempty = i;
else if (winlist[i] && lastempty != -1) {
@ -130,141 +110,111 @@ group_show(struct screen_ctx *sc, struct group_ctx *gc)
}
}
XRestackWindows(X_Dpy, winlist, gc->nhidden);
xfree(winlist);
gc->hidden = 0;
group_setactive(sc, gc->shortcut - 1);
XRestackWindows(X_Dpy, winlist, nwins);
free(winlist);
}
void
group_init(struct screen_ctx *sc)
group_init(struct screen_ctx *sc, int num)
{
int i;
long viewports[2] = {0, 0};
long ndesks = CALMWM_NGROUPS, zero = 0;
struct group_ctx *gc;
TAILQ_INIT(&sc->groupq);
sc->group_hideall = 0;
/* see if any group names have already been set and update the property
* with ours if they'll have changed.
*/
group_update_names(sc);
gc = xmalloc(sizeof(*gc));
gc->sc = sc;
gc->name = xstrdup(num_to_name[num]);
gc->num = num;
TAILQ_INIT(&gc->clientq);
for (i = 0; i < CALMWM_NGROUPS; i++) {
TAILQ_INIT(&sc->groups[i].clients);
sc->groups[i].hidden = 0;
sc->groups[i].shortcut = i + 1;
TAILQ_INSERT_TAIL(&sc->groupq, &sc->groups[i], entry);
}
TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
/* we don't support large desktops, so this is always (0, 0) */
XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_VIEWPORT,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
XChangeProperty(X_Dpy, sc->rootwin, _NET_NUMBER_OF_DESKTOPS,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
/*
* we don't use virtual roots, so make sure it's not there from a
* previous wm.
*/
XDeleteProperty(X_Dpy, sc->rootwin, _NET_VIRTUAL_ROOTS);
/*
* We don't really have a ``showing desktop'' mode, so this is zero
* always. XXX Note that when we hide all groups, or when all groups
* are hidden we could technically set this later on.
*/
XChangeProperty(X_Dpy, sc->rootwin, _NET_SHOWING_DESKTOP,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
group_setactive(sc, 0);
if (num == 1)
group_setactive(gc);
}
void
group_make_autogroup(struct conf *conf, char *val, int no)
group_setactive(struct group_ctx *gc)
{
struct autogroupwin *aw;
char *p;
struct screen_ctx *sc = gc->sc;
aw = xcalloc(1, sizeof(*aw));
sc->group_active = gc;
if ((p = strchr(val, ',')) == NULL) {
aw->name = NULL;
aw->class = xstrdup(val);
} else {
*(p++) = '\0';
aw->name = xstrdup(val);
aw->class = xstrdup(p);
}
aw->num = no;
TAILQ_INSERT_TAIL(&conf->autogroupq, aw, entry);
}
static void
group_setactive(struct screen_ctx *sc, long idx)
{
sc->group_active = &sc->groups[idx];
XChangeProperty(X_Dpy, sc->rootwin, _NET_CURRENT_DESKTOP,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&idx, 1);
xu_ewmh_net_current_desktop(sc);
}
void
group_movetogroup(struct client_ctx *cc, int idx)
{
struct screen_ctx *sc = cc->sc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_movetogroup: index out of range (%d)", idx);
if(sc->group_active != &sc->groups[idx])
client_hide(cc);
group_add(&sc->groups[idx], cc);
}
/*
* Colouring for groups upon add/remove.
*/
void
group_sticky_toggle_enter(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
gc = sc->group_active;
if (idx < 0 || idx >= CALMWM_NGROUPS)
errx(1, "group_movetogroup: index out of range (%d)", idx);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
break;
}
if (cc->group == gc)
return;
if (group_holds_only_hidden(gc))
client_hide(cc);
group_assign(gc, cc);
}
void
group_toggle_membership_enter(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc = sc->group_active;
if (gc == cc->group) {
group_remove(cc);
cc->highlight = CLIENT_HIGHLIGHT_UNGROUP;
group_assign(NULL, cc);
cc->flags |= CLIENT_UNGROUP;
} else {
group_add(gc, cc);
cc->highlight = CLIENT_HIGHLIGHT_GROUP;
group_assign(gc, cc);
cc->flags |= CLIENT_GROUP;
}
client_draw_border(cc);
}
void
group_sticky_toggle_exit(struct client_ctx *cc)
group_toggle_membership_leave(struct client_ctx *cc)
{
cc->highlight = 0;
cc->flags &= ~CLIENT_HIGHLIGHT;
client_draw_border(cc);
}
/*
* if group_hidetoggle would produce no effect, toggle the group's hidden state
*/
static void
group_fix_hidden_state(struct group_ctx *gc)
int
group_holds_only_sticky(struct group_ctx *gc)
{
struct client_ctx *cc;
int same = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
if (gc->hidden == ((cc->flags & CLIENT_HIDDEN) ? 1 : 0))
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (!(cc->flags & CLIENT_STICKY))
return(0);
}
return(1);
}
int
group_holds_only_hidden(struct group_ctx *gc)
{
struct client_ctx *cc;
int hidden = 0, same = 0;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (cc->flags & CLIENT_STICKY)
continue;
if (hidden == ((cc->flags & CLIENT_HIDDEN) ? 1 : 0))
same++;
}
if (same == 0)
gc->hidden = !gc->hidden;
hidden = !hidden;
return(hidden);
}
void
@ -273,34 +223,36 @@ group_hidetoggle(struct screen_ctx *sc, int idx)
struct group_ctx *gc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_hidetoggle: index out of range (%d)", idx);
errx(1, "group_hidetoggle: index out of range (%d)", idx);
gc = &sc->groups[idx];
group_fix_hidden_state(gc);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
break;
}
if (gc->hidden)
group_show(sc, gc);
if (group_holds_only_hidden(gc))
group_show(gc);
else {
group_hide(sc, gc);
/* XXX wtf? */
if (TAILQ_EMPTY(&gc->clients))
group_setactive(sc, idx);
group_hide(gc);
/* make clients stick to empty group */
if (TAILQ_EMPTY(&gc->clientq))
group_setactive(gc);
}
}
void
group_only(struct screen_ctx *sc, int idx)
{
int i;
struct group_ctx *gc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_only: index out of range (%d)", idx);
errx(1, "group_only: index out of range (%d)", idx);
for (i = 0; i < CALMWM_NGROUPS; i++) {
if (i == idx)
group_show(sc, &sc->groups[i]);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
group_show(gc);
else
group_hide(sc, &sc->groups[i]);
group_hide(gc);
}
}
@ -308,7 +260,7 @@ group_only(struct screen_ctx *sc, int idx)
* Cycle through active groups. If none exist, then just stay put.
*/
void
group_cycle(struct screen_ctx *sc, int reverse)
group_cycle(struct screen_ctx *sc, int flags)
{
struct group_ctx *gc, *showgroup = NULL;
@ -316,103 +268,43 @@ group_cycle(struct screen_ctx *sc, int reverse)
gc = sc->group_active;
for (;;) {
gc = reverse ? TAILQ_PREV(gc, group_ctx_q, entry) :
TAILQ_NEXT(gc, entry);
gc = (flags & CWM_RCYCLE) ? TAILQ_PREV(gc, group_ctx_q,
entry) : TAILQ_NEXT(gc, entry);
if (gc == NULL)
gc = reverse ? TAILQ_LAST(&sc->groupq, group_ctx_q) :
TAILQ_FIRST(&sc->groupq);
gc = (flags & CWM_RCYCLE) ? TAILQ_LAST(&sc->groupq,
group_ctx_q) : TAILQ_FIRST(&sc->groupq);
if (gc == sc->group_active)
break;
if (!TAILQ_EMPTY(&gc->clients) && showgroup == NULL)
if (!group_holds_only_sticky(gc) && showgroup == NULL)
showgroup = gc;
else if (!gc->hidden)
group_hide(sc, gc);
else if (!group_holds_only_hidden(gc))
group_hide(gc);
}
if (showgroup == NULL)
return;
group_hide(sc, sc->group_active);
group_hide(sc->group_active);
if (showgroup->hidden)
group_show(sc, showgroup);
if (group_holds_only_hidden(showgroup))
group_show(showgroup);
else
group_setactive(sc, showgroup->shortcut - 1);
}
/* called when a client is deleted */
void
group_client_delete(struct client_ctx *cc)
{
if (cc->group == NULL)
return;
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
cc->group = NULL; /* he he */
}
void
group_menu(XButtonEvent *e)
{
struct screen_ctx *sc;
struct group_ctx *gc;
struct menu *mi;
struct menu_q menuq;
int i;
sc = screen_fromroot(e->root);
TAILQ_INIT(&menuq);
for (i = 0; i < CALMWM_NGROUPS; i++) {
gc = &sc->groups[i];
if (TAILQ_EMPTY(&gc->clients))
continue;
mi = xcalloc(1, sizeof(*mi));
if (gc->hidden)
snprintf(mi->text, sizeof(mi->text), "%d: [%s]",
gc->shortcut, sc->group_names[i]);
else
snprintf(mi->text, sizeof(mi->text), "%d: %s",
gc->shortcut, sc->group_names[i]);
mi->ctx = gc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi == NULL || mi->ctx == NULL)
goto cleanup;
gc = (struct group_ctx *)mi->ctx;
(gc->hidden) ? group_show(sc, gc) : group_hide(sc, gc);
cleanup:
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
group_setactive(showgroup);
}
void
group_alltoggle(struct screen_ctx *sc)
{
int i;
struct group_ctx *gc;
for (i = 0; i < CALMWM_NGROUPS; i++) {
if (sc->group_hideall)
group_show(sc, &sc->groups[i]);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->hideall)
group_show(gc);
else
group_hide(sc, &sc->groups[i]);
group_hide(gc);
}
sc->group_hideall = (!sc->group_hideall);
sc->hideall = !sc->hideall;
}
void
@ -421,118 +313,45 @@ group_autogroup(struct client_ctx *cc)
struct screen_ctx *sc = cc->sc;
struct autogroupwin *aw;
struct group_ctx *gc;
int no = -1;
long *grpno;
int num = -2, both_match = 0;
long *grpnum;
if (cc->app_class == NULL || cc->app_name == NULL)
if (cc->ch.res_class == NULL || cc->ch.res_name == NULL)
return;
if (xu_getprop(cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
1, (unsigned char **)&grpno) > 0) {
if (*grpno == 0xffffffff)
no = 0;
else if (*grpno > CALMWM_NGROUPS || *grpno < 0)
no = CALMWM_NGROUPS - 1;
else
no = *grpno + 1;
XFree(grpno);
if (xu_getprop(cc->win, ewmh[_NET_WM_DESKTOP],
XA_CARDINAL, 1, (unsigned char **)&grpnum) > 0) {
num = *grpnum;
if (num > CALMWM_NGROUPS || num < -1)
num = CALMWM_NGROUPS - 1;
XFree(grpnum);
} else {
TAILQ_FOREACH(aw, &Conf.autogroupq, entry) {
if (strcmp(aw->class, cc->app_class) == 0 &&
(aw->name == NULL ||
strcmp(aw->name, cc->app_name) == 0)) {
no = aw->num;
break;
if (strcmp(aw->class, cc->ch.res_class) == 0) {
if ((aw->name != NULL) &&
(strcmp(aw->name, cc->ch.res_name) == 0)) {
num = aw->num;
both_match = 1;
} else if (aw->name == NULL && !both_match)
num = aw->num;
}
}
}
/* no group please */
if (no == 0)
if ((num == -1) || (num == 0)) {
group_assign(NULL, cc);
return;
}
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->shortcut == no) {
group_add(gc, cc);
if (gc->num == num) {
group_assign(gc, cc);
return;
}
}
if (Conf.flags & CONF_STICKY_GROUPS)
group_add(sc->group_active, cc);
}
void
group_update_names(struct screen_ctx *sc)
{
char **strings, *p;
unsigned char *prop_ret;
Atom type_ret;
int format_ret, i = 0, nstrings = 0, n, setnames = 0;
unsigned long bytes_after, num_ret;
if (XGetWindowProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_NAMES, 0,
0xffffff, False, UTF8_STRING, &type_ret, &format_ret,
&num_ret, &bytes_after, &prop_ret) == Success &&
prop_ret != NULL && format_ret == 8) {
/* failure, just set defaults */
prop_ret[num_ret - 1] = '\0'; /* paranoia */
while (i < num_ret) {
if (prop_ret[i++] == '\0')
nstrings++;
}
}
strings = xmalloc((nstrings < CALMWM_NGROUPS ? CALMWM_NGROUPS :
nstrings) * sizeof(*strings));
i = n = 0;
p = prop_ret;
while (n < nstrings) {
strings[n++] = xstrdup(p);
p += strlen(p) + 1;
}
/*
* make sure we always set our defaults if nothing is there to
* replace them.
*/
if (n < CALMWM_NGROUPS) {
setnames = 1;
i = 1;
while (n < CALMWM_NGROUPS)
strings[n++] = xstrdup(shortcut_to_name[i++]);
}
if (prop_ret != NULL)
XFree(prop_ret);
if (sc->group_nonames != 0)
free(sc->group_names);
sc->group_names = strings;
sc->group_nonames = n;
if (setnames)
group_set_names(sc);
}
static void
group_set_names(struct screen_ctx *sc)
{
unsigned char *p, *q;
size_t len = 0, tlen, slen;
int i;
for (i = 0; i < sc->group_nonames; i++)
len += strlen(sc->group_names[i]) + 1;
q = p = xcalloc(len, sizeof(*p));
tlen = len;
for (i = 0; i < sc->group_nonames; i++) {
slen = strlen(sc->group_names[i]) + 1;
strlcpy(q, sc->group_names[i], tlen);
tlen -= slen;
q += slen;
}
XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_NAMES,
UTF8_STRING, 8, PropModeReplace, p, len);
group_assign(sc->group_active, cc);
else
group_assign(NULL, cc);
}

117
input.c
View File

@ -1,117 +0,0 @@
/*
* calmwm - the calm window manager
*
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
int
input_keycodetrans(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
{
int ks;
*ctl = CTL_NONE;
*chr = '\0';
ks = XKeycodeToKeysym(X_Dpy, kc, (state & ShiftMask) ? 1 : 0);
/* Look for control characters. */
switch (ks) {
case XK_BackSpace:
*ctl = CTL_ERASEONE;
break;
case XK_Return:
*ctl = CTL_RETURN;
break;
case XK_Up:
*ctl = CTL_UP;
break;
case XK_Down:
*ctl = CTL_DOWN;
break;
case XK_Escape:
*ctl = CTL_ABORT;
break;
}
if (*ctl == CTL_NONE && (state & ControlMask)) {
switch (ks) {
case XK_s:
case XK_S:
/* Emacs "next" */
*ctl = CTL_DOWN;
break;
case XK_r:
case XK_R:
/* Emacs "previous" */
*ctl = CTL_UP;
break;
case XK_u:
case XK_U:
*ctl = CTL_WIPE;
break;
case XK_h:
case XK_H:
*ctl = CTL_ERASEONE;
break;
case XK_a:
case XK_A:
*ctl = CTL_ALL;
break;
}
}
if (*ctl == CTL_NONE && (state & Mod1Mask)) {
switch (ks) {
case XK_j:
case XK_J:
/* Vi "down" */
*ctl = CTL_DOWN;
break;
case XK_k:
case XK_K:
/* Vi "up" */
*ctl = CTL_UP;
break;
}
}
if (*ctl != CTL_NONE)
return (0);
/*
* For regular characters, only (part of, actually) Latin 1
* for now.
*/
if (ks < 0x20 || ks > 0x07e)
return (-1);
*chr = (char)ks;
return (0);
}

340
kbfunc.c
View File

@ -1,7 +1,7 @@
/*
* calmwm - the calm window manager
* calmwm - the calm window manager
*
* Copyright (c) 2004 Martin Murray <mmurray@monkey.org>
* Copyright (c) 2004 Martin Murray <mmurray@monkey.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -15,31 +15,33 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#define KNOWN_HOSTS ".ssh/known_hosts"
#define HASH_MARKER "|1|"
extern int _xev_quit;
extern sig_atomic_t cwm_status;
void
kbfunc_client_lower(struct client_ctx *cc, union arg *arg)
{
client_ptrsave(cc);
client_lower(cc);
}
@ -49,16 +51,19 @@ kbfunc_client_raise(struct client_ctx *cc, union arg *arg)
client_raise(cc);
}
#define typemask (CWM_MOVE | CWM_RESIZE | CWM_PTRMOVE)
#define movemask (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)
#define TYPEMASK (CWM_MOVE | CWM_RESIZE | CWM_PTRMOVE)
#define MOVEMASK (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)
void
kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
kbfunc_client_moveresize(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc;
struct screen_ctx *sc = cc->sc;
struct geom xine;
int x, y, flags, amt;
u_int mx, my;
unsigned int mx, my;
if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
return;
sc = cc->sc;
mx = my = 0;
flags = arg->i;
@ -69,7 +74,7 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
amt = amt * 10;
}
switch (flags & movemask) {
switch (flags & MOVEMASK) {
case CWM_UP:
my -= amt;
break;
@ -83,49 +88,53 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
mx -= amt;
break;
}
switch (flags & typemask) {
switch (flags & TYPEMASK) {
case CWM_MOVE:
cc->geom.y += my;
if (cc->geom.y + cc->geom.height < 0)
cc->geom.y = -cc->geom.height;
if (cc->geom.y > cc->sc->ymax - 1)
cc->geom.y = cc->sc->ymax - 1;
cc->geom.x += mx;
if (cc->geom.x + cc->geom.width < 0)
cc->geom.x = -cc->geom.width;
if (cc->geom.x > cc->sc->xmax - 1)
cc->geom.x = cc->sc->xmax - 1;
if (cc->geom.x + cc->geom.w < 0)
cc->geom.x = -cc->geom.w;
if (cc->geom.x > sc->view.w - 1)
cc->geom.x = sc->view.w - 1;
cc->geom.y += my;
if (cc->geom.y + cc->geom.h < 0)
cc->geom.y = -cc->geom.h;
if (cc->geom.y > sc->view.h - 1)
cc->geom.y = sc->view.h - 1;
xine = screen_find_xinerama(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP);
cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.x + cc->geom.w + (cc->bwidth * 2),
xine.x, xine.x + xine.w, sc->snapdist);
cc->geom.y += client_snapcalc(cc->geom.y,
cc->geom.y + cc->geom.h + (cc->bwidth * 2),
xine.y, xine.y + xine.h, sc->snapdist);
client_move(cc);
xu_ptr_getpos(cc->win, &x, &y);
cc->ptr.y = y + my;
cc->ptr.x = x + mx;
cc->ptr.y = y + my;
client_ptrwarp(cc);
break;
case CWM_RESIZE:
if ((cc->geom.height += my) < 1)
cc->geom.height = 1;
if ((cc->geom.width += mx) < 1)
cc->geom.width = 1;
client_resize(cc);
if ((cc->geom.w += mx) < 1)
cc->geom.w = 1;
if ((cc->geom.h += my) < 1)
cc->geom.h = 1;
client_resize(cc, 1);
/* Make sure the pointer stays within the window. */
xu_ptr_getpos(cc->win, &cc->ptr.x, &cc->ptr.y);
if (cc->ptr.x > cc->geom.width)
cc->ptr.x = cc->geom.width - cc->bwidth;
if (cc->ptr.y > cc->geom.height)
cc->ptr.y = cc->geom.height - cc->bwidth;
if (cc->ptr.x > cc->geom.w)
cc->ptr.x = cc->geom.w - cc->bwidth;
if (cc->ptr.y > cc->geom.h)
cc->ptr.y = cc->geom.h - cc->bwidth;
client_ptrwarp(cc);
break;
case CWM_PTRMOVE:
if (cc) {
xu_ptr_getpos(cc->win, &x, &y);
xu_ptr_setpos(cc->win, x + mx, y + my);
} else {
xu_ptr_getpos(sc->rootwin, &x, &y);
xu_ptr_setpos(sc->rootwin, x + mx, y + my);
}
xu_ptr_getpos(sc->rootwin, &x, &y);
xu_ptr_setpos(sc->rootwin, x + mx, y + my);
break;
default:
warnx("invalid flags passed to kbfunc_client_moveresize");
@ -135,74 +144,53 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
void
kbfunc_client_search(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc;
struct screen_ctx *sc = cc->sc;
struct client_ctx *old_cc;
struct menu *mi;
struct menu_q menuq;
sc = cc->sc;
old_cc = client_current();
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &Clientq, entry) {
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, cc->name, sizeof(mi->text));
mi->ctx = cc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
TAILQ_FOREACH(cc, &sc->clientq, entry)
menuq_add(&menuq, cc, "%s", cc->name);
if ((mi = menu_filter(sc, &menuq, "window", NULL, 0,
search_match_client, search_print_client)) != NULL) {
cc = (struct client_ctx *)mi->ctx;
if (cc->flags & CLIENT_HIDDEN)
client_unhide(cc);
if (old_cc)
client_ptrsave(old_cc);
client_ptrwarp(cc);
}
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
menuq_clear(&menuq);
}
void
kbfunc_menu_search(struct client_ctx *cc, union arg *arg)
kbfunc_menu_cmd(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc;
struct screen_ctx *sc = cc->sc;
struct cmd *cmd;
struct menu *mi;
struct menu_q menuq;
sc = cc->sc;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, cmd->label, sizeof(mi->text));
mi->ctx = cmd;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
TAILQ_FOREACH(cmd, &Conf.cmdq, entry)
menuq_add(&menuq, cmd, "%s", cmd->name);
if ((mi = menu_filter(sc, &menuq, "application", NULL, 0,
search_match_text, NULL)) != NULL)
u_spawn(((struct cmd *)mi->ctx)->image);
u_spawn(((struct cmd *)mi->ctx)->path);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
menuq_clear(&menuq);
}
void
kbfunc_client_cycle(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc;
sc = cc->sc;
struct screen_ctx *sc = cc->sc;
/* XXX for X apps that ignore events */
XGrabKeyboard(X_Dpy, sc->rootwin, True,
@ -226,39 +214,49 @@ kbfunc_cmdexec(struct client_ctx *cc, union arg *arg)
void
kbfunc_term(struct client_ctx *cc, union arg *arg)
{
u_spawn(Conf.termpath);
struct cmd *cmd;
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
if (strcmp(cmd->name, "term") == 0)
u_spawn(cmd->path);
}
}
void
kbfunc_lock(struct client_ctx *cc, union arg *arg)
{
u_spawn(Conf.lockpath);
struct cmd *cmd;
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
if (strcmp(cmd->name, "lock") == 0)
u_spawn(cmd->path);
}
}
void
kbfunc_exec(struct client_ctx *cc, union arg *arg)
{
#define NPATHS 256
struct screen_ctx *sc;
char **ap, *paths[NPATHS], *path, *pathcpy, *label;
char tpath[MAXPATHLEN];
struct screen_ctx *sc = cc->sc;
char **ap, *paths[NPATHS], *path, *pathcpy;
char tpath[PATH_MAX];
const char *label;
DIR *dirp;
struct dirent *dp;
struct menu *mi;
struct menu_q menuq;
int l, i, cmd = arg->i;
sc = cc->sc;
switch (cmd) {
case CWM_EXEC_PROGRAM:
label = "exec";
break;
case CWM_EXEC_WM:
label = "wm";
break;
default:
err(1, "kbfunc_exec: invalid cmd %d", cmd);
/*NOTREACHED*/
case CWM_EXEC_PROGRAM:
label = "exec";
break;
case CWM_EXEC_WM:
label = "wm";
break;
default:
errx(1, "kbfunc_exec: invalid cmd %d", cmd);
/*NOTREACHED*/
}
TAILQ_INIT(&menuq);
@ -281,74 +279,68 @@ kbfunc_exec(struct client_ctx *cc, union arg *arg)
/* skip everything but regular files and symlinks */
if (dp->d_type != DT_REG && dp->d_type != DT_LNK)
continue;
memset(tpath, '\0', sizeof(tpath));
(void)memset(tpath, '\0', sizeof(tpath));
l = snprintf(tpath, sizeof(tpath), "%s/%s", paths[i],
dp->d_name);
/* check for truncation etc */
if (l == -1 || l >= (int)sizeof(tpath))
if (l == -1 || l >= sizeof(tpath))
continue;
if (access(tpath, X_OK) == 0) {
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, dp->d_name, sizeof(mi->text));
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (access(tpath, X_OK) == 0)
menuq_add(&menuq, NULL, "%s", dp->d_name);
}
(void)closedir(dirp);
}
xfree(path);
free(path);
if ((mi = menu_filter(sc, &menuq, label, NULL, 1,
search_match_exec, NULL)) != NULL) {
if ((mi = menu_filter(sc, &menuq, label, NULL,
CWM_MENU_DUMMY | CWM_MENU_FILE,
search_match_exec_path, NULL)) != NULL) {
if (mi->text[0] == '\0')
goto out;
switch (cmd) {
case CWM_EXEC_PROGRAM:
u_spawn(mi->text);
break;
case CWM_EXEC_WM:
u_exec(mi->text);
warn("%s", mi->text);
break;
default:
err(1, "kb_func: egad, cmd changed value!");
break;
case CWM_EXEC_PROGRAM:
u_spawn(mi->text);
break;
case CWM_EXEC_WM:
u_exec(mi->text);
warn("%s", mi->text);
break;
default:
errx(1, "kb_func: egad, cmd changed value!");
break;
}
}
out:
if (mi != NULL && mi->dummy)
xfree(mi);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
free(mi);
menuq_clear(&menuq);
}
void
kbfunc_ssh(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc;
struct screen_ctx *sc = cc->sc;
struct cmd *cmd;
struct menu *mi;
struct menu_q menuq;
FILE *fp;
char *buf, *lbuf, *p, *home;
char hostbuf[MAXHOSTNAMELEN], filename[MAXPATHLEN];
char cmd[256];
char *buf, *lbuf, *p;
char hostbuf[HOST_NAME_MAX+1];
char path[PATH_MAX];
int l;
size_t len;
sc = cc->sc;
if ((home = getenv("HOME")) == NULL)
if ((fp = fopen(Conf.known_hosts, "r")) == NULL) {
warn("kbfunc_ssh: %s", Conf.known_hosts);
return;
}
l = snprintf(filename, sizeof(filename), "%s/%s", home, KNOWN_HOSTS);
if (l == -1 || l >= sizeof(filename))
return;
if ((fp = fopen(filename, "r")) == NULL)
return;
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
if (strcmp(cmd->name, "term") == 0)
break;
}
TAILQ_INIT(&menuq);
lbuf = NULL;
while ((buf = fgetln(fp, &len))) {
if (buf[len - 1] == '\n')
@ -356,7 +348,7 @@ kbfunc_ssh(struct client_ctx *cc, union arg *arg)
else {
/* EOF without EOL, copy and add the NUL */
lbuf = xmalloc(len + 1);
memcpy(lbuf, buf, len);
(void)memcpy(lbuf, buf, len);
lbuf[len] = '\0';
buf = lbuf;
}
@ -369,30 +361,26 @@ kbfunc_ssh(struct client_ctx *cc, union arg *arg)
/* ignore badness */
if (p - buf + 1 > sizeof(hostbuf))
continue;
(void) strlcpy(hostbuf, buf, p - buf + 1);
mi = xcalloc(1, sizeof(*mi));
(void) strlcpy(mi->text, hostbuf, sizeof(mi->text));
TAILQ_INSERT_TAIL(&menuq, mi, entry);
(void)strlcpy(hostbuf, buf, p - buf + 1);
menuq_add(&menuq, NULL, hostbuf);
}
xfree(lbuf);
fclose(fp);
free(lbuf);
(void)fclose(fp);
if ((mi = menu_filter(sc, &menuq, "ssh", NULL, 1,
if ((mi = menu_filter(sc, &menuq, "ssh", NULL, CWM_MENU_DUMMY,
search_match_exec, NULL)) != NULL) {
if (mi->text[0] == '\0')
goto out;
l = snprintf(cmd, sizeof(cmd), "%s -e ssh %s", Conf.termpath,
mi->text);
if (l != -1 && l < sizeof(cmd))
u_spawn(cmd);
l = snprintf(path, sizeof(path), "%s -T '[ssh] %s' -e ssh %s",
cmd->path, mi->text, mi->text);
if (l == -1 || l >= sizeof(path))
goto out;
u_spawn(path);
}
out:
if (mi != NULL && mi->dummy)
xfree(mi);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
free(mi);
menuq_clear(&menuq);
}
void
@ -404,15 +392,14 @@ kbfunc_client_label(struct client_ctx *cc, union arg *arg)
TAILQ_INIT(&menuq);
/* dummy is set, so this will always return */
mi = menu_filter(cc->sc, &menuq, "label", cc->label, 1,
mi = menu_filter(cc->sc, &menuq, "label", cc->label, CWM_MENU_DUMMY,
search_match_text, NULL);
if (!mi->abort) {
if (cc->label != NULL)
xfree(cc->label);
free(cc->label);
cc->label = xstrdup(mi->text);
}
xfree(mi);
free(mi);
}
void
@ -424,13 +411,13 @@ kbfunc_client_delete(struct client_ctx *cc, union arg *arg)
void
kbfunc_client_group(struct client_ctx *cc, union arg *arg)
{
group_hidetoggle(cc->sc, KBTOGROUP(arg->i));
group_hidetoggle(cc->sc, arg->i);
}
void
kbfunc_client_grouponly(struct client_ctx *cc, union arg *arg)
{
group_only(cc->sc, KBTOGROUP(arg->i));
group_only(cc->sc, arg->i);
}
void
@ -452,41 +439,66 @@ kbfunc_client_grouptoggle(struct client_ctx *cc, union arg *arg)
XGrabKeyboard(X_Dpy, cc->win, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
group_sticky_toggle_enter(cc);
group_toggle_membership_enter(cc);
}
void
kbfunc_client_movetogroup(struct client_ctx *cc, union arg *arg)
{
group_movetogroup(cc, KBTOGROUP(arg->i));
group_movetogroup(cc, arg->i);
}
void
kbfunc_client_maximize(struct client_ctx *cc, union arg *arg)
kbfunc_client_toggle_sticky(struct client_ctx *cc, union arg *arg)
{
client_maximize(cc);
client_toggle_sticky(cc);
}
void
kbfunc_client_vmaximize(struct client_ctx *cc, union arg *arg)
kbfunc_client_toggle_fullscreen(struct client_ctx *cc, union arg *arg)
{
client_vertmaximize(cc);
client_toggle_fullscreen(cc);
}
void
kbfunc_client_hmaximize(struct client_ctx *cc, union arg *arg)
kbfunc_client_toggle_maximize(struct client_ctx *cc, union arg *arg)
{
client_horizmaximize(cc);
client_toggle_maximize(cc);
}
void
kbfunc_quit_wm(struct client_ctx *cc, union arg *arg)
kbfunc_client_toggle_vmaximize(struct client_ctx *cc, union arg *arg)
{
_xev_quit = 1;
client_toggle_vmaximize(cc);
}
void
kbfunc_reload(struct client_ctx *cc, union arg *arg)
kbfunc_client_toggle_hmaximize(struct client_ctx *cc, union arg *arg)
{
conf_reload(&Conf);
client_toggle_hmaximize(cc);
}
void
kbfunc_client_toggle_freeze(struct client_ctx *cc, union arg *arg)
{
client_toggle_freeze(cc);
}
void
kbfunc_cwm_status(struct client_ctx *cc, union arg *arg)
{
cwm_status = arg->i;
}
void
kbfunc_tile(struct client_ctx *cc, union arg *arg)
{
switch (arg->i) {
case CWM_TILE_HORIZ:
client_htile(cc);
break;
case CWM_TILE_VERT:
client_vtile(cc);
break;
}
}

472
menu.c
View File

@ -1,4 +1,6 @@
/*
* calmwm - the calm window manager
*
* Copyright (c) 2008 Owain G. Ainsworth <oga@openbsd.org>
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
*
@ -13,24 +15,36 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#define PROMPT_SCHAR '<27>'
#define PROMPT_ECHAR '<27>'
#define PROMPT_SCHAR "\xc2\xbb"
#define PROMPT_ECHAR "\xc2\xab"
enum ctltype {
CTL_NONE = -1,
CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
CTL_TAB, CTL_ABORT, CTL_ALL
};
struct menu_ctx {
struct screen_ctx *sc;
char searchstr[MENU_MAXENTRY + 1];
char dispstr[MENU_MAXENTRY*2 + 1];
char promptstr[MENU_MAXENTRY + 1];
@ -41,45 +55,32 @@ struct menu_ctx {
int noresult;
int prev;
int entry;
int height;
int width;
int num;
int flags;
int x;
int y;
void (*match)(struct menu_q *, struct menu_q *, char *);
void (*print)(struct menu *, int);
void (*match)(struct menu_q *, struct menu_q *, char *);
void (*print)(struct menu *, int);
};
static struct menu *menu_handle_key(XEvent *, struct menu_ctx *,
struct menu_q *, struct menu_q *);
static void menu_handle_move(XEvent *, struct menu_ctx *,
struct screen_ctx *);
struct menu_q *);
static struct menu *menu_handle_release(XEvent *, struct menu_ctx *,
struct screen_ctx *, struct menu_q *);
static void menu_draw(struct screen_ctx *, struct menu_ctx *,
struct menu_q *, struct menu_q *);
static int menu_calc_entry(struct screen_ctx *, struct menu_ctx *,
struct menu_q *);
static void menu_draw(struct menu_ctx *, struct menu_q *,
struct menu_q *);
static void menu_draw_entry(struct menu_ctx *, struct menu_q *,
int, int);
void
menu_init(struct screen_ctx *sc)
{
XGCValues gv;
sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1, 0,
sc->color[CWM_COLOR_BG_MENU].pixel,
sc->color[CWM_COLOR_BG_MENU].pixel);
gv.foreground =
sc->color[CWM_COLOR_FG_MENU].pixel^sc->color[CWM_COLOR_BG_MENU].pixel;
gv.background = sc->color[CWM_COLOR_BG_MENU].pixel;
gv.function = GXxor;
sc->gc = XCreateGC(X_Dpy, sc->menuwin,
GCForeground|GCBackground|GCFunction, &gv);
}
static int menu_calc_entry(struct menu_ctx *, int, int);
static struct menu *menu_complete_path(struct menu_ctx *);
static int menu_keycode(XKeyEvent *, enum ctltype *, char *);
struct menu *
menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
char *initial, int dummy,
menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
const char *initial, int flags,
void (*match)(struct menu_q *, struct menu_q *, char *),
void (*print)(struct menu *, int))
{
@ -89,29 +90,30 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
XEvent e;
Window focuswin;
int evmask, focusrevert;
int xsave, ysave, xcur, ycur;
TAILQ_INIT(&resultq);
bzero(&mc, sizeof(mc));
(void)memset(&mc, 0, sizeof(mc));
xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y);
if (prompt == NULL) {
evmask = MenuMask;
mc.promptstr[0] = '\0';
mc.list = 1;
} else {
evmask = MenuMask | KeyMask; /* only accept keys if prompt */
snprintf(mc.promptstr, sizeof(mc.promptstr), "%s%c", prompt,
PROMPT_SCHAR);
snprintf(mc.dispstr, sizeof(mc.dispstr), "%s%s%c", mc.promptstr,
mc.searchstr, PROMPT_ECHAR);
mc.width = font_width(sc, mc.dispstr, strlen(mc.dispstr));
xsave = mc.x;
ysave = mc.y;
mc.sc = sc;
mc.flags = flags;
if (prompt != NULL) {
evmask = MENUMASK | KEYMASK; /* accept keys as well */
(void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr));
mc.hasprompt = 1;
} else {
evmask = MENUMASK;
mc.list = 1;
}
if (initial != NULL)
strlcpy(mc.searchstr, initial, sizeof(mc.searchstr));
(void)strlcpy(mc.searchstr, initial, sizeof(mc.searchstr));
else
mc.searchstr[0] = '\0';
@ -119,14 +121,13 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
mc.print = print;
mc.entry = mc.prev = -1;
XMoveResizeWindow(X_Dpy, sc->menuwin, mc.x, mc.y, mc.width,
font_height(sc));
XSelectInput(X_Dpy, sc->menuwin, evmask);
XMapRaised(X_Dpy, sc->menuwin);
if (xu_ptr_grab(sc->menuwin, MenuGrabMask, Cursor_question) < 0) {
if (xu_ptr_grab(sc->menuwin, MENUGRABMASK,
Conf.cursor[CF_QUESTION]) < 0) {
XUnmapWindow(X_Dpy, sc->menuwin);
return (NULL);
return(NULL);
}
XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
@ -142,38 +143,75 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
XWindowEvent(X_Dpy, sc->menuwin, evmask, &e);
switch (e.type) {
default:
break;
case KeyPress:
if ((mi = menu_handle_key(&e, &mc, menuq, &resultq))
!= NULL)
goto out;
/* FALLTHROUGH */
case Expose:
menu_draw(sc, &mc, menuq, &resultq);
menu_draw(&mc, menuq, &resultq);
break;
case MotionNotify:
menu_handle_move(&e, &mc, sc);
menu_handle_move(&e, &mc, &resultq);
break;
case ButtonRelease:
if ((mi = menu_handle_release(&e, &mc, sc, &resultq))
if ((mi = menu_handle_release(&e, &mc, &resultq))
!= NULL)
goto out;
break;
default:
break;
}
}
out:
if (dummy == 0 && mi->dummy) { /* no match */
xfree (mi);
if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) {
/* no mouse based match */
free(mi);
mi = NULL;
xu_ptr_ungrab();
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
}
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
/* restore if user didn't move */
xu_ptr_getpos(sc->rootwin, &xcur, &ycur);
if (xcur == mc.x && ycur == mc.y)
xu_ptr_setpos(sc->rootwin, xsave, ysave);
xu_ptr_ungrab();
XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, 1, 1);
XUnmapWindow(X_Dpy, sc->menuwin);
XUngrabKeyboard(X_Dpy, CurrentTime);
return (mi);
return(mi);
}
static struct menu *
menu_complete_path(struct menu_ctx *mc)
{
struct menu *mi, *mr;
struct menu_q menuq;
char *path = NULL;
path = xcalloc(1, sizeof(mr->text));
mr = xcalloc(1, sizeof(*mr));
TAILQ_INIT(&menuq);
if ((mi = menu_filter(mc->sc, &menuq, mc->searchstr, NULL,
CWM_MENU_DUMMY, search_match_path_any, NULL)) != NULL) {
mr->abort = mi->abort;
mr->dummy = mi->dummy;
strlcpy(path, mi->text, sizeof(mi->text));
}
menuq_clear(&menuq);
if (path[0] != '\0')
snprintf(mr->text, sizeof(mr->text), "%s \"%s\"",
mc->searchstr, path);
else if (!mr->abort)
strlcpy(mr->text, mc->searchstr, sizeof(mr->text));
free(path);
return(mr);
}
static struct menu *
@ -182,17 +220,22 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
{
struct menu *mi;
enum ctltype ctl;
char chr;
char chr[32];
size_t len;
int clen, i;
wchar_t wc;
if (input_keycodetrans(e->xkey.keycode, e->xkey.state,
&ctl, &chr) < 0)
return (NULL);
if (menu_keycode(&e->xkey, &ctl, chr) < 0)
return(NULL);
switch (ctl) {
case CTL_ERASEONE:
if ((len = strlen(mc->searchstr)) > 0) {
mc->searchstr[len - 1] = '\0';
clen = 1;
while (mbtowc(&wc, &mc->searchstr[len-clen], MB_CUR_MAX) == -1)
clen++;
for (i = 1; i <= clen; i++)
mc->searchstr[len - i] = '\0';
mc->changed = 1;
}
break;
@ -218,37 +261,60 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
* even if dummy is zero, we need to return something.
*/
if ((mi = TAILQ_FIRST(resultq)) == NULL) {
mi = xmalloc(sizeof *mi);
mi = xmalloc(sizeof(*mi));
(void)strlcpy(mi->text,
mc->searchstr, sizeof(mi->text));
mi->dummy = 1;
}
mi->abort = 0;
return (mi);
return(mi);
case CTL_WIPE:
mc->searchstr[0] = '\0';
mc->changed = 1;
break;
case CTL_TAB:
if ((mi = TAILQ_FIRST(resultq)) != NULL) {
/*
* - We are in exec_path menu mode
* - It is equal to the input
* We got a command, launch the file menu
*/
if ((mc->flags & CWM_MENU_FILE) &&
(strncmp(mc->searchstr, mi->text,
strlen(mi->text))) == 0)
return(menu_complete_path(mc));
/*
* Put common prefix of the results into searchstr
*/
(void)strlcpy(mc->searchstr,
mi->text, sizeof(mc->searchstr));
while ((mi = TAILQ_NEXT(mi, resultentry)) != NULL) {
i = 0;
while (tolower(mc->searchstr[i]) ==
tolower(mi->text[i]))
i++;
mc->searchstr[i] = '\0';
}
mc->changed = 1;
}
break;
case CTL_ALL:
mc->list = !mc->list;
break;
case CTL_ABORT:
mi = xmalloc(sizeof *mi);
mi = xmalloc(sizeof(*mi));
mi->text[0] = '\0';
mi->dummy = 1;
mi->abort = 1;
return (mi);
return(mi);
default:
break;
}
if (chr != '\0') {
char str[2];
str[0] = chr;
str[1] = '\0';
if (chr[0] != '\0') {
mc->changed = 1;
strlcat(mc->searchstr, str, sizeof(mc->searchstr));
(void)strlcat(mc->searchstr, chr, sizeof(mc->searchstr));
}
mc->noresult = 0;
@ -264,15 +330,16 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
mc->listing = 0;
}
return (NULL);
return(NULL);
}
static void
menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
struct menu_q *resultq)
menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
{
struct menu *mi;
int n, dy, xsave, ysave;
struct screen_ctx *sc = mc->sc;
struct menu *mi;
struct geom xine;
int n, xsave, ysave;
if (mc->list) {
if (TAILQ_EMPTY(resultq) && mc->list) {
@ -288,12 +355,13 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
mc->num = 0;
mc->width = 0;
dy = 0;
mc->height = 0;
if (mc->hasprompt) {
snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%c",
mc->promptstr, mc->searchstr, PROMPT_ECHAR);
mc->width = font_width(sc, mc->dispstr, strlen(mc->dispstr));
dy = font_height(sc);
(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s",
mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR);
mc->width = xu_xft_width(sc->xftfont, mc->dispstr,
strlen(mc->dispstr));
mc->height = sc->xftfont->height + 1;
mc->num = 1;
}
@ -308,34 +376,43 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
text = mi->text;
}
mc->width = MAX(mc->width, font_width(sc, text,
mc->width = MAX(mc->width, xu_xft_width(sc->xftfont, text,
MIN(strlen(text), MENU_MAXENTRY)));
dy += font_height(sc);
mc->height += sc->xftfont->height + 1;
mc->num++;
}
xine = screen_find_xinerama(sc, mc->x, mc->y, CWM_GAP);
xine.w += xine.x - Conf.bwidth * 2;
xine.h += xine.y - Conf.bwidth * 2;
xsave = mc->x;
ysave = mc->y;
if (mc->x < 0)
mc->x = 0;
else if (mc->x + mc->width >= sc->xmax)
mc->x = sc->xmax - mc->width;
if (mc->y + dy >= sc->ymax)
mc->y = sc->ymax - dy;
/* never hide the top of the menu */
if (mc->y < 0)
mc->y = 0;
/* Never hide the top, or left side, of the menu. */
if (mc->x + mc->width >= xine.w)
mc->x = xine.w - mc->width;
if (mc->x < xine.x) {
mc->x = xine.x;
mc->width = MIN(mc->width, (xine.w - xine.x));
}
if (mc->y + mc->height >= xine.h)
mc->y = xine.h - mc->height;
if (mc->y < xine.y) {
mc->y = xine.y;
mc->height = MIN(mc->height, (xine.h - xine.y));
}
if (mc->x != xsave || mc->y != ysave)
xu_ptr_setpos(sc->rootwin, mc->x, mc->y);
XClearWindow(X_Dpy, sc->menuwin);
XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y, mc->width, dy);
XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y,
mc->width, mc->height);
if (mc->hasprompt) {
font_draw(sc, mc->dispstr, strlen(mc->dispstr), sc->menuwin,
0, font_ascent(sc) + 1);
xu_xft_draw(sc, mc->dispstr, CWM_COLOR_MENU_FONT,
0, sc->xftfont->ascent);
n = 1;
} else
n = 0;
@ -343,47 +420,72 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
TAILQ_FOREACH(mi, resultq, resultentry) {
char *text = mi->print[0] != '\0' ?
mi->print : mi->text;
int y = n * (sc->xftfont->height + 1) + sc->xftfont->ascent + 1;
font_draw(sc, text, MIN(strlen(text), MENU_MAXENTRY),
sc->menuwin, 0, n * font_height(sc) + font_ascent(sc) + 1);
/* Stop drawing when menu doesn't fit inside the screen. */
if (mc->y + y > xine.h)
break;
xu_xft_draw(sc, text, CWM_COLOR_MENU_FONT, 0, y);
n++;
}
if (mc->hasprompt && n > 1)
XFillRectangle(X_Dpy, sc->menuwin, sc->gc,
0, font_height(sc), mc->width, font_height(sc));
if (mc->noresult)
XFillRectangle(X_Dpy, sc->menuwin, sc->gc,
0, 0, mc->width, font_height(sc));
menu_draw_entry(mc, resultq, 1, 1);
}
static void
menu_handle_move(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc)
menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
int entry, int active)
{
struct screen_ctx *sc = mc->sc;
struct menu *mi;
char *text;
int color, i = 0;
if (mc->hasprompt)
i = 1;
TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++)
break;
if (mi == NULL)
return;
color = active ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
text = mi->print[0] != '\0' ? mi->print : mi->text;
XftDrawRect(sc->xftdraw, &sc->xftcolor[color], 0,
(sc->xftfont->height + 1) * entry, mc->width,
(sc->xftfont->height + 1) + sc->xftfont->descent);
color = active ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
xu_xft_draw(sc, text, color,
0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1);
}
static void
menu_handle_move(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq)
{
mc->prev = mc->entry;
mc->entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
mc->entry = menu_calc_entry(mc, e->xbutton.x, e->xbutton.y);
if (mc->prev == mc->entry)
return;
if (mc->prev != -1)
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height(sc) * mc->prev, mc->width, font_height(sc));
menu_draw_entry(mc, resultq, mc->prev, 0);
if (mc->entry != -1) {
xu_ptr_regrab(MenuGrabMask, Cursor_select);
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height(sc) * mc->entry, mc->width, font_height(sc));
(void)xu_ptr_regrab(MENUGRABMASK, Conf.cursor[CF_NORMAL]);
menu_draw_entry(mc, resultq, mc->entry, 1);
} else
xu_ptr_regrab(MenuGrabMask, Cursor_default);
(void)xu_ptr_regrab(MENUGRABMASK, Conf.cursor[CF_DEFAULT]);
}
static struct menu *
menu_handle_release(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
struct menu_q *resultq)
menu_handle_release(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq)
{
struct menu *mi;
int entry, i = 0;
struct menu *mi;
int entry, i = 0;
entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
xu_ptr_ungrab();
entry = menu_calc_entry(mc, e->xbutton.x, e->xbutton.y);
if (mc->hasprompt)
i = 1;
@ -396,23 +498,137 @@ menu_handle_release(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
mi->text[0] = '\0';
mi->dummy = 1;
}
return (mi);
return(mi);
}
static int
menu_calc_entry(struct screen_ctx *sc, struct menu_ctx *mc, int x, int y)
menu_calc_entry(struct menu_ctx *mc, int x, int y)
{
int entry;
struct screen_ctx *sc = mc->sc;
int entry;
entry = y / font_height(sc);
entry = y / (sc->xftfont->height + 1);
/* in bounds? */
if (x <= 0 || x > mc->width || y <= 0 ||
y > font_height(sc) * mc->num || entry < 0 || entry >= mc->num)
if (x < 0 || x > mc->width || y < 0 ||
y > (sc->xftfont->height + 1) * mc->num ||
entry < 0 || entry >= mc->num)
entry = -1;
if (mc->hasprompt && entry == 0)
entry = -1;
return (entry);
return(entry);
}
static int
menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
{
KeySym ks;
unsigned int state = ev->state;
*ctl = CTL_NONE;
chr[0] = '\0';
ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0,
(state & ShiftMask) ? 1 : 0);
/* Look for control characters. */
switch (ks) {
case XK_BackSpace:
*ctl = CTL_ERASEONE;
break;
case XK_Return:
*ctl = CTL_RETURN;
break;
case XK_Tab:
*ctl = CTL_TAB;
break;
case XK_Up:
*ctl = CTL_UP;
break;
case XK_Down:
*ctl = CTL_DOWN;
break;
case XK_Escape:
*ctl = CTL_ABORT;
break;
}
if (*ctl == CTL_NONE && (state & ControlMask)) {
switch (ks) {
case XK_s:
case XK_S:
/* Emacs "next" */
*ctl = CTL_DOWN;
break;
case XK_r:
case XK_R:
/* Emacs "previous" */
*ctl = CTL_UP;
break;
case XK_u:
case XK_U:
*ctl = CTL_WIPE;
break;
case XK_h:
case XK_H:
*ctl = CTL_ERASEONE;
break;
case XK_a:
case XK_A:
*ctl = CTL_ALL;
break;
}
}
if (*ctl == CTL_NONE && (state & Mod1Mask)) {
switch (ks) {
case XK_j:
case XK_J:
/* Vi "down" */
*ctl = CTL_DOWN;
break;
case XK_k:
case XK_K:
/* Vi "up" */
*ctl = CTL_UP;
break;
}
}
if (*ctl != CTL_NONE)
return(0);
if (XLookupString(ev, chr, 32, &ks, NULL) < 0)
return(-1);
return(0);
}
void
menuq_add(struct menu_q *mq, void *ctx, const char *fmt, ...)
{
va_list ap;
struct menu *mi;
mi = xcalloc(1, sizeof(*mi));
mi->ctx = ctx;
va_start(ap, fmt);
(void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap);
va_end(ap);
TAILQ_INSERT_TAIL(mq, mi, entry);
}
void
menuq_clear(struct menu_q *mq)
{
struct menu *mi;
while ((mi = TAILQ_FIRST(mq)) != NULL) {
TAILQ_REMOVE(mq, mi, entry);
free(mi);
}
}

View File

@ -1,8 +1,8 @@
/*
* calmwm - the calm window manager
* calmwm - the calm window manager
*
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
* Copyright (c) 2008 rivo nurges <rix@estpak.ee>
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
* Copyright (c) 2008 rivo nurges <rix@estpak.ee>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,119 +16,103 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static int mousefunc_sweep_calc(struct client_ctx *, int, int, int, int);
static void mousefunc_sweep_calc(struct client_ctx *, int, int, int, int);
static void mousefunc_sweep_draw(struct client_ctx *);
static int
static void
mousefunc_sweep_calc(struct client_ctx *cc, int x, int y, int mx, int my)
{
int width = cc->geom.width, height = cc->geom.height;
cc->geom.width = abs(x - mx) - cc->bwidth;
cc->geom.height = abs(y - my) - cc->bwidth;
cc->geom.w = abs(x - mx) - cc->bwidth;
cc->geom.h = abs(y - my) - cc->bwidth;
client_applysizehints(cc);
cc->geom.x = x <= mx ? x : x - cc->geom.width;
cc->geom.y = y <= my ? y : y - cc->geom.height;
return (width != cc->geom.width || height != cc->geom.height);
cc->geom.x = x <= mx ? x : x - cc->geom.w;
cc->geom.y = y <= my ? y : y - cc->geom.h;
}
static void
mousefunc_sweep_draw(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
char asize[10]; /* fits "nnnnxnnnn\0" */
int width, height, width_size, width_name;
char s[14]; /* fits " nnnn x nnnn \0" */
snprintf(asize, sizeof(asize), "%dx%d",
(cc->geom.width - cc->geom.basew) / cc->geom.incw,
(cc->geom.height - cc->geom.baseh) / cc->geom.inch);
width_size = font_width(sc, asize, strlen(asize)) + 4;
width_name = font_width(sc, cc->name, strlen(cc->name)) + 4;
width = MAX(width_size, width_name);
height = font_ascent(sc) + font_descent(sc) + 1;
(void)snprintf(s, sizeof(s), " %4d x %-4d ",
(cc->geom.w - cc->hint.basew) / cc->hint.incw,
(cc->geom.h - cc->hint.baseh) / cc->hint.inch);
XMoveResizeWindow(X_Dpy, sc->menuwin, cc->geom.x, cc->geom.y,
width, height * 2);
XMapWindow(X_Dpy, sc->menuwin);
XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0);
XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0,
xu_xft_width(sc->xftfont, s, strlen(s)), sc->xftfont->height);
XMapWindow(X_Dpy, sc->menuwin);
XClearWindow(X_Dpy, sc->menuwin);
font_draw(sc, cc->name, strlen(cc->name), sc->menuwin,
2, font_ascent(sc) + 1);
font_draw(sc, asize, strlen(asize), sc->menuwin,
width / 2 - width_size / 2, height + font_ascent(sc) + 1);
xu_xft_draw(sc, s, CWM_COLOR_MENU_FONT, 0, sc->xftfont->ascent + 1);
}
void
mousefunc_window_resize(struct client_ctx *cc, void *arg)
mousefunc_client_resize(struct client_ctx *cc, union arg *arg)
{
XEvent ev;
Time time = 0;
Time ltime = 0;
struct screen_ctx *sc = cc->sc;
int x = cc->geom.x, y = cc->geom.y;
if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
return;
client_raise(cc);
client_ptrsave(cc);
if (xu_ptr_grab(cc->win, MouseMask, Cursor_resize) < 0)
if (xu_ptr_grab(cc->win, MOUSEMASK, Conf.cursor[CF_RESIZE]) < 0)
return;
xu_ptr_setpos(cc->win, cc->geom.width, cc->geom.height);
xu_ptr_setpos(cc->win, cc->geom.w, cc->geom.h);
mousefunc_sweep_draw(cc);
for (;;) {
XMaskEvent(X_Dpy, MouseMask|ExposureMask, &ev);
XMaskEvent(X_Dpy, MOUSEMASK, &ev);
switch (ev.type) {
case Expose:
client_draw_border(cc);
break;
case MotionNotify:
if (mousefunc_sweep_calc(cc, x, y,
ev.xmotion.x_root, ev.xmotion.y_root))
/* Recompute window output */
mousefunc_sweep_draw(cc);
/* not more than 60 times / second */
if ((ev.xmotion.time - ltime) <= (1000 / 60))
continue;
ltime = ev.xmotion.time;
/* don't sync more than 60 times / second */
if ((ev.xmotion.time - time) > (1000 / 60)) {
time = ev.xmotion.time;
XSync(X_Dpy, False);
client_resize(cc);
}
mousefunc_sweep_calc(cc, x, y,
ev.xmotion.x_root, ev.xmotion.y_root);
client_resize(cc, 1);
mousefunc_sweep_draw(cc);
break;
case ButtonRelease:
if (time) {
XSync(X_Dpy, False);
client_resize(cc);
}
client_resize(cc, 1);
XUnmapWindow(X_Dpy, sc->menuwin);
XReparentWindow(X_Dpy, sc->menuwin, sc->rootwin, 0, 0);
xu_ptr_ungrab();
/* Make sure the pointer stays within the window. */
if (cc->ptr.x > cc->geom.width)
cc->ptr.x = cc->geom.width - cc->bwidth;
if (cc->ptr.y > cc->geom.height)
cc->ptr.y = cc->geom.height - cc->bwidth;
if (cc->ptr.x > cc->geom.w)
cc->ptr.x = cc->geom.w - cc->bwidth;
if (cc->ptr.y > cc->geom.h)
cc->ptr.y = cc->geom.h - cc->bwidth;
client_ptrwarp(cc);
return;
}
}
@ -136,42 +120,51 @@ mousefunc_window_resize(struct client_ctx *cc, void *arg)
}
void
mousefunc_window_move(struct client_ctx *cc, void *arg)
mousefunc_client_move(struct client_ctx *cc, union arg *arg)
{
XEvent ev;
Time time = 0;
Time ltime = 0;
struct screen_ctx *sc = cc->sc;
struct geom xine;
int px, py;
client_raise(cc);
if (xu_ptr_grab(cc->win, MouseMask, Cursor_move) < 0)
if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
return;
if (xu_ptr_grab(cc->win, MOUSEMASK, Conf.cursor[CF_MOVE]) < 0)
return;
xu_ptr_getpos(cc->win, &px, &py);
for (;;) {
XMaskEvent(X_Dpy, MouseMask|ExposureMask, &ev);
XMaskEvent(X_Dpy, MOUSEMASK, &ev);
switch (ev.type) {
case Expose:
client_draw_border(cc);
break;
case MotionNotify:
cc->geom.x = ev.xmotion.x_root - px;
cc->geom.y = ev.xmotion.y_root - py;
/* not more than 60 times / second */
if ((ev.xmotion.time - ltime) <= (1000 / 60))
continue;
ltime = ev.xmotion.time;
/* don't sync more than 60 times / second */
if ((ev.xmotion.time - time) > (1000 / 60)) {
time = ev.xmotion.time;
XSync(X_Dpy, False);
client_move(cc);
}
cc->geom.x = ev.xmotion.x_root - px - cc->bwidth;
cc->geom.y = ev.xmotion.y_root - py - cc->bwidth;
xine = screen_find_xinerama(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP);
cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.x + cc->geom.w + (cc->bwidth * 2),
xine.x, xine.x + xine.w, sc->snapdist);
cc->geom.y += client_snapcalc(cc->geom.y,
cc->geom.y + cc->geom.h + (cc->bwidth * 2),
xine.y, xine.y + xine.h, sc->snapdist);
client_move(cc);
break;
case ButtonRelease:
if (time) {
XSync(X_Dpy, False);
client_move(cc);
}
client_move(cc);
xu_ptr_ungrab();
return;
}
@ -180,100 +173,93 @@ mousefunc_window_move(struct client_ctx *cc, void *arg)
}
void
mousefunc_window_grouptoggle(struct client_ctx *cc, void *arg)
mousefunc_client_grouptoggle(struct client_ctx *cc, union arg *arg)
{
group_sticky_toggle_enter(cc);
group_toggle_membership_enter(cc);
}
void
mousefunc_window_lower(struct client_ctx *cc, void *arg)
mousefunc_menu_group(struct client_ctx *cc, union arg *arg)
{
client_ptrsave(cc);
client_lower(cc);
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
struct menu *mi;
struct menu_q menuq;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (group_holds_only_sticky(gc))
continue;
menuq_add(&menuq, gc,
group_holds_only_hidden(gc) ? "%d: [%s]" : "%d: %s",
gc->num, gc->name);
}
if (TAILQ_EMPTY(&menuq))
return;
if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0,
NULL, NULL)) != NULL) {
gc = (struct group_ctx *)mi->ctx;
(group_holds_only_hidden(gc)) ?
group_show(gc) : group_hide(gc);
}
menuq_clear(&menuq);
}
void
mousefunc_window_hide(struct client_ctx *cc, void *arg)
mousefunc_menu_unhide(struct client_ctx *cc, union arg *arg)
{
client_hide(cc);
}
void
mousefunc_menu_group(struct client_ctx *cc, void *arg)
{
group_menu(arg);
}
void
mousefunc_menu_unhide(struct client_ctx *cc, void *arg)
{
struct screen_ctx *sc;
struct screen_ctx *sc = cc->sc;
struct client_ctx *old_cc;
struct menu *mi;
struct menu_q menuq;
char *wname;
sc = cc->sc;
old_cc = client_current();
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &Clientq, entry)
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->flags & CLIENT_HIDDEN) {
wname = (cc->label) ? cc->label : cc->name;
if (wname == NULL)
continue;
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, wname, sizeof(mi->text));
mi->ctx = cc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
menuq_add(&menuq, cc, "(%d) %s",
cc->group ? cc->group->num : 0, wname);
}
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL) {
if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0,
NULL, NULL)) != NULL) {
cc = (struct client_ctx *)mi->ctx;
client_unhide(cc);
if (old_cc != NULL)
client_ptrsave(old_cc);
client_ptrwarp(cc);
} else {
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
menuq_clear(&menuq);
}
void
mousefunc_menu_cmd(struct client_ctx *cc, void *arg)
mousefunc_menu_cmd(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc;
struct screen_ctx *sc = cc->sc;
struct cmd *cmd;
struct menu *mi;
struct menu_q menuq;
struct cmd *cmd;
sc = cc->sc;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, cmd->label, sizeof(mi->text));
mi->ctx = cmd;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
TAILQ_FOREACH(cmd, &Conf.cmdq, entry)
menuq_add(&menuq, cmd, "%s", cmd->name);
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL)
u_spawn(((struct cmd *)mi->ctx)->image);
else
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0,
NULL, NULL)) != NULL)
u_spawn(((struct cmd *)mi->ctx)->path);
menuq_clear(&menuq);
}

243
parse.y
View File

@ -21,7 +21,7 @@
%{
#include <sys/param.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <ctype.h>
@ -35,6 +35,8 @@
#include "calmwm.h"
#define YYSTYPE_IS_DECLARED
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
static struct file {
TAILQ_ENTRY(file) entry;
@ -42,18 +44,19 @@ static struct file {
char *name;
int lineno;
int errors;
} *file;
struct file *pushfile(const char *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...);
int kw_cmp(const void *, const void *);
int lookup(char *);
int lgetc(int);
int lungetc(int);
int findeol(void);
} *file, *topfile;
struct file *pushfile(const char *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...)
__attribute__((__format__ (printf, 1, 2)))
__attribute__((__nonnull__ (1)));
int kw_cmp(const void *, const void *);
int lookup(char *);
int lgetc(int);
int lungetc(int);
int findeol(void);
static struct conf *conf;
@ -70,9 +73,11 @@ typedef struct {
%token FONTNAME STICKY GAP MOUSEBIND
%token AUTOGROUP BIND COMMAND IGNORE
%token YES NO BORDERWIDTH MOVEAMOUNT
%token COLOR
%token ACTIVEBORDER INACTIVEBORDER
%token COLOR SNAPDIST
%token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
%token GROUPBORDER UNGROUPBORDER
%token MENUBG MENUFG
%token FONTCOLOR FONTSELCOLOR
%token ERROR
%token <v.string> STRING
%token <v.number> NUMBER
@ -105,8 +110,8 @@ yesno : YES { $$ = 1; }
;
main : FONTNAME STRING {
free(conf->DefaultFontName);
conf->DefaultFontName = $2;
free(conf->font);
conf->font = $2;
}
| STICKY yesno {
if ($2 == 0)
@ -115,48 +120,80 @@ main : FONTNAME STRING {
conf->flags |= CONF_STICKY_GROUPS;
}
| BORDERWIDTH NUMBER {
if ($2 < 0 || $2 > UINT_MAX) {
yyerror("invalid borderwidth: %lld", $2);
YYERROR;
}
conf->bwidth = $2;
}
| MOVEAMOUNT NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid movemount: %lld", $2);
YYERROR;
}
conf->mamount = $2;
}
| SNAPDIST NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid snapdist: %lld", $2);
YYERROR;
}
conf->snapdist = $2;
}
| COMMAND STRING string {
conf_cmd_add(conf, $3, $2, 0);
if (!conf_cmd_add(conf, $2, $3)) {
yyerror("command name/path too long");
free($2);
free($3);
YYERROR;
}
free($2);
free($3);
}
| AUTOGROUP NUMBER STRING {
if ($2 < 0 || $2 > 9) {
free($3);
yyerror("autogroup number out of range: %d", $2);
yyerror("invalid autogroup: %lld", $2);
YYERROR;
}
group_make_autogroup(conf, $3, $2);
conf_autogroup(conf, $2, $3);
free($3);
}
| IGNORE STRING {
struct winmatch *wm;
wm = xcalloc(1, sizeof(*wm));
strlcpy(wm->title, $2, sizeof(wm->title));
TAILQ_INSERT_TAIL(&conf->ignoreq, wm, entry);
conf_ignore(conf, $2);
free($2);
}
| BIND STRING string {
conf_bindname(conf, $2, $3);
if (!conf_bind_kbd(conf, $2, $3)) {
yyerror("invalid bind: %s %s", $2, $3);
free($2);
free($3);
YYERROR;
}
free($2);
free($3);
}
| GAP NUMBER NUMBER NUMBER NUMBER {
if ($2 < 0 || $2 > INT_MAX ||
$3 < 0 || $3 > INT_MAX ||
$4 < 0 || $4 > INT_MAX ||
$5 < 0 || $5 > INT_MAX) {
yyerror("invalid gap: %lld %lld %lld %lld",
$2, $3, $4, $5);
YYERROR;
}
conf->gap.top = $2;
conf->gap.bottom = $3;
conf->gap.left = $4;
conf->gap.right = $5;
}
| MOUSEBIND STRING string {
conf_mousebind(conf, $2, $3);
if (!conf_bind_mouse(conf, $2, $3)) {
yyerror("invalid mousebind: %s %s", $2, $3);
free($2);
free($3);
YYERROR;
}
free($2);
free($3);
}
@ -166,20 +203,40 @@ color : COLOR colors
;
colors : ACTIVEBORDER STRING {
free(conf->color[CWM_COLOR_BORDOR_ACTIVE].name);
conf->color[CWM_COLOR_BORDOR_ACTIVE].name = $2;
free(conf->color[CWM_COLOR_BORDER_ACTIVE]);
conf->color[CWM_COLOR_BORDER_ACTIVE] = $2;
}
| INACTIVEBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_INACTIVE].name);
conf->color[CWM_COLOR_BORDER_INACTIVE].name = $2;
free(conf->color[CWM_COLOR_BORDER_INACTIVE]);
conf->color[CWM_COLOR_BORDER_INACTIVE] = $2;
}
| URGENCYBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_URGENCY]);
conf->color[CWM_COLOR_BORDER_URGENCY] = $2;
}
| GROUPBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_GROUP].name);
conf->color[CWM_COLOR_BORDER_GROUP].name = $2;
free(conf->color[CWM_COLOR_BORDER_GROUP]);
conf->color[CWM_COLOR_BORDER_GROUP] = $2;
}
| UNGROUPBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_UNGROUP].name);
conf->color[CWM_COLOR_BORDER_UNGROUP].name = $2;
free(conf->color[CWM_COLOR_BORDER_UNGROUP]);
conf->color[CWM_COLOR_BORDER_UNGROUP] = $2;
}
| MENUBG STRING {
free(conf->color[CWM_COLOR_MENU_BG]);
conf->color[CWM_COLOR_MENU_BG] = $2;
}
| MENUFG STRING {
free(conf->color[CWM_COLOR_MENU_FG]);
conf->color[CWM_COLOR_MENU_FG] = $2;
}
| FONTCOLOR STRING {
free(conf->color[CWM_COLOR_MENU_FONT]);
conf->color[CWM_COLOR_MENU_FONT] = $2;
}
| FONTSELCOLOR STRING {
free(conf->color[CWM_COLOR_MENU_FONT_SEL]);
conf->color[CWM_COLOR_MENU_FONT_SEL] = $2;
}
;
%%
@ -192,7 +249,7 @@ struct keywords {
int
yyerror(const char *fmt, ...)
{
va_list ap;
va_list ap;
file->errors++;
va_start(ap, fmt);
@ -220,16 +277,22 @@ lookup(char *s)
{ "borderwidth", BORDERWIDTH},
{ "color", COLOR},
{ "command", COMMAND},
{ "font", FONTCOLOR},
{ "fontname", FONTNAME},
{ "gap", GAP},
{ "groupborder", GROUPBORDER},
{ "ignore", IGNORE},
{ "inactiveborder", INACTIVEBORDER},
{ "menubg", MENUBG},
{ "menufg", MENUFG},
{ "mousebind", MOUSEBIND},
{ "moveamount", MOVEAMOUNT},
{ "no", NO},
{ "selfont", FONTSELCOLOR},
{ "snapdist", SNAPDIST},
{ "sticky", STICKY},
{ "ungroupborder", UNGROUPBORDER},
{ "urgencyborder", URGENCYBORDER},
{ "yes", YES}
};
const struct keywords *p;
@ -271,8 +334,9 @@ lgetc(int quotec)
if (quotec) {
if ((c = getc(file->stream)) == EOF) {
yyerror("reached end of file while parsing quoted string");
if (popfile() == EOF)
yyerror("reached end of file while parsing "
"quoted string");
if (file == topfile || popfile() == EOF)
return (EOF);
return (quotec);
}
@ -290,7 +354,7 @@ lgetc(int quotec)
}
while (c == EOF) {
if (popfile() == EOF)
if (file == topfile || popfile() == EOF)
return (EOF);
c = getc(file->stream);
}
@ -319,11 +383,13 @@ findeol(void)
int c;
parsebuf = NULL;
pushback_index = 0;
/* skip to either EOF or the first real EOL */
while (1) {
c = lgetc(0);
if (pushback_index)
c = pushback_buffer[--pushback_index];
else
c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
@ -374,6 +440,9 @@ yylex(void)
} else if (c == quotec) {
*p = '\0';
break;
} else if (c == '\0') {
yyerror("syntax error");
return (findeol());
}
if (p + 1 >= buf + sizeof(buf) - 1) {
yyerror("string too long");
@ -421,6 +490,7 @@ nodigits:
}
}
/* Similar to other parse.y copies, but also allows '/' in strings */
#define allowed_in_string(x) \
(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
x != '{' && x != '}' && x != '<' && x != '>' && \
@ -458,8 +528,7 @@ pushfile(const char *name)
nfile->name = xstrdup(name);
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
if (errno != ENOENT)
warn("%s", nfile->name);
warn("%s", nfile->name);
free(nfile->name);
free(nfile);
return (NULL);
@ -474,94 +543,32 @@ popfile(void)
{
struct file *prev;
if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
prev->errors += file->errors;
TAILQ_REMOVE(&files, file, entry);
fclose(file->stream);
free(file->name);
free(file);
file = prev;
return (0);
}
return (EOF);
TAILQ_REMOVE(&files, file, entry);
fclose(file->stream);
free(file->name);
free(file);
file = prev;
return (file ? 0 : EOF);
}
int
parse_config(const char *filename, struct conf *xconf)
{
int errors = 0;
int errors = 0;
conf = xcalloc(1, sizeof(*conf));
conf = xconf;
if ((file = pushfile(filename)) == NULL) {
free(conf);
return (-1);
}
strlcpy(conf->conf_path, filename, sizeof(conf->conf_path));
conf_init(conf);
topfile = file;
yyparse();
errors = file->errors;
file->errors = 0;
popfile();
if (errors) {
conf_clear(conf);
}
else {
struct autogroupwin *ag;
struct keybinding *kb;
struct winmatch *wm;
struct cmd *cmd;
struct mousebinding *mb;
int i;
conf_clear(xconf);
xconf->flags = conf->flags;
xconf->bwidth = conf->bwidth;
xconf->mamount = conf->mamount;
xconf->gap = conf->gap;
while ((cmd = TAILQ_FIRST(&conf->cmdq)) != NULL) {
TAILQ_REMOVE(&conf->cmdq, cmd, entry);
TAILQ_INSERT_TAIL(&xconf->cmdq, cmd, entry);
}
while ((kb = TAILQ_FIRST(&conf->keybindingq)) != NULL) {
TAILQ_REMOVE(&conf->keybindingq, kb, entry);
TAILQ_INSERT_TAIL(&xconf->keybindingq, kb, entry);
}
while ((ag = TAILQ_FIRST(&conf->autogroupq)) != NULL) {
TAILQ_REMOVE(&conf->autogroupq, ag, entry);
TAILQ_INSERT_TAIL(&xconf->autogroupq, ag, entry);
}
while ((wm = TAILQ_FIRST(&conf->ignoreq)) != NULL) {
TAILQ_REMOVE(&conf->ignoreq, wm, entry);
TAILQ_INSERT_TAIL(&xconf->ignoreq, wm, entry);
}
while ((mb = TAILQ_FIRST(&conf->mousebindingq)) != NULL) {
TAILQ_REMOVE(&conf->mousebindingq, mb, entry);
TAILQ_INSERT_TAIL(&xconf->mousebindingq, mb, entry);
}
strlcpy(xconf->termpath, conf->termpath,
sizeof(xconf->termpath));
strlcpy(xconf->lockpath, conf->lockpath,
sizeof(xconf->lockpath));
for (i = 0; i < CWM_COLOR_MAX; i++)
xconf->color[i].name = conf->color[i].name;
xconf->DefaultFontName = conf->DefaultFontName;
}
free(conf);
return (errors ? -1 : 0);
}

568
queue.h Normal file
View File

@ -0,0 +1,568 @@
/* $OpenBSD: "queue.h",v 1.36 2012/04/11 13:29:14 naddy Exp $ */
/* $NetBSD: "queue.h",v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)"queue.h" 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST(head); \
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SIMPLEQ_FIRST(head); \
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
== NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
(var) = (tvar))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = CIRCLEQ_LAST(head, headname); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head).cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head).cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */

204
screen.c
View File

@ -15,32 +15,93 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
void
screen_init(int which)
{
struct screen_ctx *sc;
Window *wins, w0, w1;
XSetWindowAttributes rootattr;
unsigned int nwins, i;
sc = xmalloc(sizeof(*sc));
TAILQ_INIT(&sc->clientq);
TAILQ_INIT(&sc->regionq);
TAILQ_INIT(&sc->groupq);
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, sc->which);
sc->cycling = 0;
sc->hideall = 0;
conf_screen(sc);
xu_ewmh_net_supported(sc);
xu_ewmh_net_supported_wm_check(sc);
screen_update_geometry(sc);
for (i = 0; i < CALMWM_NGROUPS; i++)
group_init(sc, i);
xu_ewmh_net_desktop_names(sc);
xu_ewmh_net_wm_desktop_viewport(sc);
xu_ewmh_net_wm_number_of_desktops(sc);
xu_ewmh_net_showing_desktop(sc);
xu_ewmh_net_virtual_roots(sc);
rootattr.cursor = Conf.cursor[CF_NORMAL];
rootattr.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|
PropertyChangeMask|EnterWindowMask|LeaveWindowMask|
ColormapChangeMask|BUTTONMASK;
XChangeWindowAttributes(X_Dpy, sc->rootwin,
CWEventMask|CWCursor, &rootattr);
/* Deal with existing clients. */
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (i = 0; i < nwins; i++)
(void)client_init(wins[i], sc);
XFree(wins);
}
screen_updatestackingorder(sc);
if (HasRandr)
XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
TAILQ_INSERT_TAIL(&Screenq, sc, entry);
XSync(X_Dpy, False);
}
struct screen_ctx *
screen_fromroot(Window rootwin)
screen_find(Window win)
{
struct screen_ctx *sc;
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == rootwin)
return (sc);
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->rootwin == win)
return(sc);
}
/* XXX FAIL HERE */
return (TAILQ_FIRST(&Screenq));
return(TAILQ_FIRST(&Screenq));
}
void
@ -48,86 +109,83 @@ screen_updatestackingorder(struct screen_ctx *sc)
{
Window *wins, w0, w1;
struct client_ctx *cc;
u_int nwins, i, s;
unsigned int nwins, i, s;
if (!XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins))
return;
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (s = 0, i = 0; i < nwins; i++) {
/* Skip hidden windows */
if ((cc = client_find(wins[i])) == NULL ||
cc->flags & CLIENT_HIDDEN)
continue;
for (s = 0, i = 0; i < nwins; i++) {
/* Skip hidden windows */
if ((cc = client_find(wins[i])) == NULL ||
cc->flags & CLIENT_HIDDEN)
continue;
cc->stackingorder = s++;
cc->stackingorder = s++;
}
XFree(wins);
}
XFree(wins);
}
void
screen_init_xinerama(struct screen_ctx *sc)
{
XineramaScreenInfo *info;
int no;
if (HasXinerama == 0 || XineramaIsActive(X_Dpy) == 0) {
HasXinerama = 0;
sc->xinerama_no = 0;
}
info = XineramaQueryScreens(X_Dpy, &no);
if (info == NULL) {
/* Is xinerama actually off, instead of a malloc failure? */
if (sc->xinerama == NULL)
HasXinerama = 0;
return;
}
if (sc->xinerama != NULL)
XFree(sc->xinerama);
sc->xinerama = info;
sc->xinerama_no = no;
}
/*
* Find which xinerama screen the coordinates (x,y) is on.
*/
XineramaScreenInfo *
screen_find_xinerama(struct screen_ctx *sc, int x, int y)
struct geom
screen_find_xinerama(struct screen_ctx *sc, int x, int y, int flags)
{
XineramaScreenInfo *info;
int i;
struct region_ctx *region;
struct geom geom = sc->work;
for (i = 0; i < sc->xinerama_no; i++) {
info = &sc->xinerama[i];
if (x > info->x_org && x < info->x_org + info->width &&
y > info->y_org && y < info->y_org + info->height)
return (info);
TAILQ_FOREACH(region, &sc->regionq, entry) {
if (x >= region->area.x && x < region->area.x+region->area.w &&
y >= region->area.y && y < region->area.y+region->area.h) {
geom = region->area;
break;
}
}
return (NULL);
if (flags & CWM_GAP) {
geom.x += sc->gap.left;
geom.y += sc->gap.top;
geom.w -= (sc->gap.left + sc->gap.right);
geom.h -= (sc->gap.top + sc->gap.bottom);
}
return(geom);
}
void
screen_update_geometry(struct screen_ctx *sc, int width, int height)
screen_update_geometry(struct screen_ctx *sc)
{
long geom[2], workareas[CALMWM_NGROUPS][4];
int i;
XineramaScreenInfo *info = NULL;
struct region_ctx *region;
int info_num = 0, i;
sc->xmax = geom[0] = width;
sc->ymax = geom[1] = height;
XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_GEOMETRY,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
sc->view.x = 0;
sc->view.y = 0;
sc->view.w = DisplayWidth(X_Dpy, sc->which);
sc->view.h = DisplayHeight(X_Dpy, sc->which);
/* x, y, width, height. */
for (i = 0; i < CALMWM_NGROUPS; i++) {
workareas[i][0] = sc->gap.left;
workareas[i][1] = sc->gap.top;
workareas[i][2] = width - (sc->gap.left + sc->gap.right);
workareas[i][3] = height - (sc->gap.top + sc->gap.bottom);
sc->work.x = sc->view.x + sc->gap.left;
sc->work.y = sc->view.y + sc->gap.top;
sc->work.w = sc->view.w - (sc->gap.left + sc->gap.right);
sc->work.h = sc->view.h - (sc->gap.top + sc->gap.bottom);
/* RandR event may have a CTRC added or removed. */
if (XineramaIsActive(X_Dpy))
info = XineramaQueryScreens(X_Dpy, &info_num);
while ((region = TAILQ_FIRST(&sc->regionq)) != NULL) {
TAILQ_REMOVE(&sc->regionq, region, entry);
free(region);
}
for (i = 0; i < info_num; i++) {
region = xmalloc(sizeof(*region));
region->num = i;
region->area.x = info[i].x_org;
region->area.y = info[i].y_org;
region->area.w = info[i].width;
region->area.h = info[i].height;
TAILQ_INSERT_TAIL(&sc->regionq, region, entry);
}
if (info)
XFree(info);
XChangeProperty(X_Dpy, sc->rootwin, _NET_WORKAREA,
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)workareas, CALMWM_NGROUPS * 4);
xu_ewmh_net_desktop_geometry(sc);
xu_ewmh_net_workarea(sc);
}

105
search.c
View File

@ -2,6 +2,7 @@
* calmwm - the calm window manager
*
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
@ -14,23 +15,32 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#define PATH_ANY 0x0001
#define PATH_EXEC 0x0002
static void search_match_path(struct menu_q *, struct menu_q *,
char *, int);
static void search_match_path_exec(struct menu_q *, struct menu_q *,
char *);
static int strsubmatch(char *, char *, int);
/*
@ -42,13 +52,10 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct winname *wn;
struct menu *mi, *tierp[4], *before = NULL;
int ntiers;
ntiers = sizeof(tierp) / sizeof(tierp[0]);
TAILQ_INIT(resultq);
memset(tierp, 0, sizeof(tierp));
(void)memset(tierp, 0, sizeof(tierp));
/*
* In order of rank:
@ -79,8 +86,8 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
}
/* Then if there is a match on the window class name. */
if (tier < 0 && strsubmatch(search, cc->app_class, 0)) {
cc->matchname = cc->app_class;
if (tier < 0 && strsubmatch(search, cc->ch.res_class, 0)) {
cc->matchname = cc->ch.res_class;
tier = 3;
}
@ -92,14 +99,14 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
* window. Furthermore, this is denoted by a "!" when
* printing the window name in the search menu.
*/
if (cc == client_current() && tier < ntiers - 1)
if (cc == client_current() && tier < nitems(tierp) - 1)
tier++;
/* Clients that are hidden get ranked one up. */
if (cc->flags & CLIENT_HIDDEN && tier > 0)
if ((cc->flags & CLIENT_HIDDEN) && (tier > 0))
tier--;
assert(tier < ntiers);
assert(tier < nitems(tierp));
/*
* If you have a tierp, insert after it, and make it
@ -136,7 +143,8 @@ search_print_client(struct menu *mi, int list)
if (list)
cc->matchname = cc->name;
snprintf(mi->print, sizeof(mi->print), "%c%s", flag, cc->matchname);
(void)snprintf(mi->print, sizeof(mi->print), "(%d) %c%s",
cc->group ? cc->group->num : 0, flag, cc->matchname);
if (!list && cc->matchname != cc->name &&
strlen(mi->print) < sizeof(mi->print) - 1) {
@ -156,12 +164,46 @@ search_print_client(struct menu *mi, int list)
diff = strlen(cc->name);
}
strlcpy(buf, mi->print, sizeof(buf));
snprintf(mi->print, sizeof(mi->print),
(void)strlcpy(buf, mi->print, sizeof(buf));
(void)snprintf(mi->print, sizeof(mi->print),
"%s:%.*s%s", buf, diff, cc->name, marker);
}
}
static void
search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search, int flag)
{
char pattern[PATH_MAX];
glob_t g;
int i;
TAILQ_INIT(resultq);
(void)strlcpy(pattern, search, sizeof(pattern));
(void)strlcat(pattern, "*", sizeof(pattern));
if (glob(pattern, GLOB_MARK, NULL, &g) != 0)
return;
for (i = 0; i < g.gl_pathc; i++) {
if ((flag & PATH_EXEC) && access(g.gl_pathv[i], X_OK))
continue;
menuq_add(resultq, NULL, "%s", g.gl_pathv[i]);
}
globfree(&g);
}
static void
search_match_path_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
return(search_match_path(menuq, resultq, search, PATH_EXEC));
}
void
search_match_path_any(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
return(search_match_path(menuq, resultq, search, PATH_ANY));
}
void
search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
@ -178,39 +220,48 @@ void
search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi, *mj;
int r;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
if (strsubmatch(search, mi->text, 1) == 0 &&
fnmatch(search, mi->text, 0) == FNM_NOMATCH)
continue;
for (mj = TAILQ_FIRST(resultq); mj != NULL;
mj = TAILQ_NEXT(mj, resultentry)) {
if (strcasecmp(mi->text, mj->text) < 0) {
continue;
TAILQ_FOREACH(mj, resultq, resultentry) {
r = strcasecmp(mi->text, mj->text);
if (r < 0)
TAILQ_INSERT_BEFORE(mj, mi, resultentry);
if (r <= 0)
break;
}
}
if (mj == NULL)
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
void
search_match_exec_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
search_match_exec(menuq, resultq, search);
if (TAILQ_EMPTY(resultq))
search_match_path_exec(menuq, resultq, search);
}
static int
strsubmatch(char *sub, char *str, int zeroidx)
{
size_t len, sublen;
u_int n, flen;
size_t len, sublen;
unsigned int n, flen;
if (sub == NULL || str == NULL)
return (0);
return(0);
len = strlen(str);
sublen = strlen(sub);
if (sublen > len)
return (0);
return(0);
if (!zeroidx)
flen = len - sublen;
@ -219,7 +270,7 @@ strsubmatch(char *sub, char *str, int zeroidx)
for (n = 0; n <= flen; n++)
if (strncasecmp(sub, str + n, sublen) == 0)
return (1);
return(1);
return (0);
return(0);
}

62
strlcat.c Normal file
View File

@ -0,0 +1,62 @@
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
#include <sys/types.h>
#include <string.h>
#include "calmwm.h"
#ifndef HAVE_STRLCAT
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
#endif /* !HAVE_STRLCAT */

58
strlcpy.c Normal file
View File

@ -0,0 +1,58 @@
/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
#include <sys/types.h>
#include <string.h>
#include "calmwm.h"
#ifndef HAVE_STRLCPY
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif /* !HAVE_STRLCPY */

70
strtonum.c Normal file
View File

@ -0,0 +1,70 @@
/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
/*
* Copyright (c) 2004 Ted Unangst and Todd Miller
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */
#ifndef HAVE_STRTONUM
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
char *ep;
int error = 0;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
};
ev[0].err = errno;
errno = 0;
if (minval > maxval)
error = INVALID;
else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
error = TOOSMALL;
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
error = TOOLARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
return (ll);
}
#endif /* HAVE_STRTONUM */

1
update.sh Executable file
View File

@ -0,0 +1 @@
GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@openbsd.cs.fau.de:/cvs xenocara/app/cwm

18
util.c
View File

@ -15,24 +15,25 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#define MAXARGLEN 20
int
void
u_spawn(char *argstr)
{
switch (fork()) {
@ -42,12 +43,9 @@ u_spawn(char *argstr)
break;
case -1:
warn("fork");
return (-1);
default:
break;
}
return (0);
}
void
@ -79,6 +77,6 @@ u_exec(char *argstr)
}
*ap = NULL;
setsid();
execvp(args[0], args);
(void)setsid();
(void)execvp(args[0], args);
}

286
xevents.c
View File

@ -15,7 +15,7 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
/*
@ -24,14 +24,15 @@
* management of the xevent's.
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
@ -42,83 +43,64 @@ static void xev_handle_destroynotify(XEvent *);
static void xev_handle_configurerequest(XEvent *);
static void xev_handle_propertynotify(XEvent *);
static void xev_handle_enternotify(XEvent *);
static void xev_handle_leavenotify(XEvent *);
static void xev_handle_buttonpress(XEvent *);
static void xev_handle_buttonrelease(XEvent *);
static void xev_handle_keypress(XEvent *);
static void xev_handle_keyrelease(XEvent *);
static void xev_handle_expose(XEvent *);
static void xev_handle_clientmessage(XEvent *);
static void xev_handle_randr(XEvent *);
static void xev_handle_mappingnotify(XEvent *);
static void xev_handle_expose(XEvent *);
void (*xev_handlers[LASTEvent])(XEvent *) = {
[MapRequest] = xev_handle_maprequest,
[UnmapNotify] = xev_handle_unmapnotify,
[DestroyNotify] = xev_handle_destroynotify,
[ConfigureRequest] = xev_handle_configurerequest,
[PropertyNotify] = xev_handle_propertynotify,
[EnterNotify] = xev_handle_enternotify,
[LeaveNotify] = xev_handle_leavenotify,
[ButtonPress] = xev_handle_buttonpress,
[ButtonRelease] = xev_handle_buttonrelease,
[KeyPress] = xev_handle_keypress,
[KeyRelease] = xev_handle_keyrelease,
[Expose] = xev_handle_expose,
[DestroyNotify] = xev_handle_destroynotify,
[ClientMessage] = xev_handle_clientmessage,
[MappingNotify] = xev_handle_mappingnotify,
[Expose] = xev_handle_expose,
};
static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R,
XK_Control_L, XK_Control_R };
static void
xev_handle_maprequest(XEvent *ee)
{
XMapRequestEvent *e = &ee->xmaprequest;
struct client_ctx *cc = NULL, *old_cc;
XWindowAttributes xattr;
struct winmatch *wm;
if ((old_cc = client_current()) != NULL)
if ((old_cc = client_current()))
client_ptrsave(old_cc);
if ((cc = client_find(e->window)) == NULL) {
XGetWindowAttributes(X_Dpy, e->window, &xattr);
cc = client_new(e->window, screen_fromroot(xattr.root), 1);
}
if ((cc = client_find(e->window)) == NULL)
cc = client_init(e->window, NULL);
TAILQ_FOREACH(wm, &Conf.ignoreq, entry) {
if (strncasecmp(wm->title, cc->name, strlen(wm->title)) == 0)
return;
}
client_ptrwarp(cc);
if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
client_ptrwarp(cc);
}
static void
xev_handle_unmapnotify(XEvent *ee)
{
XUnmapEvent *e = &ee->xunmap;
XEvent ev;
struct client_ctx *cc;
/* XXX, we need a recursive locking wrapper around grab server */
XGrabServer(X_Dpy);
if ((cc = client_find(e->window)) != NULL) {
/*
* If it's going to die anyway, nuke it.
*
* Else, if it's a synthetic event delete state, since they
* want it to be withdrawn. ICCM recommends you withdraw on
* this even if we haven't alredy been told to iconify, to
* deal with legacy clients.
*/
if (XCheckTypedWindowEvent(X_Dpy, cc->win,
DestroyNotify, &ev) || e->send_event != 0) {
client_delete(cc);
} else
client_hide(cc);
if (e->send_event) {
client_set_wm_state(cc, WithdrawnState);
} else {
if (!(cc->flags & CLIENT_HIDDEN))
client_delete(cc);
}
}
XUngrabServer(X_Dpy);
}
static void
@ -143,30 +125,34 @@ xev_handle_configurerequest(XEvent *ee)
sc = cc->sc;
if (e->value_mask & CWWidth)
cc->geom.width = e->width;
cc->geom.w = e->width;
if (e->value_mask & CWHeight)
cc->geom.height = e->height;
cc->geom.h = e->height;
if (e->value_mask & CWX)
cc->geom.x = e->x;
if (e->value_mask & CWY)
cc->geom.y = e->y;
if (e->value_mask & CWBorderWidth)
wc.border_width = e->border_width;
cc->bwidth = e->border_width;
if (e->value_mask & CWSibling)
wc.sibling = e->above;
if (e->value_mask & CWStackMode)
wc.stack_mode = e->detail;
if (cc->geom.x == 0 && cc->geom.width >= sc->xmax)
if (cc->geom.x == 0 && cc->geom.w >= sc->view.w)
cc->geom.x -= cc->bwidth;
if (cc->geom.y == 0 && cc->geom.height >= sc->ymax)
if (cc->geom.y == 0 && cc->geom.h >= sc->view.h)
cc->geom.y -= cc->bwidth;
wc.x = cc->geom.x;
wc.y = cc->geom.y;
wc.width = cc->geom.width;
wc.height = cc->geom.height;
wc.width = cc->geom.w;
wc.height = cc->geom.h;
wc.border_width = cc->bwidth;
XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc);
xev_reconfig(cc);
client_config(cc);
} else {
/* let it do what it wants, it'll be ours when we map it. */
wc.x = e->x;
@ -196,40 +182,25 @@ xev_handle_propertynotify(XEvent *ee)
case XA_WM_NAME:
client_setname(cc);
break;
case XA_WM_HINTS:
client_wm_hints(cc);
client_draw_border(cc);
break;
case XA_WM_TRANSIENT_FOR:
client_transient(cc);
break;
default:
/* do nothing */
break;
}
} else {
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == e->window)
goto test;
return;
test:
if (e->atom == _NET_DESKTOP_NAMES)
group_update_names(sc);
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->rootwin == e->window) {
if (e->atom == ewmh[_NET_DESKTOP_NAMES])
xu_ewmh_net_desktop_names(sc);
}
}
}
}
void
xev_reconfig(struct client_ctx *cc)
{
XConfigureEvent ce;
ce.type = ConfigureNotify;
ce.event = cc->win;
ce.window = cc->win;
ce.x = cc->geom.x;
ce.y = cc->geom.y;
ce.width = cc->geom.width;
ce.height = cc->geom.height;
ce.border_width = cc->bwidth;
ce.above = None;
ce.override_redirect = 0;
XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&ce);
}
static void
@ -238,14 +209,10 @@ xev_handle_enternotify(XEvent *ee)
XCrossingEvent *e = &ee->xcrossing;
struct client_ctx *cc;
if ((cc = client_find(e->window)) != NULL)
client_setactive(cc, 1);
}
Last_Event_Time = e->time;
static void
xev_handle_leavenotify(XEvent *ee)
{
client_leave(NULL);
if ((cc = client_find(e->window)) != NULL)
client_setactive(cc);
}
/* We can split this into two event handlers. */
@ -254,31 +221,29 @@ xev_handle_buttonpress(XEvent *ee)
{
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc, fakecc;
struct screen_ctx *sc;
struct mousebinding *mb;
struct binding *mb;
sc = screen_fromroot(e->root);
cc = client_find(e->window);
/* Ignore caps lock and numlock */
e->state &= ~(Mod2Mask | LockMask);
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
if (e->button == mb->button && e->state == mb->modmask)
if (e->button == mb->press.button && e->state == mb->modmask)
break;
}
if (mb == NULL)
return;
if (mb->context == MOUSEBIND_CTX_ROOT) {
if (e->window != sc->rootwin)
if (mb->flags & CWM_WIN) {
if (((cc = client_find(e->window)) == NULL) &&
(cc = client_current()) == NULL)
return;
} else {
if (e->window != e->root)
return;
cc = &fakecc;
cc->sc = screen_fromroot(e->window);
} else if (cc == NULL) /* (mb->context == MOUSEBIND_CTX_WIN */
return;
cc->sc = screen_find(e->window);
}
(*mb->callback)(cc, e);
(*mb->callback)(cc, &mb->argument);
}
static void
@ -286,8 +251,8 @@ xev_handle_buttonrelease(XEvent *ee)
{
struct client_ctx *cc;
if ((cc = client_current()) != NULL)
group_sticky_toggle_exit(cc);
if ((cc = client_current()))
group_toggle_membership_leave(cc);
}
static void
@ -295,18 +260,17 @@ xev_handle_keypress(XEvent *ee)
{
XKeyEvent *e = &ee->xkey;
struct client_ctx *cc = NULL, fakecc;
struct keybinding *kb;
struct binding *kb;
KeySym keysym, skeysym;
int modshift;
unsigned int modshift;
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1);
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
/* we don't care about caps lock and numlock here */
e->state &= ~(LockMask | Mod2Mask);
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
if (keysym != kb->keysym && skeysym == kb->keysym)
if (keysym != kb->press.keysym && skeysym == kb->press.keysym)
modshift = ShiftMask;
else
modshift = 0;
@ -314,55 +278,43 @@ xev_handle_keypress(XEvent *ee)
if ((kb->modmask | modshift) != e->state)
continue;
if ((kb->keycode != 0 && kb->keysym == NoSymbol &&
kb->keycode == e->keycode) || kb->keysym ==
(modshift == 0 ? keysym : skeysym))
if (kb->press.keysym == (modshift == 0 ? keysym : skeysym))
break;
}
if (kb == NULL)
return;
if (kb->flags & KBFLAG_NEEDCLIENT) {
if (kb->flags & CWM_WIN) {
if (((cc = client_find(e->window)) == NULL) &&
(cc = client_current()) == NULL)
return;
} else {
cc = &fakecc;
cc->sc = screen_fromroot(e->window);
cc->sc = screen_find(e->window);
}
(*kb->callback)(cc, &kb->argument);
}
/*
* This is only used for the alt suppression detection.
* This is only used for the modifier suppression detection.
*/
static void
xev_handle_keyrelease(XEvent *ee)
{
XKeyEvent *e = &ee->xkey;
struct screen_ctx *sc;
struct client_ctx *cc;
int keysym;
KeySym keysym;
unsigned int i;
sc = screen_fromroot(e->root);
cc = client_current();
sc = screen_find(e->root);
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
if (keysym != XK_Alt_L && keysym != XK_Alt_R)
return;
sc->altpersist = 0;
/*
* XXX - better interface... xevents should not know about
* how/when to mtf.
*/
client_mtf(NULL);
if (cc != NULL) {
group_sticky_toggle_exit(cc);
XUngrabKeyboard(X_Dpy, CurrentTime);
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
for (i = 0; i < nitems(modkeys); i++) {
if (keysym == modkeys[i]) {
client_cycle_leave(sc);
break;
}
}
}
@ -370,17 +322,45 @@ static void
xev_handle_clientmessage(XEvent *ee)
{
XClientMessageEvent *e = &ee->xclient;
Atom xa_wm_change_state;
struct client_ctx *cc;
struct client_ctx *cc, *old_cc;
struct screen_ctx *sc;
xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
sc = screen_find(e->window);
if ((cc = client_find(e->window)) == NULL)
if ((cc = client_find(e->window)) == NULL && e->window != sc->rootwin)
return;
if (e->message_type == xa_wm_change_state && e->format == 32 &&
if (e->message_type == cwmh[WM_CHANGE_STATE] && e->format == 32 &&
e->data.l[0] == IconicState)
client_hide(cc);
if (e->message_type == ewmh[_NET_CLOSE_WINDOW])
client_send_delete(cc);
if (e->message_type == ewmh[_NET_ACTIVE_WINDOW] && e->format == 32) {
if ((old_cc = client_current()))
client_ptrsave(old_cc);
client_ptrwarp(cc);
}
if (e->message_type == ewmh[_NET_WM_DESKTOP] && e->format == 32) {
/*
* The EWMH spec states that if the cardinal returned is
* 0xFFFFFFFF (-1) then the window should appear on all
* desktops, which in our case is assigned to group 0.
*/
if (e->data.l[0] == (unsigned long)-1)
group_movetogroup(cc, 0);
else
group_movetogroup(cc, e->data.l[0]);
}
if (e->message_type == ewmh[_NET_WM_STATE] && e->format == 32)
xu_ewmh_handle_net_wm_state_msg(cc,
e->data.l[0], e->data.l[1], e->data.l[2]);
if (e->message_type == ewmh[_NET_CURRENT_DESKTOP] && e->format == 32)
group_only(sc, e->data.l[0]);
}
static void
@ -392,10 +372,9 @@ xev_handle_randr(XEvent *ee)
i = XRRRootToScreen(X_Dpy, rev->root);
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->which == (u_int)i) {
if (sc->which == i) {
XRRUpdateConfiguration(ee);
screen_update_geometry(sc, rev->width, rev->height);
screen_init_xinerama(sc);
screen_update_geometry(sc);
}
}
}
@ -408,15 +387,13 @@ static void
xev_handle_mappingnotify(XEvent *ee)
{
XMappingEvent *e = &ee->xmapping;
struct keybinding *kb;
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_ungrab(&Conf, kb);
struct screen_ctx *sc;
XRefreshKeyboardMapping(e);
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_grab(&Conf, kb);
if (e->request == MappingKeyboard) {
TAILQ_FOREACH(sc, &Screenq, entry)
conf_grab_kbd(sc->rootwin);
}
}
static void
@ -429,19 +406,14 @@ xev_handle_expose(XEvent *ee)
client_draw_border(cc);
}
volatile sig_atomic_t _xev_quit = 0;
void
xev_loop(void)
xev_process(void)
{
XEvent e;
while (_xev_quit == 0) {
XNextEvent(X_Dpy, &e);
if (e.type - Randr_ev == RRScreenChangeNotify)
xev_handle_randr(&e);
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
(*xev_handlers[e.type])(&e);
}
XNextEvent(X_Dpy, &e);
if (e.type - Randr_ev == RRScreenChangeNotify)
xev_handle_randr(&e);
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
(*xev_handlers[e.type])(&e);
}

View File

@ -15,17 +15,20 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
@ -35,10 +38,12 @@ xmalloc(size_t siz)
{
void *p;
if (siz == 0)
errx(1, "xmalloc: zero size");
if ((p = malloc(siz)) == NULL)
err(1, "malloc");
return (p);
return(p);
}
void *
@ -46,16 +51,14 @@ xcalloc(size_t no, size_t siz)
{
void *p;
if (siz == 0 || no == 0)
errx(1, "xcalloc: zero size");
if (SIZE_MAX / no < siz)
errx(1, "xcalloc: no * siz > SIZE_MAX");
if ((p = calloc(no, siz)) == NULL)
err(1, "calloc");
return (p);
}
void
xfree(void *p)
{
free(p);
return(p);
}
char *
@ -66,5 +69,21 @@ xstrdup(const char *str)
if ((p = strdup(str)) == NULL)
err(1, "strdup");
return (p);
return(p);
}
int
xasprintf(char **ret, const char *fmt, ...)
{
va_list ap;
int i;
va_start(ap, fmt);
i = vasprintf(ret, fmt, ap);
va_end(ap);
if (i < 0 || *ret == NULL)
err(1, "asprintf");
return(i);
}

579
xutil.c
View File

@ -15,35 +15,75 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id$
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask };
int
xu_ptr_grab(Window win, int mask, Cursor curs)
void
xu_btn_grab(Window win, int mask, unsigned int btn)
{
return (XGrabPointer(X_Dpy, win, False, mask,
unsigned int i;
for (i = 0; i < nitems(ign_mods); i++)
XGrabButton(X_Dpy, btn, (mask | ign_mods[i]), win,
False, BUTTONMASK, GrabModeAsync,
GrabModeSync, None, None);
}
void
xu_btn_ungrab(Window win)
{
XUngrabButton(X_Dpy, AnyButton, AnyModifier, win);
}
void
xu_key_grab(Window win, unsigned int mask, KeySym keysym)
{
KeyCode code;
unsigned int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XkbKeycodeToKeysym(X_Dpy, code, 0, 0) != keysym) &&
(XkbKeycodeToKeysym(X_Dpy, code, 0, 1) == keysym))
mask |= ShiftMask;
for (i = 0; i < nitems(ign_mods); i++)
XGrabKey(X_Dpy, code, (mask | ign_mods[i]), win,
True, GrabModeAsync, GrabModeAsync);
}
void
xu_key_ungrab(Window win)
{
XUngrabKey(X_Dpy, AnyKey, AnyModifier, win);
}
int
xu_ptr_grab(Window win, unsigned int mask, Cursor curs)
{
return(XGrabPointer(X_Dpy, win, False, mask,
GrabModeAsync, GrabModeAsync,
None, curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
int
xu_ptr_regrab(int mask, Cursor curs)
xu_ptr_regrab(unsigned int mask, Cursor curs)
{
return (XChangeActivePointerGrab(X_Dpy, mask,
return(XChangeActivePointerGrab(X_Dpy, mask,
curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
@ -54,31 +94,13 @@ xu_ptr_ungrab(void)
}
void
xu_btn_grab(Window win, int mask, u_int btn)
xu_ptr_getpos(Window win, int *x, int *y)
{
int i;
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XGrabButton(X_Dpy, btn, (mask | ign_mods[i]), win,
False, ButtonMask, GrabModeAsync,
GrabModeSync, None, None);
}
Window w0, w1;
int tmp0, tmp1;
unsigned int tmp2;
void
xu_btn_ungrab(Window win, int mask, u_int btn)
{
int i;
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XUngrabButton(X_Dpy, btn, (mask | ign_mods[i]), win);
}
void
xu_ptr_getpos(Window rootwin, int *x, int *y)
{
Window w0, w1;
int tmp0, tmp1;
u_int tmp2;
XQueryPointer(X_Dpy, rootwin, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
}
void
@ -87,68 +109,21 @@ xu_ptr_setpos(Window win, int x, int y)
XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
}
void
xu_key_grab(Window win, int mask, int keysym)
{
KeyCode code;
int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XKeycodeToKeysym(X_Dpy, code, 0) != keysym) &&
(XKeycodeToKeysym(X_Dpy, code, 1) == keysym))
mask |= ShiftMask;
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XGrabKey(X_Dpy, code, (mask | ign_mods[i]), win,
True, GrabModeAsync, GrabModeAsync);
}
void
xu_key_ungrab(Window win, int mask, int keysym)
{
KeyCode code;
int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XKeycodeToKeysym(X_Dpy, code, 0) != keysym) &&
(XKeycodeToKeysym(X_Dpy, code, 1) == keysym))
mask |= ShiftMask;
for (i = 0; i < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XUngrabKey(X_Dpy, code, (mask | ign_mods[i]), win);
}
void
xu_sendmsg(Window win, Atom atm, long val)
{
XEvent e;
memset(&e, 0, sizeof(e));
e.xclient.type = ClientMessage;
e.xclient.window = win;
e.xclient.message_type = atm;
e.xclient.format = 32;
e.xclient.data.l[0] = val;
e.xclient.data.l[1] = CurrentTime;
XSendEvent(X_Dpy, win, False, 0, &e);
}
int
xu_getprop(Window win, Atom atm, Atom type, long len, u_char **p)
xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
{
Atom realtype;
u_long n, extra;
int format;
Atom realtype;
unsigned long n, extra;
int format;
if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type,
&realtype, &format, &n, &extra, p) != Success || *p == NULL)
return (-1);
return(-1);
if (n == 0)
XFree(*p);
return (n);
return(n);
}
int
@ -161,7 +136,7 @@ xu_getstrprop(Window win, Atom atm, char **text) {
XGetTextProperty(X_Dpy, win, &prop, atm);
if (!prop.nitems)
return (0);
return(0);
if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
&nitems) == Success && nitems > 0 && *list) {
@ -169,7 +144,7 @@ xu_getstrprop(Window win, Atom atm, char **text) {
XTextProperty prop2;
if (Xutf8TextListToTextProperty(X_Dpy, list, nitems,
XUTF8StringStyle, &prop2) == Success) {
*text = xstrdup(prop2.value);
*text = xstrdup((const char *)prop2.value);
XFree(prop2.value);
}
} else {
@ -180,107 +155,351 @@ xu_getstrprop(Window win, Atom atm, char **text) {
XFree(prop.value);
return (nitems);
return(nitems);
}
/* Root Window Properties */
void
xu_ewmh_net_supported(struct screen_ctx *sc)
{
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED],
XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS);
}
void
xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
{
Window w;
w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0);
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME],
cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)WMNAME,
strlen(WMNAME));
}
void
xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
{
long geom[2] = { sc->view.w, sc->view.h };
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
}
void
xu_ewmh_net_workarea(struct screen_ctx *sc)
{
long workareas[CALMWM_NGROUPS][4];
int i;
for (i = 0; i < CALMWM_NGROUPS; i++) {
workareas[i][0] = sc->work.x;
workareas[i][1] = sc->work.y;
workareas[i][2] = sc->work.w;
workareas[i][3] = sc->work.h;
}
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workareas,
CALMWM_NGROUPS * 4);
}
void
xu_ewmh_net_client_list(struct screen_ctx *sc)
{
struct client_ctx *cc;
Window *winlist;
int i = 0, j = 0;
TAILQ_FOREACH(cc, &sc->clientq, entry)
i++;
if (i == 0)
return;
winlist = xcalloc(i, sizeof(*winlist));
TAILQ_FOREACH(cc, &sc->clientq, entry)
winlist[j++] = cc->win;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
free(winlist);
}
void
xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
{
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
}
void
xu_ewmh_net_wm_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
xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *sc)
{
long ndesks = CALMWM_NGROUPS;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
}
void
xu_ewmh_net_showing_desktop(struct screen_ctx *sc)
{
long zero = 0;
/* We don't support `showing desktop' mode, so this is zero.
* Note that when we hide all groups, or when all groups are
* hidden we could technically set this later on.
*/
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
}
void
xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
{
/* We don't support virtual roots, so delete if set by previous wm. */
XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]);
}
void
xu_ewmh_net_current_desktop(struct screen_ctx *sc)
{
long num = sc->group_active->num;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
}
void
xu_ewmh_net_desktop_names(struct screen_ctx *sc)
{
struct group_ctx *gc;
char *p, *q;
unsigned char *prop_ret;
int i = 0, j = 0, nstrings = 0, n = 0;
size_t len = 0, tlen, slen;
/* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */
if ((j = xu_getprop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
prop_ret[j - 1] = '\0'; /* paranoia */
while (i < j) {
if (prop_ret[i++] == '\0')
nstrings++;
}
}
p = (char *)prop_ret;
while (n < nstrings) {
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == n) {
free(gc->name);
gc->name = xstrdup(p);
p += strlen(p) + 1;
break;
}
}
n++;
}
if (prop_ret != NULL)
XFree(prop_ret);
TAILQ_FOREACH(gc, &sc->groupq, entry)
len += strlen(gc->name) + 1;
q = p = xcalloc(len, sizeof(*p));
tlen = len;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
slen = strlen(gc->name) + 1;
(void)strlcpy(q, gc->name, tlen);
tlen -= slen;
q += slen;
}
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len);
}
/* Application Window Properties */
void
xu_ewmh_net_wm_desktop(struct client_ctx *cc)
{
long num = 0xffffffff;
if (cc->group)
num = cc->group->num;
XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
}
Atom *
xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
{
Atom *state, *p = NULL;
if ((*n = xu_getprop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
(unsigned char **)&p)) <= 0)
return(NULL);
state = xcalloc(*n, sizeof(Atom));
(void)memcpy(state, p, *n * sizeof(Atom));
XFree((char *)p);
return(state);
}
void
xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
Atom first, Atom second)
{
unsigned int i;
static struct handlers {
int atom;
int property;
void (*toggle)(struct client_ctx *);
} handlers[] = {
{ _NET_WM_STATE_STICKY,
CLIENT_STICKY,
client_toggle_sticky },
{ _NET_WM_STATE_MAXIMIZED_VERT,
CLIENT_VMAXIMIZED,
client_toggle_vmaximize },
{ _NET_WM_STATE_MAXIMIZED_HORZ,
CLIENT_HMAXIMIZED,
client_toggle_hmaximize },
{ _NET_WM_STATE_HIDDEN,
CLIENT_HIDDEN,
client_toggle_hidden },
{ _NET_WM_STATE_FULLSCREEN,
CLIENT_FULLSCREEN,
client_toggle_fullscreen },
{ _NET_WM_STATE_DEMANDS_ATTENTION,
CLIENT_URGENCY,
client_urgency },
};
for (i = 0; i < nitems(handlers); i++) {
if (first != ewmh[handlers[i].atom] &&
second != ewmh[handlers[i].atom])
continue;
switch (action) {
case _NET_WM_STATE_ADD:
if (!(cc->flags & handlers[i].property))
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_REMOVE:
if (cc->flags & handlers[i].property)
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_TOGGLE:
handlers[i].toggle(cc);
}
}
}
void
xu_ewmh_restore_net_wm_state(struct client_ctx *cc)
{
Atom *atoms;
int i, n;
atoms = xu_ewmh_get_net_wm_state(cc, &n);
for (i = 0; i < n; i++) {
if (atoms[i] == ewmh[_NET_WM_STATE_STICKY])
client_toggle_sticky(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ])
client_toggle_hmaximize(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT])
client_toggle_vmaximize(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN])
client_toggle_hidden(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN])
client_toggle_fullscreen(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION])
client_urgency(cc);
}
free(atoms);
}
void
xu_ewmh_set_net_wm_state(struct client_ctx *cc)
{
Atom *atoms, *oatoms;
int n, i, j;
oatoms = xu_ewmh_get_net_wm_state(cc, &n);
atoms = xcalloc((n + _NET_WM_STATES_NITEMS), sizeof(Atom));
for (i = j = 0; i < n; i++) {
if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] &&
oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] &&
oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] &&
oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] &&
oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] &&
oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION])
atoms[j++] = oatoms[i];
}
free(oatoms);
if (cc->flags & CLIENT_STICKY)
atoms[j++] = ewmh[_NET_WM_STATE_STICKY];
if (cc->flags & CLIENT_HIDDEN)
atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN];
if (cc->flags & CLIENT_FULLSCREEN)
atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN];
else {
if (cc->flags & CLIENT_HMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ];
if (cc->flags & CLIENT_VMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT];
}
if (cc->flags & CLIENT_URGENCY)
atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION];
if (j > 0)
XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE],
XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j);
else
XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
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;
}
int
xu_getstate(struct client_ctx *cc, int *state)
xu_xft_width(XftFont *xftfont, const char *text, int len)
{
long *p = NULL;
XGlyphInfo extents;
if (xu_getprop(cc->win, WM_STATE, WM_STATE, 2L, (u_char **)&p) <= 0)
return (-1);
XftTextExtentsUtf8(X_Dpy, xftfont, (const FcChar8*)text,
len, &extents);
*state = (int)*p;
XFree((char *)p);
return (0);
return(extents.xOff);
}
void
xu_setstate(struct client_ctx *cc, int state)
xu_xft_draw(struct screen_ctx *sc, const char *text, int color, int x, int y)
{
long dat[2];
dat[0] = state;
dat[1] = None;
cc->state = state;
XChangeProperty(X_Dpy, cc->win, WM_STATE, WM_STATE, 32,
PropModeReplace, (unsigned char *)dat, 2);
}
Atom cwm_atoms[CWM_NO_ATOMS];
char *atoms[CWM_NO_ATOMS] = {
"WM_STATE",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
"WM_PROTOCOLS",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"_NET_SUPPORTED",
"_NET_SUPPORTING_WM_CHECK",
"_NET_WM_NAME",
"_NET_ACTIVE_WINDOW",
"_NET_CLIENT_LIST",
"_NET_NUMBER_OF_DESKTOPS",
"_NET_CURRENT_DESKTOP",
"_NET_DESKTOP_VIEWPORT",
"_NET_DESKTOP_GEOMETRY",
"_NET_VIRTUAL_ROOTS",
"_NET_SHOWING_DESKTOP",
"_NET_DESKTOP_NAMES",
"_NET_WM_DESKTOP",
"_NET_WORKAREA",
};
void
xu_getatoms(void)
{
XInternAtoms(X_Dpy, atoms, CWM_NO_ATOMS, False, cwm_atoms);
}
void
xu_setwmname(struct screen_ctx *sc)
{
/*
* set up the _NET_SUPPORTED hint with all netwm atoms that we
* know about.
*/
XChangeProperty(X_Dpy, sc->rootwin, _NET_SUPPORTED, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&_NET_SUPPORTED,
CWM_NO_ATOMS - CWM_NETWM_START);
/*
* netwm spec says that to prove that the hint is not stale you must
* provide _NET_SUPPORTING_WM_CHECK containing a window (we use the
* menu window). The property must be set on the root window and the
* window itself, the window also must have _NET_WM_NAME set with the
* window manager name.
*/
XChangeProperty(X_Dpy, sc->rootwin, _NET_SUPPORTING_WM_CHECK,
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&sc->menuwin, 1);
XChangeProperty(X_Dpy, sc->menuwin, _NET_SUPPORTING_WM_CHECK,
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&sc->menuwin, 1);
XChangeProperty(X_Dpy, sc->menuwin, _NET_WM_NAME, UTF8_STRING,
8, PropModeReplace, WMNAME, strlen(WMNAME));
}
unsigned long
xu_getcolor(struct screen_ctx *sc, char *name)
{
XColor color, tmp;
if (!XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, sc->which),
name, &color, &tmp)) {
warnx("XAllocNamedColor error: '%s'", name);
return 0;
}
return color.pixel;
}
void
xu_freecolor(struct screen_ctx *sc, unsigned long pixel)
{
XFreeColors(X_Dpy, DefaultColormap(X_Dpy, sc->which), &pixel, 1, 0L);
XftDrawStringUtf8(sc->xftdraw, &sc->xftcolor[color], sc->xftfont,
x, y, (const FcChar8*)text, strlen(text));
}