773 Commits

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

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

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

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

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

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

add underscore in PKG_CONFIG

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

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

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

Original commit message:

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

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

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

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

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

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

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

Window offset noticed by at least Andre Stoebe via bugs@, and others since
(and likely before). Thanks!
2018-11-08 15:49:42 +00:00
b4d4eba6af cvsimport
* refs/heads/master:
  Remove unused prototype; from Ross L Richardson.
2018-07-16 14:19:23 +00:00
9999c3e6e0 Remove unused prototype; from Ross L Richardson. 2018-07-16 14:19:23 +00:00
a9dbac8209 README: update 2018-05-14 15:31:34 +02:00
fae50566fc Makefile: drop unused fontconfig dependency 2018-05-14 14:47:24 +02:00
0551094182 Do not print any parse errors when ~/.cwmrc is missing. Regression introduced in
revision 1.109 of calmwm.c.

ok okan@
2018-02-19 19:29:42 +00:00
cae6987922 cvsimport
* refs/heads/master:
  Do not print any parse errors when ~/.cwmrc is missing. Regression introduced in revision 1.109 of calmwm.c.
  Store the screen's visual type and colormap.
  Consolidate region 'view' and 'area'.
  limit scope of screen_apply_gap()
  Clean up conf_file/homedir and conf_init() bits.
2018-02-19 19:29:42 +00:00
b1929b5ed6 Store the screen's visual type and colormap. 2018-02-13 15:43:15 +00:00
13763662c9 Consolidate region 'view' and 'area'. 2018-02-13 15:06:22 +00:00
e6bf7429b3 limit scope of screen_apply_gap() 2018-02-09 20:08:07 +00:00
1a5f80bd0b Clean up conf_file/homedir and conf_init() bits. 2018-02-09 19:54:54 +00:00
c6745ee21c calmwm: replace INFTIM with -1 2018-02-09 20:11:41 +01:00
f3211427c1 Use screen's saved view instead of re-querying the server. 2018-02-06 15:05:20 +00:00
63ebc0cd8b cvsimport
* refs/heads/master: (28 commits)
  Use screen's saved view instead of re-querying the server.
  Slightly expand and expose verbose debugging.
  add debugging for x events
  Add a simple debug logging mechanism.
  Simplification; use asprintf where appropriate now.
  Use func attributes where appropriate.
  Fix wins comparison declaration since it's unsigned from XQueryTree().
  Generate name_to_func[] in a clean and readable fashion.
  Shrink tier[] by one after removing matchname in r1.55.
  If the requested group number is invalid, bail but don't kill cwm.
  Quick fix: exit after a failed execvp in u_spawn instead; previously we did in u_exec, but the introduction of re-exec'ing the previous invocation of cwm if 'exec_wm' failed missed the 'exec' failing path. Will likely split out as a proper fix.
  Only exec the fallback when in CWM_EXEC_WM state.
  Typo, from Julien Steinhauser.
  Convert menu-exec-wm from an abritrary exec menu, into a config-based menu from which one may configure (wm <name> <path_and_args>) (and choose) specific window managers to replace the running one. 'wm cwm cwm' is included by default.
  As done for buttonrelease, work specific un-cycling and un-highlighting actions into the keyrelease event, only performing what's actually needed for each; should result in much fewer events against keyreleases. No intended behaviour change.
  Merge group_toggle_membership_leave into the buttonrelease event and only do border work for a group/ungroup action.
  add helper function client_show to bring together like actions for unhide/raise
  Add support for re-exec'ing with SIGHUP; equivalent to the already built-in 'restart' function.
  Use poll and XNextEvent to replace XNextEvent blocking inside the x11 event handler.
  zap stray that snuck in
  ...
2018-02-06 15:05:20 +00:00
14c17b5f9b Slightly expand and expose verbose debugging. 2018-02-04 22:56:26 +00:00
8623c7add7 add debugging for x events 2018-02-02 13:50:22 +00:00
34e15dbd7a Add a simple debug logging mechanism. 2018-02-02 13:40:55 +00:00
9bf750b054 Simplification; use asprintf where appropriate now. 2018-02-02 13:27:25 +00:00
174537f29e Use func attributes where appropriate. 2018-02-01 15:17:51 +00:00
f34e659ca7 Fix wins comparison declaration since it's unsigned from XQueryTree(). 2018-01-23 16:18:59 +00:00
03a2e9cf05 Generate name_to_func[] in a clean and readable fashion. 2018-01-23 16:00:21 +00:00
5324f9a4e3 Shrink tier[] by one after removing matchname in r1.55. 2018-01-23 13:51:39 +00:00
8675b5e158 If the requested group number is invalid, bail but don't kill cwm. 2018-01-23 13:48:49 +00:00
e99f1d4683 Quick fix: exit after a failed execvp in u_spawn instead; previously we did in
u_exec, but the introduction of re-exec'ing the previous invocation of cwm if
'exec_wm' failed missed the 'exec' failing path. Will likely split out as a
proper fix.

Odd behaviour reported by Ve Telko.
2018-01-08 16:21:54 +00:00
50c0a4eef6 Only exec the fallback when in CWM_EXEC_WM state.
Broken quit noticed by Ve Telko.
2018-01-02 14:04:58 +00:00
5672d5d203 Typo, from Julien Steinhauser. 2017-12-30 22:25:09 +00:00
15ca9c03ba If the replacement window manager fails to start, restart the fallback (the
original invocation of cwm).
2017-12-29 20:09:19 +00:00
6e7dbf5bb7 Convert menu-exec-wm from an abritrary exec menu, into a config-based menu from
which one may configure (wm <name> <path_and_args>) (and choose) specific
window managers to replace the running one. 'wm cwm cwm' is included by
default.

No objections and seems sensible to sthen.
2017-12-29 20:03:46 +00:00
43db5b55ea As done for buttonrelease, work specific un-cycling and un-highlighting actions
into the keyrelease event, only performing what's actually needed for each;
should result in much fewer events against keyreleases. No intended behaviour
change.

Additionally, like we do for group membership, grab the keyboard only when
required for cycling.
2017-12-29 18:50:43 +00:00
ba75c13953 Merge group_toggle_membership_leave into the buttonrelease event and only do
border work for a group/ungroup action.
2017-12-29 16:55:50 +00:00
5ddaed415c add helper function client_show to bring together like actions for unhide/raise 2017-12-29 12:54:54 +00:00
156681f0a5 Add support for re-exec'ing with SIGHUP; equivalent to the already built-in
'restart' function.
2017-12-27 18:46:18 +00:00
3d7c82936e Use poll and XNextEvent to replace XNextEvent blocking inside the x11 event
handler.
2017-12-27 17:04:35 +00:00
03e5d86952 zap stray that snuck in 2017-12-22 21:30:01 +00:00
dfaf44c0ac Return the connection number for the display. 2017-12-22 21:27:45 +00:00
c5d03b0853 Fix a few comments and while here, wrap some long lines. 2017-12-22 21:21:44 +00:00
18a53717ae Use a variable to keep track of flags for menu_filter(). 2017-12-19 19:38:43 +00:00
bc5215f41a Add support for _NET_WM_STATE_SKIP_PAGER and _NET_WM_STATE_SKIP_TASKBAR; eerily
close to cwm's 'ignore'.

Roughly based on an initial diff from Walter Alejandro Iglesias, but with
support for both Atoms and without cwm-based bindings.
2017-12-19 14:30:53 +00:00
5e5d06f063 remove duplicate ExposureMask 2017-12-15 13:13:17 +00:00
64ff5f399a move variable declarations up, to match everything else 2017-12-13 15:10:17 +00:00
add109c006 clean up after previous (noprompt removal) 2017-12-12 15:37:16 +00:00
888f6c5319 Unconditionally show prompt on menus, regardless of invocation. 2017-12-11 20:58:18 +00:00
a0774f4777 cvsimport
* refs/heads/master:
  Original idea from Dimitris Papastamos to move windows to corners a while ago; re-proposed by Julien Steinhauser with an updated diff. Apparently this was in the original calmnwm.
  give command and group menus their own match callbacks
  stash dir into conf since it'll be of use
  organize this a bit better
  spacing
2017-12-07 16:25:33 +00:00
0fc9d47fb4 Original idea from Dimitris Papastamos to move windows to corners a while ago;
re-proposed by Julien Steinhauser with an updated diff. Apparently this was in
the original calmnwm.

However, expand the original idea and let clients 'snap' to edges instead,
neatly allowing key bindings that snap to adjacent edges (i.e. corners) as
well. No default bindings assigned.
2017-12-07 16:25:33 +00:00
f0524fe07a give command and group menus their own match callbacks 2017-12-07 16:03:10 +00:00
b06ddae624 stash dir into conf since it'll be of use 2017-12-07 15:47:14 +00:00
0d76265e23 organize this a bit better 2017-12-07 15:40:54 +00:00
592e8efaee spacing 2017-12-07 15:39:47 +00:00
dd5f951205 cvsimport
* refs/heads/master:
  Revert r1.109 (Switch to XWindowEvent() pulling out events that match the mask *and* window.) of mousefunc.c. When a client destroys itself while we are moving or resizing it, XWindowEvent() blocks. Found the hard way by Anton Lazarov, and Lea°hNeukirchen found the right bit to revert - thanks! Reverting since the reason to switch from XMaskEvent was unclear.
2017-11-30 18:18:51 +00:00
d9d6b4f88f Revert r1.109 (Switch to XWindowEvent() pulling out events that match the mask
*and* window.) of mousefunc.c. When a client destroys itself while we are
moving or resizing it, XWindowEvent() blocks. Found the hard way by Anton
Lazarov, and Lea°hNeukirchen found the right bit to revert - thanks! Reverting
since the reason to switch from XMaskEvent was unclear.
2017-11-30 18:18:51 +00:00
c36c5063e8 README: update 2017-10-17 19:01:20 +02:00
7d7e256396 Case matters for menu matching on executables; from ben@lloyd.im. 2017-09-06 14:15:13 +00:00
087b803b45 cvsimport
* refs/heads/master:
  Case matters for menu matching on executables; from ben@lloyd.im.
2017-09-06 14:15:13 +00:00
5a46349ec7 cvsimport
* refs/heads/master:
  Use the key names from keysymdef.h in the default key bindings list for clarification.
  remove extra parentheses
  Pull over the remaining re-implemented window move/resize functions and create a wrapper so that the key and mouse based move/resize callbacks can be unified. This has already been done with other window operations and menus.
  rename one function, matching others, to help upcoming change
2017-07-27 15:07:12 +02:00
8dee1182a2 Use the key names from keysymdef.h in the default key bindings list for
clarification.

from Michael Reed.
2017-07-21 15:55:37 +00:00
60ed85c50f remove extra parentheses 2017-07-14 18:01:46 +00:00
c039e7fb8c Pull over the remaining re-implemented window move/resize functions and create
a wrapper so that the key and mouse based move/resize callbacks can be unified.
This has already been done with other window operations and menus.
2017-07-14 17:23:38 +00:00
aefc0bf0fb rename one function, matching others, to help upcoming change 2017-07-12 16:56:26 +00:00
8908e5ec42 drop fgetln.c 2017-07-11 13:17:45 +02:00
1ceb2dd795 cvsimport
* refs/heads/master:
  Replace fgetln(3) with POSIX getline(3); inspired by brynet and Ingo.
  fix a few WARNINGS (new sentence, new line and extraneous Pp macro) found with mandoc -Tlint
  from src, by espie@: no need to generate y.tab.h if nothing uses it, set YFLAGS to nothing
2017-07-11 13:15:08 +02:00
d179dfdeb9 Replace fgetln(3) with POSIX getline(3); inspired by brynet and Ingo.
feedback and ok brynet@
2017-07-10 13:36:58 +00:00
f94fde51fb fix a few WARNINGS (new sentence, new line and extraneous Pp macro) found with
mandoc -Tlint
2017-07-06 17:09:17 +00:00
eb79ce65c9 from src, by espie@:
no need to generate y.tab.h if nothing uses it, set YFLAGS to nothing
2017-07-06 17:01:10 +00:00
4040f112e1 cvsimport
* refs/heads/master:
  drop obsolete comment
  Alter callbacks to take a struct instead of a growing number of arguments; greatly simplifies upcoming work.
  Ensure clients stay within the viewable bounds on placement, even with empty borders; based on a patch from Vadim Vygonets.
  Clean up, unify and accurately calculate edge distance with client move/resize actions, so as to not lose windows off the edge.
  Switch bwidth type; unfortunately X11 is inconsistent.
2017-05-09 18:43:40 +00:00
16a2ccc225 drop obsolete comment 2017-05-09 18:43:40 +00:00
113ffcf47e Alter callbacks to take a struct instead of a growing number of arguments;
greatly simplifies upcoming work.
2017-05-09 13:40:18 +00:00
189a735eaa Ensure clients stay within the viewable bounds on placement, even with empty
borders; based on a patch from Vadim Vygonets.
2017-05-05 14:14:19 +00:00
3d6ff6dabf Clean up, unify and accurately calculate edge distance with client move/resize
actions, so as to not lose windows off the edge.

inspired by diffs (and feedback) from Vadim Vygonets.
2017-05-01 12:54:55 +00:00
4d85e2e2a6 Switch bwidth type; unfortunately X11 is inconsistent. 2017-04-26 21:10:54 +00:00
a8527b5661 calmwm: sighdlr: use -1 instead of WAIT_ANY 2017-04-25 22:26:57 +02:00
15b9a8fe27 The r1.36 menuq_add() conversion did this one wrong; fix by reverting to the
original code which adds to resultentry list, as opposed to the wrong one in
menuq_add(). Fixes crash noticed by at least Rickard Gustafsson.
2017-04-25 13:40:33 +00:00
2d9185129b cvsimport
* refs/heads/master:
  The r1.36 menuq_add() conversion did this one wrong; fix by reverting to the original code which adds to resultentry list, as opposed to the wrong one in menuq_add(). Fixes crash noticed by at least Rickard Gustafsson.
  Remove 'noresult' (and simplify) as it does nothing and makes the logic around it needless. History doesn't show what this might have been used for in future iterations.
  search_match_path() isn't supposed to return anything.
  Move queue init to caller so it's only called once in a few cases.
  sort
  Raise the previously focused window, instead of the previous window in the cycle list; seems to match behaviour of other wm's; from Walter Alejandro Iglesias.
  For {h,v}tile, instead of keeping the master client's {h,v} geometry, expand it to %50 of the area, then fill in the remaining space with the other clients in the same group; from Gerrit Meyerheim.
2017-04-25 13:40:33 +00:00
717e6c8167 Remove 'noresult' (and simplify) as it does nothing and makes the logic around
it needless. History doesn't show what this might have been used for in future
iterations.
2017-04-25 12:08:05 +00:00
ffcfff3dea search_match_path() isn't supposed to return anything. 2017-04-24 15:06:11 +00:00
56b44257a5 Move queue init to caller so it's only called once in a few cases. 2017-04-24 13:35:25 +00:00
15a6fcc851 sort 2017-04-24 13:31:19 +00:00
9b56325908 Raise the previously focused window, instead of the previous window in the
cycle list; seems to match behaviour of other wm's; from Walter Alejandro
Iglesias.
2017-04-24 12:27:32 +00:00
50dd70afc5 For {h,v}tile, instead of keeping the master client's {h,v} geometry, expand it
to %50 of the area, then fill in the remaining space with the other clients in
the same group; from Gerrit Meyerheim.

support from ajacoutot.
2017-04-24 12:18:04 +00:00
ab8da61802 cvsimport
* refs/heads/master:
  Fix group hidden state when a client wants to be on all desktops on start; reported by Vadim Vygonets with diff, but another approach was taken.
  Call client_resize instead of client_move after client_placecalc since the size may also change.
2017-02-10 15:00:54 +00:00
8d9d84d859 Fix group hidden state when a client wants to be on all desktops on start;
reported by Vadim Vygonets with diff, but another approach was taken.
2017-02-10 15:00:54 +00:00
172da14d77 Call client_resize instead of client_move after client_placecalc since the size
may also change.

from Vadim Vygonets
2017-02-06 18:10:28 +00:00
48b465bcff cvsimport
* refs/heads/master:
  Typo on binding name; from Redouan Ait Mallouk.
2017-01-15 21:07:44 +00:00
7ce071fdaa Leahize 2017-01-23 21:21:58 +01:00
f908118a3a Typo on binding name; from Redouan Ait Mallouk. 2017-01-15 21:07:44 +00:00
b6a5b876e7 cvsimport
* refs/heads/master:
  Ensure client stays inbound on key-based resize; based on logic existing in key-based client move; from Vadim Vygonets.
  When a window has a user or program specified position, ensure the edge of the final position is at least viewable and warp'able by the difference of bwidth; prevents mapping windows completely off the virtual screen.
2017-01-05 21:18:20 +00:00
96918a06e6 Ensure client stays inbound on key-based resize; based on logic existing in
key-based client move; from Vadim Vygonets.
2017-01-05 21:18:20 +00:00
613d11434a When a window has a user or program specified position, ensure the edge of the
final position is at least viewable and warp'able by the difference of bwidth;
prevents mapping windows completely off the virtual screen.
2016-12-19 14:17:26 +00:00
44216b65c9 add config migration script 2016-12-16 14:31:33 +01:00
840dc39292 fix build 2016-12-16 14:29:50 +01:00
775ba78311 cvsimport
* refs/heads/master:
  stray newlines
  Add search_print_text(), a default callback for mi->print in menu_filter(). While here, normalize the remaining search_print_* argument paramters.
  Consistent use of menuq_add for ssh menu.
  Now that dim.{x,y} are available early, use them before requiring a MotionNotify event.
  Set dim.{x,y} during client_init and update on resize, instead of (re)calculating only when applying hints.
  'window-search' is spelled 'menu-window'; the former snuck in during the conversion('menu-window' already existed and was properlly documented); found the hard way by sthen@ while trying to convert.
  Fold unbinding functions into one for each, key and mouse; plugs a leak when unbinding a mouse button bound to a command.
  use the correct type
  Tame the number of 'exec' and 'path' search_match wrappers. No functional change now, though more can likely go later, losing the (paritally complete or incomplete/broken) argument completion bits.
  Switch ssh menu to search_match_text; like group/window/cmd menus, use only a substring match. The previous matching is only intended for the exec menus.
  Change 'menu-window' to display all windows; then add 'menu-window-hidden' for the previous behaviour of 'menu-window'.  'menu-window' becomes the default binding; use 'bind-mouse "1" menu-window-hidden' to restore old behaviour for those who prefer.
  Normalize bind function names, based on a few categories: window, group, menu and pointer.
  Use an additional check with lstat(2) when d_type is unknown.
  revert previous; upcoming changes will hopefully deal with these more naturally.
  Add a wrapper based upon xevent handlers around client move/resize for key and mouse bindings.
  Define callbacks, then default bindings.
  Reorganize for upcoming changes.
  Remove the (8) default bindings for pointer move since they conflict with default bindings for emacs, which wins; the feature remains and can be bound to whatever users wish with cwmrc(5).
2016-12-16 13:48:38 +01:00
8b4666cb92 stray newlines 2016-12-06 21:59:33 +00:00
a7f2ab7497 Add search_print_text(), a default callback for mi->print in menu_filter(). While
here, normalize the remaining search_print_* argument paramters.
2016-12-06 21:54:10 +00:00
89e4e7fb14 Consistent use of menuq_add for ssh menu. 2016-12-06 21:09:22 +00:00
2ae4797297 Now that dim.{x,y} are available early, use them before requiring a
MotionNotify event.
2016-12-06 21:03:58 +00:00
74092f78aa Set dim.{x,y} during client_init and update on resize, instead of
(re)calculating only when applying hints.
2016-12-06 21:00:13 +00:00
cf4c1879e5 'window-search' is spelled 'menu-window'; the former snuck in during the
conversion('menu-window' already existed and was properlly documented); found
the hard way by sthen@ while trying to convert.
2016-12-06 15:03:13 +00:00
cfffa70364 Fold unbinding functions into one for each, key and mouse; plugs a leak when
unbinding a mouse button bound to a command.
2016-12-02 17:02:17 +00:00
5b7e66a13b use the correct type 2016-12-02 16:50:19 +00:00
94f681fc90 Tame the number of 'exec' and 'path' search_match wrappers. No functional
change now, though more can likely go later, losing the (paritally complete or
incomplete/broken) argument completion bits.
2016-12-01 20:28:19 +00:00
b94f0748d8 Switch ssh menu to search_match_text; like group/window/cmd menus, use only a
substring match. The previous matching is only intended for the exec menus.
2016-12-01 18:44:09 +00:00
92111e526d Change 'menu-window' to display all windows; then add 'menu-window-hidden' for
the previous behaviour of 'menu-window'.  'menu-window' becomes the default
binding; use 'bind-mouse "1" menu-window-hidden' to restore old behaviour for
those who prefer.

OK sthen@ (long long time ago on a different version)
2016-12-01 18:17:52 +00:00
d968a6c1bf Normalize bind function names, based on a few categories: window, group, menu
and pointer.

Replace 'bind' and 'mousebind' options with 'bind-key' and 'bind-mouse',
respectively, replace special 'unmap' keyword with 'unbind-key' and
'unbind-mouse', and additionally allow unbinding all with 'all' keyword.
2016-12-01 17:17:27 +00:00
ae9f900b91 Use an additional check with lstat(2) when d_type is unknown.
from James McDonald via portable.
2016-11-15 18:43:09 +00:00
db02592e5c revert previous; upcoming changes will hopefully deal with these more
naturally.
2016-11-15 00:22:02 +00:00
62dc5ae132 Add a wrapper based upon xevent handlers around client move/resize for key and
mouse bindings.
2016-11-15 00:07:03 +00:00
ed22d7a944 Define callbacks, then default bindings. 2016-11-14 21:25:59 +00:00
64e133b289 Reorganize for upcoming changes. 2016-11-14 19:56:05 +00:00
98c913cc3b Remove the (8) default bindings for pointer move since they conflict with
default bindings for emacs, which wins; the feature remains and can be bound to
whatever users wish with cwmrc(5).

from mpi@
2016-11-14 13:52:33 +00:00
dcb741d2b1 cvsimport
* refs/heads/master: (34 commits)
  Make it clear these are flags.
  Remove duplicate check that strsubmatch() already does; while here, fix a comment.
  Sprinkle __func__ in appropriate error messages.
  Get rid of 'matchname'; it's too surprising to have the menu change during client search as different potential str matches are cycled through. If there's interest, the only string that doesn't exist in the listing is the window's class - that can be added of course, but it makes the line too long imho.
  clean up search_match_client(); no behaviour change
  Refactor callbacks to take a void * so as to not try and generalize into client_ctx in keypress and buttonpress event handlers; pass appropriate *ctx's based on context.
  remove another unused proto
  Rename 2 kbfunc to match closer to what they do
  Add an argument to the callbacks to pass the xevent context, button or key press. This allows to remove a few hacks to duplicate functions only for behaviour changes; now differing behaviours are pushed down to the callback. Also will allow for previously unavailable actions to be bind-able down the road.
  Check the ptr bounds in the new client during cycling, since not all actions do ptrsave, such as restoring client geometry; adapted from a diff by Vadim Vygonets.
  More accurate to say 'toggle', rather than 'select', for group[n]/nogroup.
  Add CM-a for 'nogroup' (CM-0 stays for now); update manpage to reflect.
  Stash wmname into conf.
  When removing xrandr regions, ensure clients are within the bounds of the screen; adapted from an ancient diff from Sviatoslav Chagaev. Things in this area will likely change, but put this in so it works now and serves as a reminder.
  Calculate client nameqlen in client_setname(), the only place it's needed/used.
  Turn CALMWM_NGROUPS define into variable, ngroups.
  Start simplifying menu code; and in turn, remove a cursor no longer needed.
  Defaults are split between defines and conf_init(); normalize these, as well as give 'sticky' groups its own variable.
  For both kb and mouse move, it is possible to grab a client and move it completely off the screen/region; instead, if the pointer is outside of the client bounds, warp the pointer to the closest edge before moving.
  client_ptrwarp should not deal with unhiding or raising clients (non ptr requests); most callers do this already - deal with the few that do not. client_ptrwarp becomes a simple wrapper (setpos) but it will be expanded.
  ...
2016-10-24 20:44:08 +00:00
db93599a0f Make it clear these are flags. 2016-10-24 20:44:08 +00:00
972e28d58c Remove duplicate check that strsubmatch() already does; while here, fix a
comment.
2016-10-24 18:57:12 +00:00
eb43101591 Sprinkle __func__ in appropriate error messages. 2016-10-24 17:39:38 +00:00
ffd60b3cab Get rid of 'matchname'; it's too surprising to have the menu change during
client search as different potential str matches are cycled through. If there's
interest, the only string that doesn't exist in the listing is the window's
class - that can be added of course, but it makes the line too long imho.
2016-10-24 17:16:23 +00:00
b1af1bedd0 clean up search_match_client(); no behaviour change 2016-10-22 19:16:43 +00:00
0bb1be86c6 Refactor callbacks to take a void * so as to not try and generalize into
client_ctx in keypress and buttonpress event handlers; pass appropriate *ctx's
based on context.

While here, limit some globals, replace defines with appropriate variables and
fix some naming.
2016-10-18 17:03:30 +00:00
38eac7d7e9 remove another unused proto 2016-10-12 16:11:15 +00:00
15bf703e1c Rename 2 kbfunc to match closer to what they do 2016-10-06 14:53:52 +00:00
987ee736b9 Add an argument to the callbacks to pass the xevent context, button or
key press. This allows to remove a few hacks to duplicate functions only
for behaviour changes; now differing behaviours are pushed down to the
callback. Also will allow for previously unavailable actions to be bind-able
down the road.
2016-10-06 14:41:19 +00:00
66f5360fd4 Check the ptr bounds in the new client during cycling, since not all
actions do ptrsave, such as restoring client geometry; adapted from a
diff by Vadim Vygonets.
2016-10-06 14:30:05 +00:00
e7dcb17e1e More accurate to say 'toggle', rather than 'select', for group[n]/nogroup. 2016-10-05 14:01:23 +00:00
c131620d7b Add CM-a for 'nogroup' (CM-0 stays for now); update manpage to reflect. 2016-10-05 13:35:17 +00:00
d40820d3f3 Stash wmname into conf. 2016-10-05 13:10:59 +00:00
a37606c63f When removing xrandr regions, ensure clients are within the bounds of
the screen; adapted from an ancient diff from Sviatoslav Chagaev. Things
in this area will likely change, but put this in so it works now and
serves as a reminder.
2016-10-04 20:15:55 +00:00
5c13775d31 Calculate client nameqlen in client_setname(), the only place it's
needed/used.
2016-10-04 15:52:32 +00:00
9306c1fbd3 Turn CALMWM_NGROUPS define into variable, ngroups. 2016-10-04 15:18:20 +00:00
8aa5033d12 Start simplifying menu code; and in turn, remove a cursor no longer
needed.
2016-10-03 18:43:49 +00:00
03f5dc219e Defaults are split between defines and conf_init(); normalize these, as
well as give 'sticky' groups its own variable.
2016-10-03 14:42:34 +00:00
792f85cde9 For both kb and mouse move, it is possible to grab a client and move it
completely off the screen/region; instead, if the pointer is outside of
the client bounds, warp the pointer to the closest edge before moving.
2016-10-03 13:52:17 +00:00
a8a111dffd client_ptrwarp should not deal with unhiding or raising clients (non ptr
requests); most callers do this already - deal with the few that do not.
client_ptrwarp becomes a simple wrapper (setpos) but it will be expanded.
2016-10-03 13:41:30 +00:00
1d3a6905f1 remove unused proto 2016-09-30 21:44:51 +00:00
1c54fc079d Set the initial ptr position during client init, instead of waiting
until (maybe) a ptrwarp call. Likewise, explicitly ensure an inbounds ptr
position (same as initial) when saving.
2016-09-30 20:55:54 +00:00
2d8f621b8d Use instinsic X11 functions for key/btn/ptr grab/ungrab/regrab requests;
the one line wrappers provided no value and limited altering calls where
needed; additionally, most of them had but one caller.
2016-09-30 18:28:06 +00:00
8b26a43bf1 Replace mousefunc_sweep_draw() with a generic menu_windraw() using va
lists; use it appropriately for both window dimension and position in
the respective mousefunc calls.

ok bryent@
2016-09-30 15:12:19 +00:00
f63b2e2341 Switch to XWindowEvent() pulling out events that match the mask *and*
window.
2016-09-30 15:05:02 +00:00
24d6348e52 no need to unmap menu window again 2016-09-29 00:30:40 +00:00
0e8c706633 Mechanical change: move screen menu bits to their own struct. 2016-09-29 00:21:55 +00:00
92b81d3df5 Inline Xft draw and extents wrappers; too much abstraction. 2016-09-28 17:06:33 +00:00
b32989d379 Do not call sweep_draw() too early: don't yet have w/h dimensions; plus
we will get a MotionNotify event right away anyway, setting required
parameters.
2016-09-28 15:54:54 +00:00
57b2a6cf79 Continue merging kb and mouse functions: fold
mousefunc_menu_{client,cmd,group} into the respective
kbfunc_menu_{client,cmd,group} functions; simply pass a flag down from
config denoting mouse action behaviour.
2016-09-22 14:36:03 +00:00
3947f1268a Allow ctrl-[ for abort (esc); from Benjamin Scher Purcell 2016-09-20 19:58:54 +00:00
08631748fd de-static client_inbound() 2016-09-20 19:11:19 +00:00
74e4a3c60b remove debug that accidentally snuck in 2016-09-20 18:23:12 +00:00
9cf3174696 Get rid of curcc, instead cycle through the queue; removes the need for
client_none().
2016-09-20 18:21:32 +00:00
fa06851b0e cvsimport
* refs/heads/master:
  During init, query screen for _NET_ACTIVE_WINDOW and set that client as active; while we already look at what's under the pointer, use this information first, then look under the pointer (saving that round-trip). This restores the active state to a client after restart even if the pointer is not above it (and of course the pointer is not above another client).
2016-09-16 14:32:02 +00:00
6a53e3a859 During init, query screen for _NET_ACTIVE_WINDOW and set that client as
active; while we already look at what's under the pointer, use this
information first, then look under the pointer (saving that round-trip).
This restores the active state to a client after restart even if the
pointer is not above it (and of course the pointer is not above another
client).
2016-09-16 14:32:02 +00:00
8d44e4b3e8 cvsimport
* refs/heads/master:
  Some clients fail to setup hints at all, so initalize for them; fallout from r1.218 switching to malloc - clearly missed this case.
  Fix-up a few simple uses of client_current(): check CLIENT_ACTIVE flag instead of relying on curcc.
  init label
  Limit mouse resize to hints within the client; matches kbd resize behaviour.
  Switch to just malloc since we need initialize most everything anyway.
  change 'sticky' to 'stick' to toggle client stickiness (seems the default binding worked for everyone for a long time!); conflict with group sticky found by Ali Farzanrad - thanks!
  Simplify group_holds_only_hidden(); from Vadim Vygonets.
  Simplify toggling flags; from Vadim Vygonets.
  Do not draw borders on ignored clients when returning from fullscreen; from Vadim Vygonets.
  Remove redundant minimum client size adjustment (minw and minh are always positive since r1.214); from Vadim Vygonets.
2016-09-14 21:00:24 +00:00
2bbe111cc0 Some clients fail to setup hints at all, so initalize for them; fallout
from r1.218 switching to malloc - clearly missed this case.

found the hard way by brynet@
2016-09-14 21:00:24 +00:00
b8933ebcca Fix-up a few simple uses of client_current(): check CLIENT_ACTIVE flag
instead of relying on curcc.
2016-09-14 19:45:33 +00:00
9124a561e3 init label 2016-09-13 17:42:58 +00:00
a94254137c Limit mouse resize to hints within the client; matches kbd resize
behaviour.
2016-09-13 13:42:28 +00:00
e1234b75b8 Switch to just malloc since we need initialize most everything anyway. 2016-09-12 13:48:41 +00:00
b14a4b222d change 'sticky' to 'stick' to toggle client stickiness (seems the
default binding worked for everyone for a long time!); conflict with
group sticky found by Ali Farzanrad - thanks!
2016-09-12 13:47:00 +00:00
c82c3f0835 Simplify group_holds_only_hidden(); from Vadim Vygonets. 2016-09-02 16:07:11 +00:00
67a9eaa440 Simplify toggling flags; from Vadim Vygonets. 2016-09-02 15:08:44 +00:00
44f3fefe11 Do not draw borders on ignored clients when returning from fullscreen;
from Vadim Vygonets.
2016-09-01 18:38:52 +00:00
4cf9a34b65 Remove redundant minimum client size adjustment (minw and minh are
always positive since r1.214); from Vadim Vygonets.
2016-09-01 18:34:04 +00:00
d2616ce397 cvsimport
* refs/heads/master:
  Enable the use of numpad Enter key on menus; from Henrique N. Lengler.
2016-08-28 15:23:24 +00:00
8c1b05e534 Enable the use of numpad Enter key on menus; from Henrique N. Lengler.
ok beck phessler
2016-08-28 15:23:24 +00:00
319b388b42 cvsimport
* refs/heads/master:
  add a column to previous commit for consitency.
  conf_screen: report the fontname that can't be opened in case of failure. ok dcoppa@
2016-08-13 09:59:48 +00:00
5015c850bd add a column to previous commit for consitency. 2016-08-13 09:59:48 +00:00
375368a94c conf_screen: report the fontname that can't be opened in case of failure.
ok dcoppa@
2016-08-13 09:58:37 +00:00
bbbaacf177 cvsimport
* refs/heads/master:
  If supplied format to menuq_add() is NULL, fill text with an empty string; found by Christian Neukirchen.
2016-06-08 13:18:37 +02:00
ffc365bc5d If supplied format to menuq_add() is NULL, fill text with an empty
string; found by Christian Neukirchen.
2016-04-28 16:28:38 +00:00
a889f32af6 menu: menuq_add: avoid passing null pointers to vsnprintf. 2016-04-02 17:21:04 +02:00
4ac6daa27d Fix a typo from r1.201; fixes window_grouptoggle binding. 2015-11-17 15:19:19 +00:00
f9e9f76856 cvsimport
* refs/heads/master:
  Fix a typo from r1.201; fixes window_grouptoggle binding.
  If a client does not set increment values, use 'moveamount' as a way to scale keyboard based resizes; extend kbfunc_amount().
  Inline the only use of mousefunc_sweep_calc.
2015-11-17 15:19:19 +00:00
8aa2f317af If a client does not set increment values, use 'moveamount' as a way to
scale keyboard based resizes; extend kbfunc_amount().

Behaviour noted by, tested by, and ok sthen@
2015-11-17 14:32:38 +00:00
e8d77b6890 Inline the only use of mousefunc_sweep_calc. 2015-11-17 14:31:28 +00:00
669631e5fa Run pledge only on OpenBSD. 2015-11-16 14:47:21 +01:00
323672ace7 more client vs screen context differences 2015-11-12 21:28:03 +00:00
4e51ce57a7 cvsimport
* refs/heads/master:
  more client vs screen context differences
  If a client sets hints, honor them for kb resize requests, just like we do for mouse based resize requests.
  Move kb pointer movement out of the kbfunc_client_moveresize since it's got nothing to do with clients, thus doing flags work causes lots of waste and almost useless jumpy pointer movements; while here, split out move and resize since they share almost no code, just like mouse client move/resize; factor out amount and factor. Still wonder why this is here, but it works now.
  pledge "stdio rpath proc exec" cwm before main event loop, after init/setup - mostly for menu building.
  Partial revert of replacing screen_area() with region_find(); until a fix for a regression is found; this bug has been around for a long time it seems, but this change exposed it. Likely need to track clients in to and out of regions.
  Use position on root to figure out region.
  Start cleaning up name vs function differences; replace magic numbers.
  Clean up unused defines.
  Extend region to include both view and work areas; switch to region_find() which no longer needs to recalculate gap each time a client (or menu) is created or altered. If no RandR, fall back to display dimensions while building regions instead of during execution.
2015-11-12 21:28:03 +00:00
4aca2b8764 If a client sets hints, honor them for kb resize requests, just like we
do for mouse based resize requests.

Based on a patch from Vadim Vygonets.
2015-11-12 18:33:30 +00:00
9a48836ceb Move kb pointer movement out of the kbfunc_client_moveresize since it's
got nothing to do with clients, thus doing flags work causes lots of
waste and almost useless jumpy pointer movements; while here, split out
move and resize since they share almost no code, just like mouse client
move/resize; factor out amount and factor. Still wonder why this is
here, but it works now.
2015-11-12 18:26:41 +00:00
a2c066e447 pledge "stdio rpath proc exec" cwm before main event loop, after
init/setup - mostly for menu building.

ok semarie@ (another cwm user)
2015-11-12 17:56:54 +00:00
00bdd48b1d Partial revert of replacing screen_area() with region_find(); until a
fix for a regression is found; this bug has been around for a long time
it seems, but this change exposed it. Likely need to track clients in to
and out of regions.
2015-11-11 14:22:01 +00:00
09565bc423 Use position on root to figure out region. 2015-11-10 22:06:57 +00:00
e20110f344 Start cleaning up name vs function differences; replace magic numbers. 2015-11-10 20:05:33 +00:00
31c74e9157 Clean up unused defines. 2015-11-09 20:15:23 +00:00
d7bd299819 Extend region to include both view and work areas; switch to
region_find() which no longer needs to recalculate gap each time
a client (or menu) is created or altered. If no RandR, fall back
to display dimensions while building regions instead of during
execution.
2015-11-09 20:03:29 +00:00
cd21e16675 cvsimport
* refs/heads/master:
  Only when mapping clients from an initial wm start or restart, query the pointer and if it matches the child window, activate it; new clients will not need to make this roundtrip to the server.
  On execwm, we should properly release resources before exec'ing into a new window manager; so allow CWM_EXEC_WM to assign new wm to wm_argv and pass through cwm_status (now EXECWM) so that x_teardown() gets called before exec'ing the new window manager.  Removes the need for a separate x_restart() now, using new wm_argv; and consolidates errno for execvp.
2015-09-23 14:09:40 +00:00
5fcf251672 Only when mapping clients from an initial wm start or restart, query the
pointer and if it matches the child window, activate it; new clients
will not need to make this roundtrip to the server.

Based on a patch from Preben Guldberg.
2015-09-23 14:09:40 +00:00
0fdcf3f3df On execwm, we should properly release resources before exec'ing into a
new window manager; so allow CWM_EXEC_WM to assign new wm to wm_argv and
pass through cwm_status (now EXECWM) so that x_teardown() gets called
before exec'ing the new window manager.  Removes the need for a separate
x_restart() now, using new wm_argv; and consolidates errno for execvp.
2015-09-16 17:58:25 +00:00
3f0b6cf4ea cvsimport
* refs/heads/master: (21 commits)
  Lost fix from r1.112; add comment.
  Mechanical change: group->gc
  Add consistent checks against NULL.
  Move client cycle grab/ungrab into a more relevant place; while here, update comments about why we need to grab/ungrab the keyboard.
  Re-add lost chunk in group_cycle from r1.113.
  Further simplify _NET_WM_DESKTOP handling using new group_assign().
  oops; remove left over debug print
  Allowing sending a valid 'nogroup' (0) group_ctx to group_assign() (since we init all groups), though assigning the client's group to NULL for 'sticky'; use this simplification in a few places (others to follow).
  Split out sticky mode checks and the restoring of a client's group and _NET_WM_DESKTOP from the config-based auto-grouping; no (intentional) behavior changes.  Needed for further work in cleaning up this area.
  Implement _NET_CLIENT_LIST_STACKING (from Thomas Admin), but bottom-to-top order, as per spec (notified Thomas as well).
  Don't allow freeze operations on fullscreen (consistent with what fullscreen does).
  Sort _NET_WM_STATE Atoms like the spec.
  Move CLIENT_STICKY logic from client hide/unhide to group hide/unhide; rationale being that clients should be able to hide/unhide independently of group switching.
  Add Xkb modifier to ignore mask; from Alexander Polakov.
  Fix whitespace.
  Add client freeze extension to _NET_WM_STATE Atom, allowing flag to persist. As usual with new Atoms, requires X restart.
  _NET_WM_STATE_STICKY implies only sticky at the group/desktop level, not position and size; based on discussion with a few.
  Instead of special casing the 'term' and 'lock' commands, go back to keeping them hidden; showing them has apparently caused confusion/angst.
  Leave command list order from .cwmrc alone; remove sort.
  Bring group and client cycle closer together.
  ...
2015-08-28 12:07:28 +00:00
47a10cc055 Lost fix from r1.112; add comment.
Reported (again!) by Peter Kane.
2015-08-28 12:07:28 +00:00
28d4001eca Mechanical change: group->gc 2015-08-27 18:53:14 +00:00
f467838e7b Add consistent checks against NULL. 2015-08-27 18:42:56 +00:00
19826222f6 Move client cycle grab/ungrab into a more relevant place; while here,
update comments about why we need to grab/ungrab the keyboard.
2015-08-27 18:40:09 +00:00
5391659629 Re-add lost chunk in group_cycle from r1.113. 2015-08-27 17:43:44 +00:00
6d53091429 Further simplify _NET_WM_DESKTOP handling using new group_assign(). 2015-08-25 20:35:49 +00:00
871ed39ccf oops; remove left over debug print 2015-08-25 19:52:02 +00:00
cb60a4b917 Allowing sending a valid 'nogroup' (0) group_ctx to group_assign()
(since we init all groups), though assigning the client's group to NULL
for 'sticky'; use this simplification in a few places (others to
follow).
2015-08-25 19:49:19 +00:00
96262a6b0c Split out sticky mode checks and the restoring of a client's group and
_NET_WM_DESKTOP from the config-based auto-grouping; no (intentional)
behavior changes.  Needed for further work in cleaning up this area.
2015-08-25 18:29:10 +00:00
dcfbc9e809 Implement _NET_CLIENT_LIST_STACKING (from Thomas Admin), but
bottom-to-top order, as per spec (notified Thomas as well).
2015-08-24 15:42:57 +00:00
97db17d056 Don't allow freeze operations on fullscreen (consistent with what
fullscreen does).
2015-08-24 14:57:19 +00:00
4269ea0463 Sort _NET_WM_STATE Atoms like the spec. 2015-08-24 14:56:10 +00:00
b224945446 Move CLIENT_STICKY logic from client hide/unhide to group hide/unhide;
rationale being that clients should be able to hide/unhide independently
of group switching.
2015-08-23 17:31:20 +00:00
ae9306748b Add Xkb modifier to ignore mask; from Alexander Polakov. 2015-08-21 16:53:48 +00:00
cd3bbb1c9c Fix whitespace. 2015-08-21 16:52:37 +00:00
1d31f9000c Add client freeze extension to _NET_WM_STATE Atom, allowing flag to
persist. As usual with new Atoms, requires X restart.
2015-08-21 16:30:02 +00:00
05478f061e _NET_WM_STATE_STICKY implies only sticky at the group/desktop level, not
position and size; based on discussion with a few.
2015-08-21 16:14:39 +00:00
c96fd247dd Instead of special casing the 'term' and 'lock' commands, go back to
keeping them hidden; showing them has apparently caused confusion/angst.
2015-08-21 16:05:55 +00:00
a79253abdc Leave command list order from .cwmrc alone; remove sort. 2015-08-21 15:55:39 +00:00
871c9f24ba Bring group and client cycle closer together. 2015-08-21 15:52:49 +00:00
54bbd88e51 Alter big move and resize bindings to match what's in cwm(1);
functionally the same, but re-binding should be based on default
bindings. Matches other similiar bindings.

Discovered by a portable user.
2015-08-20 14:45:15 +00:00
c0f2d0cc75 cvsimport 2015-07-12 14:31:47 +00:00
0d13b7c220 introduce 'groupsearch' for group menu search; matches on either group
number/shortcut and/or name.
2015-07-12 14:31:47 +00:00
5c2decc8d0 simplify menu print text 2015-07-12 14:21:09 +00:00
02779e64b8 revert previous; seems search_match_text() needs mi->text pre-populated.
(only used in one place, application menu searching); re-visit later.

broken application menu searching reported by Peter Kane.
2015-07-03 17:11:16 +00:00
6716719593 cvsimport 2015-07-01 16:40:15 +02:00
cb900def7f style 2015-07-01 14:36:42 +00:00
8efaf33cfb no longer need to pass down format strings after introducing search_print_cmd 2015-06-30 19:02:24 +00:00
5d8027b75d Introduce a callback for cmd menu printing, special-casing 'lock' and
'term'.
2015-06-30 18:54:12 +00:00
c87953eb64 keep cmdq sorted 2015-06-30 18:44:29 +00:00
6b8b856217 style and spacing nits 2015-06-30 18:42:50 +00:00
8515d717ae Re-implement XClientMessage handling so that we can feed screen_find and
client_find valid resources as needed, relieving the need for
screen_find to ungracefully handle invalid root windows. Removes a long
standing XXX.  Should theoretically allow XClientMessage handling on
more than one X screen.  Alter callers of screen_find to handle
failures.
2015-06-30 14:01:43 +00:00
9ea5f25347 Shuffle code in kbfunc_ssh so that a missing known_hosts file still
allows a (now blank) menu to appear, as opposed to nothing at all.

Behavior reported by Alex Greif.
2015-06-29 14:24:40 +00:00
e7b4045ece move client_find down 2015-06-28 19:54:37 +00:00
17720de4ab replace assert usage 2015-06-28 19:50:46 +00:00
18f63629fd region is a _ctx, so name it so 2015-06-26 18:54:25 +00:00
66bc416217 Mechanical change from xinerama to region backed areas. 2015-06-26 17:17:46 +00:00
90288e2fc3 collect 'gap' applications 2015-06-26 16:11:21 +00:00
8aa40078d1 Replace screen region info gathering with XRandR equivalent of Xinerama
queries (currently act on XRandR events anyway). Fall-back mode without
XRandR is still what X provides. This removes -lXinerama.
2015-06-26 15:21:58 +00:00
4454948f21 cvsimport 2015-06-09 13:02:15 +00:00
ee400b08a8 typo in comment; from jsteinha [at] iutbayonne.univ-pau.fr 2015-06-09 13:02:15 +00:00
654bae4f59 just populating the ctx is enough since the menu uses a callback and ignores anything else 2015-06-08 15:43:13 +00:00
e9750e2144 switch to search_print_client() as the print callback since it will now display labels as well 2015-06-08 15:41:27 +00:00
8888c44888 add client label 2015-06-08 15:34:03 +00:00
9180bb1a06 stash window dimensions 2015-06-08 15:11:29 +00:00
8653c88c90 clean up 2015-06-08 15:08:44 +00:00
bd5f724931 even if the menuq is empty, at least show an empty menu instead of just
bailing making it look like the binding isn't working.
2015-06-07 19:10:00 +00:00
cd5827491c use the same geometry struct in menus as everywhere else 2015-06-05 18:43:36 +00:00
a4cba7e113 add a flag to indicate a menu list instead of overloading 'prompt' 2015-06-05 15:01:51 +00:00
f22a3b1065 redundant check for 'list' 2015-06-05 14:54:04 +00:00
d9f38134be a start to cleaning:
- no reason for pointer movement and window movement and resizing
  sections that just describe default key bindings
- key bindings are for all search and exec dialogs, not just for windows
- adding missing bindings
- normalize and wordsmith
- re-order sections according to mdoc(7)

feedback and ok from jmc@
2015-05-31 23:07:36 +00:00
b1c2046efd merge kbd and mouse grouptoggle 2015-05-21 00:37:04 +00:00
31d4fe9f33 fill in mwm defines 2015-05-20 23:54:39 +00:00
b9db4486da Fix autogroup so name,class doesn't need to be quoted.
ok okan@ on an older version of this diff
2015-05-17 04:39:50 +00:00
d328f63c7d Make window name and window class separate parameters to conf_autogroup.
No functional change, but will be used shortly.

ok okan@
2015-05-17 04:34:01 +00:00
7c4a8f3b51 Fix stat patch to use lstat(2) and fix logic 2015-04-23 21:42:34 +02:00
4cb6e0b17b Add an additional stat() check if a d_type check fails for kbfunc_exec lookups
Some filesystems (eg XFS) don't populate the d_type field of struct
dirent, used by kbfunc_exec to filter paths. This patch does an
additional stat() check for any file that fails the d_type check which
will work correctly on these filesystems.
2015-04-12 19:08:34 +02:00
da1021c60c queue.h: update. 2015-04-06 19:47:04 +02:00
ffbfc32999 add reallocarray.c 2015-03-29 18:12:47 +02:00
557cd382dc plug a leak 2015-03-29 00:21:05 +00:00
bc21e4aa37 cvsimport 2015-03-29 00:21:05 +00:00
a4a414b68b Introduce a xreallocarray and convert a few xcalloc instances that do
not require zero'ing.
2015-03-28 23:12:47 +00:00
0bbe0ad98c Reshuffle and slightly simplify menu_complete_path(), removing an
allocation, for tab-completion; checked by Alexander Polakov as well.
2015-03-28 22:09:10 +00:00
5b64e1540c plug a leak 2015-03-28 21:55:48 +00:00
bad97699f9 Simplify key/mb binding moving argtype into flags and dropping another
variable; removes the need to zero out struct binding, leaving a simple
malloc.
2015-03-26 21:41:43 +00:00
ac42dff6c1 simplify error messages; discussed with doug@ 2015-02-14 18:24:12 +00:00
668da8493a cvsimport 2015-02-11 14:20:35 +00:00
81c05b3a51 Merge pull request #8 from afh/pull/macosx
Use _POSIX_HOST_NAME_MAX
2015-02-11 15:19:03 +01:00
9cfb9225ed Use _POSIX_HOST_NAME_MAX
to be able to compile under Mac OS X.
2015-02-11 15:11:43 +01:00
7e0c2e7728 calloc -> malloc 2015-01-24 18:16:59 +00:00
976d77c76d update imported str*.c 2015-01-24 19:04:46 +01:00
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
1603d18560 cvsimport 2011-12-29 20:48:38 +00:00
96ebac8b04 fix pkg-config packages 2011-12-12 16:17:39 +01:00
cbe46b651f cvsimport 2011-11-06 02:03:47 +00:00
901a75d3c6 cvsimport 2011-10-17 18:18:38 +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
54c1b90725 cvsimport 2011-09-19 07:23:03 +00:00
3c67ec8ccc cvsimport 2011-09-13 09:17:30 +00:00
b1d81b7eb6 cvsimport 2011-09-08 12:35:33 +00:00
513c35dca9 cvsimport 2011-08-04 20:53:37 +02:00
f067809af4 cvsimport 2011-05-15 17:58:47 +00:00
a90a0b5705 cvsimport 2011-05-07 17:15:37 +00:00
2bac9a6be9 cvsimport 2011-05-03 19:28:18 +02:00
9eaece0541 cvsimport 2011-02-18 11:50:01 +00:00
5bc15cdb58 cvsimport 2010-12-24 23:42:39 +01: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 6121 additions and 4145 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,24 +1,54 @@
# $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 xevents.c group.c \
kbfunc.c mousefunc.c font.c parse.y
kbfunc.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 strlcpy.o strlcat.o parse.o \
strtonum.o reallocarray.o
PKG_CONFIG?= pkg-config
CFLAGS+= -Wall
CPPFLAGS+= `${PKG_CONFIG} --cflags x11 xft xrandr`
LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lxcb -lXau -lXdmcp \
-lfontconfig -lexpat -lfreetype -lz -lXinerama -lXrandr -lXext
CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE
MANDIR= ${X11BASE}/man/man
MAN= cwm.1 cwmrc.5
LDFLAGS+= `${PKG_CONFIG} --libs x11 xft xrandr`
obj: _xenocara_obj
MANPREFIX?= ${PREFIX}/share/man
.include <bsd.prog.mk>
.include <bsd.xorg.mk>
all: ${PROG}
clean:
rm -f ${OBJS} ${PROG} parse.c
${PROG}: ${OBJS}
${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/-[^.]*$$//') && \
gpg2 --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
.PRECIOUS: parse.c

147
README Normal file
View File

@ -0,0 +1,147 @@
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/leahneukirchen/cwm
Releases can be found at http://leahneukirchen.org/releases
You are welcome to join the IRC channel ##cwm on irc.libera.chat
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.
Changes made between OpenBSD 5.6 and 5.7
* Implemented _NET_WM_STATE_STICKY in cwm(1). Allows client to "stick"
to all desktops or groups.
* Ensure cwm(1) client that wants to be in nogroup stays in nogroup
(thus stays in view), even when (re)reading NET_WM_DESKTOP.
Changes made between OpenBSD 5.7 and 5.8
* In cwm(1), introduce "groupsearch" for group menu search.
* In cwm(1), show an empty "ssh to" menu if the known_hosts file is missing.
* In cwm(1), replace screen region info gathering with XRandR
equivalent of Xinerama queries.
Changes made between OpenBSD 5.8 and 5.9
* Don't allow freeze operations on fullscreen.
* Implement _NET_CLIENT_LIST_STACKING.
2017-10-17: Fourth public release 6.2 of portable cwm.
Changes made between OpenBSD 6.2 and 6.3
* Fix blocking bug during moving or resizing.
* window-snap-* commands to move windows to edges and corners.
* Add support for _NET_WM_STATE_SKIP_PAGER and _NET_WM_STATE_SKIP_TASKBAR.
* Add support for re-exec'ing with SIGHUP.
2018-05-14: Fifth public release 6.3 of portable cwm.
2020-01-04: Sixth public release 6.6 of portable cwm.
Changes made between OpenBSD 6.4 and 6.5
* Added a configtest flag (-n) to cwm(1).
* Introduced 'group-close-[n]' action to cwm(1) to close all windows
within a specified group.
2020-05-22: Seventh public release 6.7 of portable cwm.
Changes made between OpenBSD 6.6 and 6.7
* Allowed cwm(1) configuration of window size based on percentage of
the master window during horizontal and vertical tiling actions.
* Allowed use of window-htile and window-vtile with the "empty" group
clients in cwm(1).
2022-04-30: Eighth public release 7.1 of portable cwm.
Changes made between OpenBSD 6.9 and 7.0
* Changed cwm(1) maximization and full-screen mode toggling to keep
the cursor within the window, preventing focus loss.
Changes made between OpenBSD 7.0 and 7.1
* Added a cwm(1) "group-last" command that shows only the previously
active group.
* Allowed bare numbers for key and mouse bindings in cwm(1).
--Leah Neukirchen <leah@vuxu.org>
[0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/

260
calmwm.c
View File

@ -18,58 +18,70 @@
* $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 <poll.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;
Display *X_Dpy;
Time Last_Event_Time = CurrentTime;
Atom cwmh[CWMH_NITEMS];
Atom ewmh[EWMH_NITEMS];
struct screen_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct conf Conf;
volatile sig_atomic_t cwm_status;
Cursor Cursor_default;
Cursor Cursor_move;
Cursor Cursor_normal;
Cursor Cursor_question;
Cursor Cursor_resize;
struct screen_ctx_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct client_ctx_q Clientq = TAILQ_HEAD_INITIALIZER(Clientq);
int HasXinerama, HasRandr, Randr_ev;
struct conf Conf;
static void sigchld_cb(int);
static void dpy_init(const char *);
void usage(void);
static void sighdlr(int);
static int x_errorhandler(Display *, XErrorEvent *);
static int x_wmerrorhandler(Display *, XErrorEvent *);
static void x_setup(void);
static void x_setupscreen(struct screen_ctx *, u_int);
static int x_init(const 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;
int ch;
char *fallback;
int ch, xfd, nflag = 0;
struct pollfd pfd[1];
while ((ch = getopt(argc, argv, "c:d:")) != -1) {
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
warnx("no locale support");
mbtowc(NULL, NULL, MB_CUR_MAX);
conf_init(&Conf);
fallback = u_argv(argv);
Conf.wm_argv = u_argv(argv);
while ((ch = getopt(argc, argv, "c:d:nv")) != -1) {
switch (ch) {
case 'c':
conf_file = optarg;
free(Conf.conf_file);
Conf.conf_file = xstrdup(optarg);
break;
case 'd':
display_name = optarg;
break;
case 'n':
nflag = 1;
break;
case 'v':
Conf.debug++;
break;
default:
usage();
}
@ -77,154 +89,108 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (signal(SIGCHLD, sigchld_cb) == SIG_ERR)
if (signal(SIGCHLD, sighdlr) == SIG_ERR ||
signal(SIGHUP, sighdlr) == SIG_ERR ||
signal(SIGINT, sighdlr) == SIG_ERR ||
signal(SIGTERM, sighdlr) == SIG_ERR)
err(1, "signal");
dpy_init(display_name);
if (parse_config(Conf.conf_file, &Conf) == -1) {
warnx("error parsing config file");
if (nflag)
return 1;
}
if (nflag)
return 0;
bzero(&Conf, sizeof(Conf));
conf_setup(&Conf, conf_file);
xu_getatoms();
x_setup();
xfd = x_init(display_name);
cwm_status = CWM_RUNNING;
xev_loop();
#ifdef __OpenBSD__
if (pledge("stdio rpath proc exec", NULL) == -1)
err(1, "pledge");
#endif
memset(&pfd, 0, sizeof(pfd));
pfd[0].fd = xfd;
pfd[0].events = POLLIN;
while (cwm_status == CWM_RUNNING) {
xev_process();
if (poll(pfd, 1, -1) == -1) {
if (errno != EINTR)
warn("poll");
}
}
x_teardown();
if (cwm_status == CWM_EXEC_WM) {
u_exec(Conf.wm_argv);
warnx("'%s' failed to start, starting fallback", Conf.wm_argv);
u_exec(fallback);
}
return (0);
return 0;
}
static void
dpy_init(const char *dpyname)
static int
x_init(const char *dpyname)
{
int i;
XSetErrorHandler(x_errorhandler);
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.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i);
static void
x_setup(void)
{
struct screen_ctx *sc;
struct keybinding *kb;
int i;
xu_atom_init();
conf_cursor(&Conf);
Cursor_default = XCreateFontCursor(X_Dpy, XC_X_cursor);
Cursor_move = XCreateFontCursor(X_Dpy, XC_fleur);
Cursor_normal = XCreateFontCursor(X_Dpy, XC_left_ptr);
Cursor_question = XCreateFontCursor(X_Dpy, XC_question_arrow);
Cursor_resize = XCreateFontCursor(X_Dpy, XC_bottom_right_corner);
for (i = 0; i < ScreenCount(X_Dpy); i++)
screen_init(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);
return ConnectionNumber(X_Dpy);
}
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);
XCloseDisplay(X_Dpy);
}
static void
x_setupscreen(struct screen_ctx *sc, u_int which)
{
Window *wins, w0, w1;
XWindowAttributes winattr;
XSetWindowAttributes rootattr;
int fake;
u_int nwins, i;
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, sc->which);
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);
conf_font(&Conf, sc);
TAILQ_INIT(&sc->mruq);
/* Initialize menu window. */
menu_init(sc);
xu_setwmname(sc);
rootattr.cursor = Cursor_normal;
rootattr.event_mask = CHILDMASK|PropertyChangeMask|EnterWindowMask|
LeaveWindowMask|ColormapChangeMask|BUTTONMASK;
XChangeWindowAttributes(X_Dpy, sc->rootwin,
CWEventMask|CWCursor, &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;
(void)client_new(wins[i], sc, winattr.map_state != IsUnmapped);
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]);
XftFontClose(X_Dpy, sc->xftfont);
XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin);
}
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);
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 int
x_wmerrorhandler(Display *dpy, XErrorEvent *e)
{
errx(1, "root window unavailable - perhaps another wm is running?");
return (0);
return 0;
}
static int
x_errorhandler(Display *dpy, XErrorEvent *e)
{
#if DEBUG
#ifdef DEBUG
char msg[80], number[80], req[80];
XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg));
@ -232,32 +198,42 @@ x_errorhandler(Display *dpy, XErrorEvent *e)
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
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(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
break;
case SIGHUP:
cwm_status = CWM_EXEC_WM;
break;
case SIGINT:
case SIGTERM:
cwm_status = CWM_QUIT;
break;
}
errno = save_errno;
}
__dead void
void
usage(void)
{
extern char *__progname;
(void)fprintf(stderr, "usage: %s [-c file] [-d display]\n",
(void)fprintf(stderr, "usage: %s [-nv] [-c file] [-d display]\n",
__progname);
exit(1);
}

777
calmwm.h
View File

@ -21,16 +21,42 @@
#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 **);
void *reallocarray(void *, size_t, size_t);
#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>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/extensions/Xinerama.h>
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
#define LOG_DEBUG0(...) log_debug(0, __func__, __VA_ARGS__)
#define LOG_DEBUG1(...) log_debug(1, __func__, __VA_ARGS__)
#define LOG_DEBUG2(...) log_debug(2, __func__, __VA_ARGS__)
#define LOG_DEBUG3(...) log_debug(3, __func__, __VA_ARGS__)
#undef MIN
#undef MAX
#define MIN(x, y) ((x) < (y) ? (x) : (y))
@ -40,61 +66,60 @@
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
#define CONFFILE ".cwmrc"
#define WMNAME "CWM"
#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
#define MOUSEMASK (BUTTONMASK | PointerMotionMask)
#define IGNOREMODMASK (LockMask | Mod2Mask | 0x2000)
#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)
/* direction/amount */
#define CWM_UP 0x0001
#define CWM_DOWN 0x0002
#define CWM_LEFT 0x0004
#define CWM_RIGHT 0x0008
#define CWM_BIGAMOUNT 0x0010
#define CWM_UP_BIG (CWM_UP | CWM_BIGAMOUNT)
#define CWM_DOWN_BIG (CWM_DOWN | CWM_BIGAMOUNT)
#define CWM_LEFT_BIG (CWM_LEFT | CWM_BIGAMOUNT)
#define CWM_RIGHT_BIG (CWM_RIGHT | CWM_BIGAMOUNT)
#define CWM_UP_RIGHT (CWM_UP | CWM_RIGHT)
#define CWM_UP_LEFT (CWM_UP | CWM_LEFT)
#define CWM_DOWN_RIGHT (CWM_DOWN | CWM_RIGHT)
#define CWM_DOWN_LEFT (CWM_DOWN | CWM_LEFT)
/* 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
#define CWM_CYCLE_FORWARD 0x0001
#define CWM_CYCLE_REVERSE 0x0002
#define CWM_CYCLE_INGROUP 0x0004
/* exec */
#define CWM_EXEC_PROGRAM 0x0001
#define CWM_EXEC_WM 0x0002
/* cycle */
#define CWM_CYCLE 0x0001
#define CWM_RCYCLE 0x0002
#define CWM_INGROUP 0x0004
#define KBTOGROUP(X) ((X) - 1)
union arg {
char *c;
int i;
enum cwm_status {
CWM_QUIT,
CWM_RUNNING,
CWM_EXEC_WM
};
enum cwmcolor {
enum cursor_font {
CF_NORMAL,
CF_MOVE,
CF_RESIZE,
CF_QUESTION,
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_FONT,
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 {
char *name;
unsigned long pixel;
struct geom {
int x;
int y;
int w;
int h;
};
struct gap {
int top;
int bottom;
@ -106,24 +131,20 @@ struct winname {
TAILQ_ENTRY(winname) entry;
char *name;
};
TAILQ_HEAD(winname_q, winname);
TAILQ_HEAD(name_q, winname);
TAILQ_HEAD(ignore_q, winname);
struct client_ctx {
TAILQ_ENTRY(client_ctx) entry;
TAILQ_ENTRY(client_ctx) group_entry;
TAILQ_ENTRY(client_ctx) mru_entry;
TAILQ_ENTRY(client_ctx) entry;
struct screen_ctx *sc;
struct group_ctx *gc;
Window win;
XSizeHints *size;
Colormap cmap;
u_int bwidth; /* border width */
struct {
int x; /* x position */
int y; /* y position */
int width; /* width */
int height;/* height */
} geom, savegeom;
Colormap colormap;
int bwidth; /* border width */
int obwidth; /* original border width */
struct geom geom, savegeom, fullgeom;
struct {
long flags; /* defined hints */
int basew; /* desired width */
int baseh; /* desired height */
int minw; /* minimum width */
@ -139,125 +160,138 @@ struct client_ctx {
int x; /* x position */
int y; /* y position */
} ptr;
#define CLIENT_PROTO_DELETE 0x0001
#define CLIENT_PROTO_TAKEFOCUS 0x0002
int xproto;
struct {
int h; /* height */
int w; /* width */
} dim;
#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
#define CLIENT_SKIP_PAGER 0x4000
#define CLIENT_SKIP_TASKBAR 0x8000
#define CLIENT_SKIP_CYCLE (CLIENT_HIDDEN | CLIENT_IGNORE | \
CLIENT_SKIP_TASKBAR | CLIENT_SKIP_PAGER)
#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;
int active;
int stackingorder;
#define CLIENT_HIGHLIGHT_GROUP 0x0001
#define CLIENT_HIGHLIGHT_UNGROUP 0x0002
int highlight;
struct winname_q nameq;
#define CLIENT_MAXNAMEQLEN 5
int nameqlen;
struct name_q nameq;
char *name;
char *label;
char *matchname;
struct group_ctx *group;
char *app_class;
char *app_name;
char *res_class; /* class hint */
char *res_name; /* class hint */
int initial_state; /* wm hint */
};
TAILQ_HEAD(client_ctx_q, client_ctx);
TAILQ_HEAD(cycle_entry_q, client_ctx);
struct winmatch {
TAILQ_ENTRY(winmatch) entry;
#define WIN_MAXTITLELEN 256
char title[WIN_MAXTITLELEN];
};
TAILQ_HEAD(winmatch_q, winmatch);
TAILQ_HEAD(client_q, client_ctx);
struct group_ctx {
TAILQ_ENTRY(group_ctx) entry;
struct client_ctx_q clients;
int shortcut;
int hidden;
int nhidden;
int highstack;
struct screen_ctx *sc;
char *name;
int num;
};
TAILQ_HEAD(group_ctx_q, group_ctx);
TAILQ_HEAD(group_q, group_ctx);
struct autogroupwin {
TAILQ_ENTRY(autogroupwin) entry;
char *class;
char *name;
int num;
struct autogroup {
TAILQ_ENTRY(autogroup) entry;
char *class;
char *name;
int num;
};
TAILQ_HEAD(autogroupwin_q, autogroupwin);
TAILQ_HEAD(autogroup_q, autogroup);
struct region_ctx {
TAILQ_ENTRY(region_ctx) entry;
int num;
struct geom view; /* viewable area */
struct geom work; /* workable area, gap-applied */
};
TAILQ_HEAD(region_q, region_ctx);
struct screen_ctx {
TAILQ_ENTRY(screen_ctx) entry;
u_int which;
int which;
Window rootwin;
Window menuwin;
struct color color[CWM_COLOR_MAX];
GC gc;
int altpersist;
int xmax;
int ymax;
int cycling;
int hideall;
int snapdist;
struct geom view; /* viewable area */
struct geom work; /* workable area, gap-applied */
struct gap gap;
struct cycle_entry_q mruq;
XftColor xftcolor;
XftDraw *xftdraw;
XftFont *font;
int xinerama_no;
XineramaScreenInfo *xinerama;
#define CALMWM_NGROUPS 9
struct group_ctx groups[CALMWM_NGROUPS];
struct group_ctx_q groupq;
int group_hideall;
int group_nonames;
struct client_q clientq;
struct region_q regionq;
struct group_q groupq;
struct group_ctx *group_active;
char **group_names;
struct group_ctx *group_last;
Colormap colormap;
Visual *visual;
struct {
Window win;
XftDraw *xftdraw;
} prop;
XftColor xftcolor[CWM_COLOR_NITEMS];
XftFont *xftfont;
};
TAILQ_HEAD(screen_ctx_q, screen_ctx);
TAILQ_HEAD(screen_q, screen_ctx);
struct keybinding {
TAILQ_ENTRY(keybinding) entry;
void (*callback)(struct client_ctx *, union arg *);
union arg argument;
int modmask;
int keysym;
int keycode;
#define KBFLAG_NEEDCLIENT 0x0001
int flags;
struct cargs {
char *cmd;
int flag;
enum {
CWM_XEV_KEY,
CWM_XEV_BTN
} xev;
};
TAILQ_HEAD(keybinding_q, keybinding);
enum context {
CWM_CONTEXT_NONE = 0,
CWM_CONTEXT_CC,
CWM_CONTEXT_SC
};
struct bind_ctx {
TAILQ_ENTRY(bind_ctx) entry;
void (*callback)(void *, struct cargs *);
struct cargs *cargs;
enum context context;
unsigned int modmask;
union {
KeySym keysym;
unsigned int button;
} press;
};
TAILQ_HEAD(keybind_q, bind_ctx);
TAILQ_HEAD(mousebind_q, bind_ctx);
struct mousebinding {
TAILQ_ENTRY(mousebinding) entry;
void (*callback)(struct client_ctx *, void *);
int modmask;
int button;
#define MOUSEBIND_CTX_ROOT 0x0001
#define MOUSEBIND_CTX_WIN 0x0002
int context;
struct cmd_ctx {
TAILQ_ENTRY(cmd_ctx) entry;
char *name;
char *path;
};
TAILQ_HEAD(mousebinding_q, mousebinding);
TAILQ_HEAD(cmd_q, cmd_ctx);
TAILQ_HEAD(wm_q, cmd_ctx);
struct cmd {
TAILQ_ENTRY(cmd) entry;
int flags;
char image[MAXPATHLEN];
#define CMD_MAXLABELLEN 256
char label[CMD_MAXLABELLEN];
};
TAILQ_HEAD(cmd_q, cmd);
#define CWM_MENU_DUMMY 0x0001
#define CWM_MENU_FILE 0x0002
#define CWM_MENU_LIST 0x0004
#define CWM_MENU_WINDOW_ALL 0x0008
#define CWM_MENU_WINDOW_HIDDEN 0x0010
struct menu {
TAILQ_ENTRY(menu) entry;
TAILQ_ENTRY(menu) resultentry;
#define MENU_MAXENTRY 50
#define MENU_MAXENTRY 200
char text[MENU_MAXENTRY + 1];
char print[MENU_MAXENTRY + 1];
void *ctx;
@ -267,253 +301,332 @@ struct menu {
TAILQ_HEAD(menu_q, menu);
struct conf {
struct keybinding_q keybindingq;
struct autogroupwin_q autogroupq;
struct winmatch_q ignoreq;
char conf_path[MAXPATHLEN];
struct keybind_q keybindq;
struct mousebind_q mousebindq;
struct autogroup_q autogroupq;
struct ignore_q ignoreq;
struct cmd_q cmdq;
struct mousebinding_q mousebindingq;
#define CONF_STICKY_GROUPS 0x0001
int flags;
#define CONF_BWIDTH 1
struct wm_q wmq;
int ngroups;
int stickygroups;
int nameqlen;
int bwidth;
#define CONF_MAMOUNT 1
int mamount;
#define CONF_SNAPDIST 0
int snapdist;
int htile;
int vtile;
struct gap gap;
struct color color[CWM_COLOR_MAX];
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
#define CONF_FONT "sans-serif:pixelsize=14:bold"
char *color[CWM_COLOR_NITEMS];
char *font;
char *wmname;
Cursor cursor[CF_NITEMS];
int xrandr;
int xrandr_event_base;
char *conf_file;
char *known_hosts;
char *wm_argv;
int debug;
};
/* MWM hints */
struct mwm_hints {
u_long flags;
u_long functions;
u_long decorations;
#define MWM_HINTS_ELEMENTS 5L
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_HINTS_STATUS (1L << 3)
unsigned long flags;
#define MWM_FUNC_ALL (1L << 0)
#define MWM_FUNC_RESIZE (1L << 1)
#define MWM_FUNC_MOVE (1L << 2)
#define MWM_FUNC_MINIMIZE (1L << 3)
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)
unsigned long functions;
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)
unsigned long decorations;
#define MWM_INPUT_MODELESS 0
#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
#define MWM_INPUT_SYSTEM_MODAL 2
#define MWM_INPUT_FULL_APPLICATION_MODAL 3
long inputMode;
#define MWM_TEAROFF_WINDOW (1L << 0)
unsigned long status;
};
#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)
__dead void usage(void);
enum cwmh {
WM_STATE,
WM_DELETE_WINDOW,
WM_TAKE_FOCUS,
WM_PROTOCOLS,
_MOTIF_WM_HINTS,
UTF8_STRING,
WM_CHANGE_STATE,
CWMH_NITEMS
};
enum ewmh {
_NET_SUPPORTED,
_NET_SUPPORTING_WM_CHECK,
_NET_ACTIVE_WINDOW,
_NET_CLIENT_LIST,
_NET_CLIENT_LIST_STACKING,
_NET_NUMBER_OF_DESKTOPS,
_NET_CURRENT_DESKTOP,
_NET_DESKTOP_VIEWPORT,
_NET_DESKTOP_GEOMETRY,
_NET_VIRTUAL_ROOTS,
_NET_SHOWING_DESKTOP,
_NET_DESKTOP_NAMES,
_NET_WORKAREA,
_NET_WM_NAME,
_NET_WM_DESKTOP,
_NET_CLOSE_WINDOW,
_NET_WM_STATE,
#define _NET_WM_STATES_NITEMS 9
_NET_WM_STATE_STICKY,
_NET_WM_STATE_MAXIMIZED_VERT,
_NET_WM_STATE_MAXIMIZED_HORZ,
_NET_WM_STATE_HIDDEN,
_NET_WM_STATE_FULLSCREEN,
_NET_WM_STATE_DEMANDS_ATTENTION,
_NET_WM_STATE_SKIP_PAGER,
_NET_WM_STATE_SKIP_TASKBAR,
_CWM_WM_STATE_FREEZE,
EWMH_NITEMS
};
enum net_wm_state {
_NET_WM_STATE_REMOVE,
_NET_WM_STATE_ADD,
_NET_WM_STATE_TOGGLE
};
void client_applysizehints(struct client_ctx *);
struct client_ctx *client_current(void);
void client_cycle(struct screen_ctx *, int);
void client_delete(struct client_ctx *);
extern Display *X_Dpy;
extern Time Last_Event_Time;
extern Atom cwmh[CWMH_NITEMS];
extern Atom ewmh[EWMH_NITEMS];
extern struct screen_q Screenq;
extern struct conf Conf;
void usage(void);
void client_apply_sizehints(struct client_ctx *);
void client_close(struct client_ctx *);
void client_config(struct client_ctx *);
struct client_ctx *client_current(struct screen_ctx *);
void client_draw_border(struct client_ctx *);
struct client_ctx *client_find(Window);
void client_freeze(struct client_ctx *);
void client_getsizehints(struct client_ctx *);
void client_get_sizehints(struct client_ctx *);
void client_hide(struct client_ctx *);
void client_horizmaximize(struct client_ctx *);
void client_leave(struct client_ctx *);
void client_htile(struct client_ctx *);
int client_inbound(struct client_ctx *, int, int);
struct client_ctx *client_init(Window, struct screen_ctx *);
void client_lower(struct client_ctx *);
void client_map(struct client_ctx *);
void client_maximize(struct client_ctx *);
void client_move(struct client_ctx *);
void client_mtf(struct client_ctx *);
struct client_ctx *client_new(Window, struct screen_ctx *, int);
void client_ptrsave(struct client_ctx *);
void client_ptrwarp(struct client_ctx *);
struct client_ctx *client_next(struct client_ctx *);
struct client_ctx *client_prev(struct client_ctx *);
void client_ptr_inbound(struct client_ctx *, int);
void client_ptr_save(struct client_ctx *);
void client_ptr_warp(struct client_ctx *);
void client_raise(struct client_ctx *);
void client_resize(struct client_ctx *);
void client_send_delete(struct client_ctx *);
void client_setactive(struct client_ctx *, int);
void client_setname(struct client_ctx *);
void client_remove(struct client_ctx *);
void client_resize(struct client_ctx *, int);
void client_set_active(struct client_ctx *);
void client_set_name(struct client_ctx *);
void client_show(struct client_ctx *);
int client_snapcalc(int, int, int, int, int);
void client_toggle_hidden(struct client_ctx *);
void client_toggle_hmaximize(struct client_ctx *);
void client_toggle_fullscreen(struct client_ctx *);
void client_toggle_freeze(struct client_ctx *);
void client_toggle_maximize(struct client_ctx *);
void client_toggle_skip_pager(struct client_ctx *);
void client_toggle_skip_taskbar(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_vertmaximize(struct client_ctx *);
void client_warp(struct client_ctx *);
void client_urgency(struct client_ctx *);
void client_vtile(struct client_ctx *);
void client_wm_hints(struct client_ctx *);
void group_alltoggle(struct screen_ctx *);
void group_autogroup(struct client_ctx *);
void group_client_delete(struct client_ctx *);
void group_assign(struct group_ctx *, struct client_ctx *);
int group_autogroup(struct client_ctx *);
void group_cycle(struct screen_ctx *, int);
void group_hidetoggle(struct screen_ctx *, int);
void group_init(struct screen_ctx *);
void group_make_autogroup(struct conf *, char *, int);
void group_menu(XButtonEvent *);
void group_hide(struct group_ctx *);
int group_holds_only_hidden(struct group_ctx *);
int group_holds_only_sticky(struct group_ctx *);
void group_init(struct screen_ctx *, int, const char *);
void group_movetogroup(struct client_ctx *, int);
void group_only(struct screen_ctx *, int);
void group_sticky(struct client_ctx *);
void group_sticky_toggle_enter(struct client_ctx *);
void group_sticky_toggle_exit(struct client_ctx *);
void group_close(struct screen_ctx *, int);
int group_restore(struct client_ctx *);
void group_show(struct group_ctx *);
void group_toggle(struct screen_ctx *, int);
void group_toggle_all(struct screen_ctx *);
void group_toggle_membership(struct client_ctx *);
void group_update_names(struct screen_ctx *);
void search_match_client(struct menu_q *, struct menu_q *,
char *);
void search_match_cmd(struct menu_q *, struct menu_q *,
char *);
void search_match_exec(struct menu_q *, struct menu_q *,
char *);
void search_match_group(struct menu_q *, struct menu_q *,
char *);
void search_match_path(struct menu_q *, struct menu_q *,
char *);
void search_match_text(struct menu_q *, struct menu_q *,
char *);
void search_match_wm(struct menu_q *, struct menu_q *,
char *);
void search_print_client(struct menu *, int);
void search_print_cmd(struct menu *, int);
void search_print_group(struct menu *, int);
void search_print_text(struct menu *, int);
void search_print_wm(struct menu *, int);
XineramaScreenInfo *screen_find_xinerama(struct screen_ctx *, int, int);
struct screen_ctx *screen_fromroot(Window);
void screen_init_xinerama(struct screen_ctx *);
void screen_update_geometry(struct screen_ctx *, int, int);
struct region_ctx *region_find(struct screen_ctx *, int, int);
void screen_assert_clients_within(struct screen_ctx *);
struct geom screen_area(struct screen_ctx *, int, int, int);
struct screen_ctx *screen_find(Window);
void screen_init(int);
void screen_prop_win_create(struct screen_ctx *, Window);
void screen_prop_win_destroy(struct screen_ctx *);
void screen_prop_win_draw(struct screen_ctx *,
const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
void screen_update_geometry(struct screen_ctx *);
void screen_updatestackingorder(struct screen_ctx *);
void kbfunc_client_cycle(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_freeze(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_grouptoggle(struct client_ctx *,
union arg *);
void kbfunc_client_hide(struct client_ctx *, union arg *);
void kbfunc_client_hmaximize(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_maximize(struct client_ctx *,
union arg *);
void kbfunc_client_movetogroup(struct client_ctx *,
union arg *);
void kbfunc_client_nogroup(struct client_ctx *,
union arg *);
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_vmaximize(struct client_ctx *,
union arg *);
void kbfunc_cmdexec(struct client_ctx *, union arg *);
void kbfunc_exec(struct client_ctx *, union arg *);
void kbfunc_lock(struct client_ctx *, union arg *);
void kbfunc_menu_search(struct client_ctx *, union arg *);
void kbfunc_moveresize(struct client_ctx *, union arg *);
void kbfunc_quit_wm(struct client_ctx *, union arg *);
void kbfunc_reload(struct client_ctx *, union arg *);
void kbfunc_ssh(struct client_ctx *, union arg *);
void kbfunc_term(struct client_ctx *, union arg *);
void mousefunc_menu_cmd(struct client_ctx *, void *);
void mousefunc_menu_group(struct client_ctx *, void *);
void mousefunc_menu_unhide(struct client_ctx *, void *);
void mousefunc_window_grouptoggle(struct client_ctx *,
void *);
void mousefunc_window_hide(struct client_ctx *, void *);
void mousefunc_window_lower(struct client_ctx *, void *);
void mousefunc_window_move(struct client_ctx *, void *);
void mousefunc_window_raise(struct client_ctx *, void *);
void mousefunc_window_resize(struct client_ctx *, void *);
void kbfunc_cwm_status(void *, struct cargs *);
void kbfunc_ptrmove(void *, struct cargs *);
void kbfunc_client_snap(void *, struct cargs *);
void kbfunc_client_move(void *, struct cargs *);
void kbfunc_client_resize(void *, struct cargs *);
void kbfunc_client_close(void *, struct cargs *);
void kbfunc_client_lower(void *, struct cargs *);
void kbfunc_client_raise(void *, struct cargs *);
void kbfunc_client_hide(void *, struct cargs *);
void kbfunc_client_toggle_freeze(void *, struct cargs *);
void kbfunc_client_toggle_sticky(void *, struct cargs *);
void kbfunc_client_toggle_fullscreen(void *,
struct cargs *);
void kbfunc_client_toggle_maximize(void *, struct cargs *);
void kbfunc_client_toggle_hmaximize(void *, struct cargs *);
void kbfunc_client_toggle_vmaximize(void *, struct cargs *);
void kbfunc_client_htile(void *, struct cargs *);
void kbfunc_client_vtile(void *, struct cargs *);
void kbfunc_client_cycle(void *, struct cargs *);
void kbfunc_client_toggle_group(void *, struct cargs *);
void kbfunc_client_movetogroup(void *, struct cargs *);
void kbfunc_group_toggle(void *, struct cargs *);
void kbfunc_group_only(void *, struct cargs *);
void kbfunc_group_last(void *, struct cargs *);
void kbfunc_group_close(void *, struct cargs *);
void kbfunc_group_cycle(void *, struct cargs *);
void kbfunc_group_toggle_all(void *, struct cargs *);
void kbfunc_menu_client(void *, struct cargs *);
void kbfunc_menu_cmd(void *, struct cargs *);
void kbfunc_menu_group(void *, struct cargs *);
void kbfunc_menu_wm(void *, struct cargs *);
void kbfunc_menu_exec(void *, struct cargs *);
void kbfunc_menu_ssh(void *, struct cargs *);
void kbfunc_client_menu_label(void *, struct cargs *);
void kbfunc_exec_cmd(void *, struct cargs *);
void kbfunc_exec_lock(void *, struct cargs *);
void kbfunc_exec_term(void *, struct cargs *);
struct menu *menu_filter(struct screen_ctx *, struct menu_q *,
char *, char *, int,
const char *, const char *, int,
void (*)(struct menu_q *, struct menu_q *, char *),
void (*)(struct menu *, int));
void menu_init(struct screen_ctx *);
void menuq_add(struct menu_q *, void *, const char *, ...)
__attribute__((__format__ (printf, 3, 4)));
void menuq_clear(struct menu_q *);
int parse_config(const char *, struct conf *);
void conf_bindname(struct conf *, char *, char *);
void conf_autogroup(struct conf *, int, const char *,
const char *);
int conf_bind_key(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 *);
void conf_cmd_add(struct conf *, char *, char *, int);
void conf_color(struct conf *, struct screen_ctx *);
void conf_font(struct conf *, struct screen_ctx *);
void conf_gap(struct conf *, struct screen_ctx *);
void conf_grab(struct conf *, struct keybinding *);
void conf_grab_mouse(struct client_ctx *);
void conf_cmd_add(struct conf *, const char *,
const char *);
void conf_wm_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_mousebind(struct conf *, char *, char *);
void conf_reload(struct conf *);
void conf_setup(struct conf *, const char *);
void conf_ungrab(struct conf *, struct keybinding *);
void conf_ignore(struct conf *, const char *);
void conf_screen(struct screen_ctx *);
void conf_group(struct screen_ctx *);
int font_ascent(struct screen_ctx *);
int font_descent(struct screen_ctx *);
void font_draw(struct screen_ctx *, const char *, int,
Drawable, int, int);
u_int font_height(struct screen_ctx *);
void font_init(struct screen_ctx *, const char *);
int font_width(struct screen_ctx *, const char *, int);
XftFont *font_make(struct screen_ctx *, const char *);
void xev_process(void);
void xev_loop(void);
int xu_get_prop(Window, Atom, Atom, long, unsigned char **);
int xu_get_strprop(Window, Atom, char **);
void xu_ptr_get(Window, int *, int *);
void xu_ptr_set(Window, int, int);
void xu_get_wm_state(Window, long *);
void xu_set_wm_state(Window, long);
void xu_send_clientmsg(Window, Atom, Time);
void xu_xorcolor(XftColor, XftColor, XftColor *);
void xu_btn_grab(Window, int, u_int);
void xu_btn_ungrab(Window, int, u_int);
void xu_configure(struct client_ctx *);
void xu_freecolor(struct screen_ctx *, unsigned long);
void xu_getatoms(void);
unsigned long xu_getcolor(struct screen_ctx *, char *);
int xu_getprop(Window, Atom, Atom, long, u_char **);
int xu_getstate(struct client_ctx *, int *);
int xu_getstrprop(Window, Atom, char **);
void xu_key_grab(Window, int, int);
void xu_key_ungrab(Window, int, int);
void xu_ptr_getpos(Window, int *, int *);
int xu_ptr_grab(Window, int, Cursor);
int xu_ptr_regrab(int, Cursor);
void xu_ptr_setpos(Window, int, int);
void xu_ptr_ungrab(void);
void xu_sendmsg(Window, Atom, long);
void xu_setstate(struct client_ctx *, int);
void xu_setwmname(struct screen_ctx *);
void xu_atom_init(void);
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_desktop_viewport(struct screen_ctx *);
void xu_ewmh_net_workarea(struct screen_ctx *);
void xu_ewmh_net_client_list(struct screen_ctx *);
void xu_ewmh_net_client_list_stacking(struct screen_ctx *);
void xu_ewmh_net_active_window(struct screen_ctx *, Window);
void xu_ewmh_net_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 *);
int xu_ewmh_get_net_wm_desktop(struct client_ctx *, long *);
void xu_ewmh_set_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 *);
char *u_argv(char * const *);
void u_exec(char *);
void u_spawn(char *);
void log_debug(int, const char *, const char *, ...)
__attribute__((__format__ (printf, 3, 4)))
__attribute__((__nonnull__ (3)));
void *xcalloc(size_t, size_t);
void xfree(void *);
void *xmalloc(size_t);
void *xreallocarray(void *, size_t, size_t);
char *xstrdup(const char *);
/* Externs */
extern Display *X_Dpy;
extern Cursor Cursor_default;
extern Cursor Cursor_move;
extern Cursor Cursor_normal;
extern Cursor Cursor_question;
extern Cursor Cursor_resize;
extern struct screen_ctx_q Screenq;
extern struct client_ctx_q Clientq;
extern struct conf Conf;
extern int HasXinerama, HasRandr, Randr_ev;
#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];
int xasprintf(char **, const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int xvasprintf(char **, const char *, va_list)
__attribute__((__nonnull__ (2)));
#endif /* _CALMWM_H_ */

1314
client.c

File diff suppressed because it is too large Load Diff

1092
conf.c

File diff suppressed because it is too large Load Diff

242
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: September 25 2010 $
.Dd $Mdocdate$
.Dt CWM 1
.Os
.Sh NAME
@ -23,24 +23,54 @@
.Sh SYNOPSIS
.\" For a program: program [-abc] file ...
.Nm cwm
.Op Fl nv
.Op Fl c Ar file
.Op Fl d Ar display
.Sh DESCRIPTION
.Nm
is a window manager for X11 which contains many features that
concentrate on the efficiency and transparency of window management.
.Nm
also aims to maintain the simplest and most pleasant aesthetic.
concentrate on the efficiency and transparency of window management,
while maintaining the simplest and most pleasant aesthetic.
.Pp
The following notation is used throughout this page:
The options are as follows:
.Bl -tag -width Ds
.It Fl c Ar file
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.
.It Fl n
Configtest mode.
Only check the configuration file for validity.
.It Fl v
Verbose mode.
Multiple
.Fl v
options increase the verbosity.
.El
.Pp
.Nm
actions are initiated either via key or mouse bindings.
The following notations are used throughout this page:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic C
Control.
Control key.
.It Ic M
Meta.
Meta key.
.It Ic S
Shift.
Shift key.
.It Ic 4
Mod4 (windows) key.
.It Ic M1
Left mouse button.
.It Ic M2
@ -49,11 +79,7 @@ Middle mouse button.
Right mouse button.
.El
.Pp
.Nm
is very simple in its use.
Most of the actions are initiated via key bindings.
The current key bindings are described below;
their functionality is described in more detail later.
The default key bindings are:
.Pp
.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact
.It Ic CM-Return
@ -66,9 +92,9 @@ Hide current window.
Lower current window.
.It Ic M-Up
Raise current window.
.It Ic M-/
.It Ic M-slash
Search for windows.
.It Ic C-/
.It Ic C-slash
Search for applications.
.It Ic CM-n
Label current window.
@ -77,11 +103,11 @@ Cycle through currently visible windows.
.It Ic MS-Tab
Reverse cycle through currently visible windows.
.It Ic CM-x
Delete current window.
Close current window.
.It Ic CM-[n]
Select group n, where n is 1-9.
.It Ic CM-0
Select all groups.
Toggle visibility of group n, where n is 1-9.
.It Ic CM-a
Toggle visibility of all groups.
.It Ic CM-g
Toggle group membership of current window.
.It Ic M-Right
@ -90,17 +116,31 @@ Cycle through active groups.
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.
.It Ic CM-=
Toggle full-screen mode of current window.
.It Ic CM-m
Toggle maximization of current window.
.It Ic CM-equal
Toggle vertical maximization of current window.
.It Ic CMS-=
.It Ic CMS-equal
Toggle horizontal maximization of current window.
.It Ic M-?
.It Ic M-[hjkl]
Move window by a small amount.
.It Ic MS-[hjkl]
Move window by a large amount; see
.Xr cwmrc 5 .
.It Ic CM-[hjkl]
Resize window by a small amount.
.It Ic CMS-[hjkl]
Resize window by a large amount; see
.Xr cwmrc 5 .
.It Ic M-question
Spawn
.Dq exec program
dialog.
.It Ic M-.
.It Ic M-period
Spawn
.Dq ssh to
dialog.
@ -112,19 +152,16 @@ will be executed via the configured terminal emulator.
.It Ic CM-w
Spawn
.Dq exec WindowManager
dialog; allows you to switch from
.Nm
to another window manager without restarting the X server.
menu, allowing a switch to another window manager.
.It Ic CMS-r
Reload configuration.
Restart.
.It Ic CMS-q
Quit
.Nm .
Quit.
.El
.Pp
The mouse bindings are also important, they are:
The default mouse bindings are:
.Pp
.Bl -tag -width Ds -offset indent -compact
.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact
.It Ic M-M1
Move current window.
.It Ic CM-M1
@ -137,47 +174,33 @@ Lower current window.
Hide current window.
.El
.Pp
The options for
.Nm
are as follows:
.Bl -tag -width Ds
.It Fl c Ar file
Specify the config file to use. Defaults to
.Pa ~/.cwmrc .
.It Fl d Ar display
Specify the display to use.
.El
.Sh POINTER MOVEMENT
The pointer can be moved with the use of the keyboard through bindings.
.Ic C-[Up|Down|Left|Right]
moves the pointer a small amount, while
.Ic CS-[Up|Down|Left|Right]
moves the pointer a larger amount.
For example, to move the pointer to the left by a small amount,
press
.Ic C-Left .
To move the pointer down by a larger amount, press
.Ic CS-Down .
.Sh WINDOW MOVEMENT AND RESIZING
.Nm
windows can be moved with the use of the keyboard through
.Cm vi Ns -like
bindings.
.Ic M-[hjkl]
moves the current window a small amount, while
.Ic MS-[hjkl]
moves the current window a larger amount.
For example, to move the current window to the left a small amount, press
.Ic M-h .
To move the current window down by a larger amount, press
.Ic MS-j .
The following key bindings may be used to navigate
search and exec dialogs:
.Pp
Similarly, windows may be resized with the same key bindings with the addition
of the Control key.
.Ic CM-[hjkl]
resizes the window a small amount and
.Ic CMS-[hjkl]
resizes by a larger increment.
.Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact
.It Ic [Return]
Select item.
.It Ic [Down], C-s No or Ic M-j
Next item.
.It Ic [Up], C-r No or Ic M-k
Previous item.
.It Ic [Backspace] No or Ic C-h
Backspace.
.It Ic C-u
Clear input.
.It Ic C-a
List all available items.
.It Ic [Esc]
Cancel.
.El
.Pp
.Nm
rereads its configuration file when it receives a hangup signal,
.Dv SIGHUP ,
by executing itself with the name and arguments with which it was started.
This is equivalent to the
.Ar restart
function.
.Sh SEARCH
.Nm
features the ability to search for windows by their current title,
@ -190,37 +213,21 @@ keeps a history of the 5 previous titles of a window.
When searching, the leftmost character of the result list may show a
flag:
.Pp
.Bl -tag -width 10n -offset indent -compact
.Bl -tag -width Ds -offset indent -compact
.It !
The window is the currently focused window.
Window is currently focused.
.It &
The window is hidden.
.El
.Pp
The following key bindings may be used to navigate the result list:
.Pp
.Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact
.It Ic [Down], C-s No or Ic M-j
Select the next window in the list.
.It Ic [Up], C-r No or Ic M-k
Select the previous window in the list.
.It Ic [Backspace] No or Ic C-h
Backspace.
.It Ic C-u
Clear the input.
.It Ic [Return]
Focus the selected window.
.It Ic [Esc]
Cancel.
.It Ic C-a
Whenever there are no matching windows, list every window.
Window is hidden.
.El
.Sh APPLICATIONS
.Nm
manages a list of applications defined with the
.Cm command
configuration option.
.Sh GROUPS
.Nm
has the ability to group windows together, and use the groups to
perform operations on the entire group instead of just one window.
Currently, the only operation that is supported is to hide and unhide
the grouped windows.
Together with the
.Pa sticky
option, this can be used to emulate virtual desktops.
@ -232,20 +239,19 @@ and a red border will be shown on those just removed.
.Sh MENUS
Menus are recalled by clicking the mouse on the root window:
.Pp
.Bl -tag -width 10n -offset indent -compact
.Bl -tag -width Ds -offset indent -compact
.It Ic M1
Show list of currently hidden windows.
Clicking on an item will unhide that window.
Show list of currently defined windows.
Selecting an item will warp to that window, unhiding it if necessary.
.It Ic M2
Show list of currently defined groups.
Clicking on an item will hide/unhide that group.
Selecting an item will hide/unhide that group.
.It Ic M3
Show list of applications as defined in
.Pa ~/.cwmrc .
Clicking on an item will spawn that application.
Show list of applications as defined in the configuration file.
Selecting an item will spawn that application.
.El
.Sh ENVIRONMENT
.Bl -tag -width "DISPLAYXXX"
.Bl -tag -width "DISPLAYXXX" -compact
.It DISPLAY
.Nm
starts on this display unless the
@ -253,23 +259,14 @@ starts on this display unless the
option is given.
.El
.Sh FILES
.Bl -tag -width Ds
.Bl -tag -width "~/.cwmrcXX" -compact
.It Pa ~/.cwmrc
Default
.Nm
configuration file.
.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
@ -280,3 +277,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

650
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 8 2011 $
.Dd $Mdocdate$
.Dt CWMRC 5
.Os
.Sh NAME
@ -37,86 +37,114 @@ 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 windowname,windowclass
Control automatic window grouping, based on the name and/or class
properties, where
.Bl -tag -width Ds
.It Ic autogroup Ar group Oo Ar windowname , Oc Ns Ar windowclass
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.
.It Ic bind-key Ar key function
Bind or rebind key
.Ar key
to
.Ar function .
The modifier keys come first, followed by a
.Sq - .
.Sq - ,
then a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h .
.Pp
The following modifiers are recognised:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It C
The Control key.
.It M
The Meta key.
.It S
The Shift key.
.It 4
The Mod4 key (normally the windows key).
.It Ic C
Control key.
.It Ic M
Meta key.
.It Ic S
Shift key.
.It Ic 4
Mod4 (windows) key.
.It Ic 5
Mod5 (AltGr) key.
.El
.Pp
The
.Sq -
should be followed by either a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h ,
or a numerical keycode value enclosed in
.Dq [] .
The
.Ar command
.Ar function
may either be one from the
.Sx BIND COMMAND LIST
.Sx BIND FUNCTION LIST
(see below) or the command line that is to be executed.
.It Ic bind-mouse Ar button function
Bind or rebind button
.Ar button
to
.Ar function .
The modifier keys come first, followed by a
.Sq - ,
then the button number.
.Pp
A special
.Ar command
keyword
.Dq unmap
can be used to remove the named keybinding.
This can be used to remove a binding which conflicts with an
application.
The same modifiers are recognised as for
.Ar key
in
.Nm bind-key .
.Pp
The following buttons are recognised:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic 1
Left mouse button.
.It Ic 2
Middle mouse button.
.It Ic 3
Right mouse button.
.It Ic 4
Scroll up mouse button.
.It Ic 5
Scroll down mouse button.
.El
.Pp
The
.Ar function
may be taken from the
.Sx BIND FUNCTION LIST
(see below) or the command line that is to be executed.
.It Ic borderwidth Ar pixels
Set the window border width to
.Ar pixels .
.Pp
.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.
.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.
.It Ic color ungroupborder Ar color
Set the color of the border while ungrouping a window.
.Pp
.It Ic command Ar name path
Every
.Ar name
@ -124,7 +152,7 @@ entry is shown in the application menu.
When selected, the defined
.Ar path
is executed with
.Xr execve 2 .
.Xr execvp 3 .
.Pp
The
.Ar name
@ -134,19 +162,17 @@ 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
.Xr xlock 1 ,
respectively.
.Pp
.It Ic fontname Ar font
Change the default
.Ar font
for
.Xr Xft 3 .
.Pp
.It Ic gap Ar top bottom left right
Define a
.Dq gap
@ -157,67 +183,277 @@ This
can be used for applications such as
.Xr xclock 1 ,
where the user may wish to remain visible.
.Pp
.It Ic htile Ar percent
Set the percentage of screen the master window should occupy
after calling
.Ic window-htile .
If set to 0, the horizontal size of the master window will
remain unchanged.
The default is 50.
.It Ic ignore Ar windowname
Ignore, and do not warp to, windows with the name
.Ar windowname
when drawing borders and cycling through windows.
.Pp
.It Ic mousebind Ar buttons command
Cause the creation of a mouse binding, or replacement of a default
mouse binding.
The modifier keys come first, followed by a
.Sq - .
.Pp
The following modifiers are recognised:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It C
The Control key.
.It M
The Meta key.
.It S
The Shift key.
.It 4
The Mod4 key (normally the windows key).
.El
.Pp
The
.Sq -
should be followed by number:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It 1
Left mouse button.
.It 2
Middle mouse button.
.It 3
Right mouse button.
.El
.Pp
The
.Ar command
may be taken from the
.Sx MOUSEBIND COMMAND LIST
(see below).
.Pp
.It Ic moveamount Ar pixels
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.
By enabling sticky group mode,
.Xr cwm 1
will assign new windows to the currently selected group.
.It Ic unbind-key Ar key
Unbind function bound to
.Ar key .
A special
.Ar key
keyword
.Dq all
can be used to unbind all keys.
.It Ic unbind-mouse Ar button
Unbind function bound to
.Ar button .
A special
.Ar button
keyword
.Dq all
can be used to unbind all buttons.
.It Ic vtile Ar percent
Set the percentage of screen the master window should occupy
after calling
.Ic window-vtile .
If set to 0, the vertical size of the master window will
remain unchanged.
The default is 50.
.It Ic wm Ar name path
Every
.Ar name
entry is shown in the wm menu.
When selected, the window manager is replaced by
.Ar path .
.El
.Sh EXAMPLE CONFIGURATION
.Sh BIND FUNCTION LIST
.Bl -tag -width 23n -compact
.It restart
Restart the running
.Xr cwm 1 .
.It quit
Quit
.Xr cwm 1 .
.It terminal
Spawn a new terminal.
.It lock
Lock the screen.
.It menu-window
Launch window search menu.
.It menu-window-hidden
Launch hidden window search menu.
.It menu-cmd
Launch application search menu.
.It menu-group
Launch group search menu.
.It menu-exec
Launch
.Dq exec program
menu.
.It menu-exec-wm
Launch
.Dq exec WindowManager
menu.
.It menu-ssh
Launch
.Dq ssh
menu.
.It group-toggle-[n]
Toggle visibility of group n, where n is 1-9.
.It group-only-[n]
Show only group n, where n is 1-9, hiding other groups.
.It group-last
Show only the previously active group.
.It group-close-[n]
Close all windows in group n, where n is 1-9.
.It group-toggle-all
Toggle visibility of all groups.
.It window-group
Toggle group membership of current window.
.It window-movetogroup-[n]
Hide current window from display and move to group n, where n is 1-9.
.It group-cycle
Forward cycle through groups.
.It group-rcycle
Reverse cycle through groups.
.It window-cycle
Forward cycle through windows.
.It window-rcycle
Reverse cycle through windows.
.It window-cycle-ingroup
Forward cycle through windows in current group.
.It window-rcycle-ingroup
Reverse cycle through windows in current group.
.It window-close
Close current window.
.It window-hide
Hide current window.
.It window-lower
Lower current window.
.It window-raise
Raise current window.
.It window-menu-label
Label current window.
.It window-freeze
Freeze current window geometry.
.It window-stick
Stick current window to all groups (same as assigning to nogroup).
.It window-fullscreen
Full-screen current window (gap + border removed).
.It window-maximize
Maximize current window (gap + border honored).
.It window-vmaximize
Vertically maximize current window (gap + border honored).
.It window-hmaximize
Horizontally maximize current window (gap + border honored).
.It window-htile
Current window is placed at the top of the screen, maximized
horizontally and resized to
.Ar htile
(default half) of the vertical screen space.
Other windows in its group share remaining screen space.
.It window-vtile
Current window is placed on the left of the screen, maximized vertically
and resized to
.Ar vtile
(default half) of the horizontal screen space.
Other windows in its group share remaining screen space.
.It window-move
Move current window.
.It window-resize
Resize current window.
.It window-move-up
Move window
.Ar moveamount
pixels up.
.It window-move-down
Move window
.Ar moveamount
pixels down.
.It window-move-right
Move window
.Ar moveamount
pixels right.
.It window-move-left
Move window
.Ar moveamount
pixels left.
.It window-move-up-big
Move window 10 times
.Ar moveamount
pixels up.
.It window-move-down-big
Move window 10 times
.Ar moveamount
pixels down.
.It window-move-right-big
Move window 10 times
.Ar moveamount
pixels right.
.It window-move-left-big
Move window 10 times
.Ar moveamount
pixels left.
.It window-resize-up
Resize window
.Ar moveamount
pixels up.
.It window-resize-down
Resize window
.Ar moveamount
pixels down.
.It window-resize-right
Resize window
.Ar moveamount
pixels right.
.It window-resize-left
Resize window
.Ar moveamount
pixels left.
.It window-resize-up-big
Resize window 10 times
.Ar moveamount
pixels up.
.It window-resize-down-big
Resize window 10 times
.Ar moveamount
pixels down.
.It window-resize-right-big
Resize window 10 times
.Ar moveamount
pixels right.
.It window-resize-left-big
Resize window 10 times
.Ar moveamount
pixels left.
.It window-snap-up
Snap window to top edge.
.It window-snap-down
Snap window to bottom edge.
.It window-snap-right
Snap window to right edge.
.It window-snap-left
Snap window to left edge.
.It window-snap-up-right
Snap window to top-right corner.
.It window-snap-up-left
Snap window to top-left corner.
.It window-snap-down-right
Snap window to bottom-right corner.
.It window-snap-down-left
Snap window to bottom-left corner.
.It pointer-move-up
Move pointer
.Ar moveamount
pixels up.
.It pointer-move-down
Move pointer
.Ar moveamount
pixels down.
.It pointer-move-right
Move pointer
.Ar moveamount
pixels right.
.It pointer-move-left
Move pointer
.Ar moveamount
pixels left.
.It pointer-move-up-big
Move pointer 10 times
.Ar moveamount
pixels up.
.It pointer-move-down-big
Move pointer 10 times
.Ar moveamount
pixels down.
.It pointer-move-right-big
Move pointer 10 times
.Ar moveamount
pixels right.
.It pointer-move-left-big
Move pointer 10 times
.Ar moveamount
pixels left.
.El
.Sh FILES
.Bl -tag -width "~/.cwmrcXXX" -compact
.It Pa ~/.cwmrc
Default
.Xr cwm 1
configuration file.
.El
.Sh EXAMPLES
.Bd -literal
# Set default Xft(3) font
fontname "sans-serif:pixelsize=14:bold"
@ -240,215 +476,25 @@ ignore xwi
ignore xapm
ignore xclock
# Keybindings
bind CM-r label
bind CS-Return "xterm -e top"
bind 4-o unmap
bind M-1 grouponly1
bind M-2 grouponly2
bind M-3 grouponly3
bind MS-1 movetogroup1
bind MS-2 movetogroup2
bind MS-3 movetogroup3
# Key bindings
bind-key CM-r window-menu-label
bind-key CS-Return "xterm -e top"
bind-key C4-equal window-vmaximize
bind-key C4S-equal window-hmaximize
bind-key M-1 group-only-1
bind-key M-2 group-only-2
bind-key M-3 group-only-3
bind-key MS-1 window-movetogroup-1
bind-key MS-2 window-movetogroup-2
bind-key MS-3 window-movetogroup-3
unbind-key 4-o
unbind-key CM-equal
unbind-key CMS-equal
# Mousebindings
mousebind M-2 window_lower
mousebind M-3 window_resize
# Mouse bindings
bind-mouse M-2 window-lower
bind-mouse M-3 window-resize
.Ed
.Sh BIND COMMAND LIST
.Bl -tag -width 18n -compact
.It reload
Reload configuration.
.It quit
Quit
.Xr cwm 1 .
.It terminal
Spawn a new terminal.
.It lock
Lock the screen.
.It search
Launch window search menu.
.It menusearch
Launch application search menu.
.It exec
Launch
.Dq exec program
menu.
.It exec_wm
Launch
.Dq exec WindowManager
menu.
.It ssh
Launch
.Dq ssh
menu.
.It group[n]
Select group n, where n is 1-9.
.It grouponly[n]
Like
.Ar group[n]
but also hides the other groups.
.It nogroup
Select all groups.
.It grouptoggle
Toggle group membership of current window.
.It movetogroup[n]
Hide current window from display and move to group n, where n is 1-9.
.It cyclegroup
Forward cycle through groups.
.It rcyclegroup
Reverse cycle through groups.
.It cycle
Forward cycle through windows.
.It rcycle
Reverse cycle through windows.
.It cycleingroup
Forward cycle through windows in current group.
.It rcycleingroup
Reverse cycle through windows in current group.
.It delete
Delete current window.
.It hide
Hide current window.
.It lower
Lower current window.
.It raise
Raise current window.
.It label
Label current window.
.It freeze
Freeze current window geometry.
.It maximize
Maximize current window full-screen.
.It vmaximize
Maximize current window vertically.
.It hmaximize
Maximize current window horizontally.
.It moveup
Move window
.Ar moveamount
pixels up.
.It movedown
Move window
.Ar moveamount
pixels down.
.It moveright
Move window
.Ar moveamount
pixels right.
.It moveleft
Move window
.Ar moveamount
pixels left.
.It bigmoveup
Move window 10 times
.Ar moveamount
pixels up.
.It bigmovedown
Move window 10 times
.Ar moveamount
pixels down.
.It bigmoveright
Move window 10 times
.Ar moveamount
pixels right.
.It bigmoveleft
Move window 10 times
.Ar moveamount
pixels left.
.It resizeup
Resize window
.Ar moveamount
pixels up.
.It resizedown
Resize window
.Ar moveamount
pixels down.
.It resizeright
Resize window
.Ar moveamount
pixels right.
.It resizeleft
Resize window
.Ar moveamount
pixels left.
.It bigresizeup
Resize window 10 times
.Ar moveamount
pixels up.
.It bigresizedown
Resize window 10 times
.Ar moveamount
pixels down.
.It bigresizeright
Resize window 10 times
.Ar moveamount
pixels right.
.It bigresizeleft
Resize window 10 times
.Ar moveamount
pixels left.
.It ptrmoveup
Move pointer
.Ar moveamount
pixels up.
.It ptrmovedown
Move pointer
.Ar moveamount
pixels down.
.It ptrmoveright
Move pointer
.Ar moveamount
pixels right.
.It ptrmoveleft
Move pointer
.Ar moveamount
pixels left.
.It bigptrmoveup
Move pointer 10 times
.Ar moveamount
pixels up.
.It bigptrmovedown
Move pointer 10 times
.Ar moveamount
pixels down.
.It bigptrmoveright
Move pointer 10 times
.Ar moveamount
pixels right.
.It bigptrmoveleft
Move pointer 10 times
.Ar moveamount
pixels left.
.El
.Sh MOUSEBIND COMMAND LIST
.Bl -tag -width 18n -compact
.It window_move
Move current window.
.It window_resize
Resize current window.
.It window_lower
Lower current window.
.It window_raise
Raise current window.
.It window_hide
Hide current window.
.It window_grouptoggle
Toggle group membership of current window.
.It menu_group
Launch group list.
.It menu_unhide
Launch hidden window list.
.It menu_cmd
Launch command list.
.El
.Sh FILES
.Bl -tag -width "~/.cwmrcXXX" -compact
.It Pa ~/.cwmrc
default
.Xr cwm 1
configuration file
.El
.Sh SEE ALSO
.Xr cwm 1
.Sh HISTORY

105
font.c
View File

@ -1,105 +0,0 @@
/*
* calmwm - the calm window manager
*
* 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.
*
* $OpenBSD$
*/
#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->font->height + 1);
}
void
font_init(struct screen_ctx *sc, const char *color)
{
if (sc->xftdraw)
XftDrawDestroy(sc->xftdraw);
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 (sc->xftcolor.pixel)
XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which), &sc->xftcolor);
if (!XftColorAllocName(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which), color, &sc->xftcolor))
errx(1, "XftColorAllocName");
}
int
font_width(struct screen_ctx *sc, const char *text, int len)
{
XGlyphInfo extents;
XftTextExtentsUtf8(X_Dpy, sc->font, (const FcChar8*)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);
XftDrawStringUtf8(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);
}

661
group.c
View File

@ -19,113 +19,96 @@
* $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 struct group_ctx *group_next(struct group_ctx *);
static struct group_ctx *group_prev(struct group_ctx *);
static void group_restack(struct group_ctx *);
static void group_set_active(struct group_ctx *);
const char *shortcut_to_name[] = {
"nogroup", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine"
};
static void
group_add(struct group_ctx *gc, struct client_ctx *cc)
void
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 ((gc != NULL) && (gc->num == 0))
gc = NULL;
no = gc->shortcut - 1;
cc->gc = gc;
if (cc->group == gc)
return;
if (cc->group != NULL)
TAILQ_REMOVE(&cc->group->clients, 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_set_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 screen_ctx *sc = gc->sc;
struct client_ctx *cc;
screen_updatestackingorder(sc);
screen_updatestackingorder(gc->sc);
gc->nhidden = 0;
gc->highstack = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
client_hide(cc);
gc->nhidden++;
if (cc->stackingorder > gc->highstack)
gc->highstack = cc->stackingorder;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY) &&
!(cc->flags & CLIENT_HIDDEN))
client_hide(cc);
}
gc->hidden = 1; /* XXX: equivalent to gc->nhidden > 0 */
}
void
group_show(struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY) &&
(cc->flags & CLIENT_HIDDEN))
client_show(cc);
}
group_restack(gc);
group_set_active(gc);
}
static void
group_show(struct screen_ctx *sc, struct group_ctx *gc)
group_restack(struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
Window *winlist;
u_int i;
int lastempty = -1;
int i, lastempty = -1;
int nwins = 0, highstack = 0;
gc->highstack = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
if (cc->stackingorder > gc->highstack)
gc->highstack = cc->stackingorder;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (cc->stackingorder > highstack)
highstack = cc->stackingorder;
}
winlist = (Window *) xcalloc(sizeof(*winlist), (gc->highstack + 1));
winlist = xreallocarray(NULL, (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, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
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) {
@ -135,81 +118,33 @@ 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, const char *name)
{
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(name);
gc->num = num;
TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
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);
}
/* 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_set_active(gc);
}
void
group_make_autogroup(struct conf *conf, char *val, int no)
group_set_active(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
@ -218,332 +153,226 @@ group_movetogroup(struct client_ctx *cc, int idx)
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_movetogroup: index out of range (%d)", idx);
gc = &sc->groups[idx];
if (cc->group == gc)
return;
if (gc->hidden) {
client_hide(cc);
gc->nhidden++;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
if (cc->gc == gc)
return;
if (gc->num != 0 && group_holds_only_hidden(gc))
client_hide(cc);
group_assign(gc, cc);
}
}
group_add(gc, cc);
}
/*
* Colouring for groups upon add/remove.
*/
void
group_sticky_toggle_enter(struct client_ctx *cc)
group_toggle_membership(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
struct group_ctx *gc = sc->group_active;
gc = sc->group_active;
if (gc == cc->group) {
group_remove(cc);
cc->highlight = CLIENT_HIGHLIGHT_UNGROUP;
if (cc->gc == gc) {
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)
{
cc->highlight = 0;
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 screen_ctx *sc = gc->sc;
struct client_ctx *cc;
int same = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
if (gc->hidden == ((cc->flags & CLIENT_HIDDEN) ? 1 : 0))
same++;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY))
return 0;
}
if (same == 0)
gc->hidden = !gc->hidden;
return 1;
}
void
group_hidetoggle(struct screen_ctx *sc, int idx)
int
group_holds_only_hidden(struct group_ctx *gc)
{
struct group_ctx *gc;
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_hidetoggle: index out of range (%d)", idx);
gc = &sc->groups[idx];
group_fix_hidden_state(gc);
if (gc->hidden)
group_show(sc, gc);
else {
group_hide(sc, gc);
/* XXX wtf? */
if (TAILQ_EMPTY(&gc->clients))
group_setactive(sc, idx);
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY)))
return 0;
}
return 1;
}
void
group_only(struct screen_ctx *sc, int idx)
{
int i;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(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]);
else
group_hide(sc, &sc->groups[i]);
}
}
/*
* Cycle through active groups. If none exist, then just stay put.
*/
void
group_cycle(struct screen_ctx *sc, int flags)
{
struct group_ctx *gc, *showgroup = NULL;
assert(sc->group_active != NULL);
gc = sc->group_active;
for (;;) {
gc = (flags & CWM_RCYCLE) ? TAILQ_PREV(gc, group_ctx_q,
entry) : TAILQ_NEXT(gc, entry);
if (gc == NULL)
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)
showgroup = gc;
else if (!gc->hidden)
group_hide(sc, gc);
}
if (showgroup == NULL)
return;
group_hide(sc, sc->group_active);
if (showgroup->hidden)
group_show(sc, 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);
if (sc->group_last != sc->group_active)
sc->group_last = sc->group_active;
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)
(void)snprintf(mi->text, sizeof(mi->text), "%d: [%s]",
gc->shortcut, sc->group_names[i]);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
group_show(gc);
else
(void)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_hide(gc);
}
}
void
group_alltoggle(struct screen_ctx *sc)
group_toggle(struct screen_ctx *sc, int idx)
{
int i;
for (i = 0; i < CALMWM_NGROUPS; i++) {
if (sc->group_hideall)
group_show(sc, &sc->groups[i]);
else
group_hide(sc, &sc->groups[i]);
}
sc->group_hideall = (!sc->group_hideall);
}
void
group_autogroup(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct autogroupwin *aw;
struct group_ctx *gc;
int no = -1;
long *grpno;
if (cc->app_class == NULL || cc->app_name == NULL)
return;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
if (group_holds_only_hidden(gc))
group_show(gc);
else
group_hide(gc);
}
}
}
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;
void
group_toggle_all(struct screen_ctx *sc)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->hideall)
group_show(gc);
else
no = *grpno + 1;
XFree(grpno);
} 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;
group_hide(gc);
}
sc->hideall = !sc->hideall;
}
void
group_close(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
struct client_ctx *cc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
client_close(cc);
}
}
}
/* no group please */
if (no == 0)
return;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->shortcut == no) {
group_add(gc, cc);
return;
}
}
if (Conf.flags & CONF_STICKY_GROUPS)
group_add(sc->group_active, cc);
}
void
group_update_names(struct screen_ctx *sc)
group_cycle(struct screen_ctx *sc, int flags)
{
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++;
struct group_ctx *newgc, *oldgc, *showgroup = NULL;
oldgc = sc->group_active;
newgc = oldgc;
for (;;) {
newgc = (flags & CWM_CYCLE_REVERSE) ? group_prev(newgc) :
group_next(newgc);
if (newgc == oldgc)
break;
if (!group_holds_only_sticky(newgc) && showgroup == NULL)
showgroup = newgc;
else if (!group_holds_only_hidden(newgc))
group_hide(newgc);
}
if (showgroup == NULL)
return;
group_hide(oldgc);
if (group_holds_only_hidden(showgroup))
group_show(showgroup);
else
group_set_active(showgroup);
}
static struct group_ctx *
group_next(struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct group_ctx *newgc;
return(((newgc = TAILQ_NEXT(gc, entry)) != NULL) ?
newgc : TAILQ_FIRST(&sc->groupq));
}
static struct group_ctx *
group_prev(struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct group_ctx *newgc;
return(((newgc = TAILQ_PREV(gc, group_q, entry)) != NULL) ?
newgc : TAILQ_LAST(&sc->groupq, group_q));
}
int
group_restore(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
int num;
long grpnum;
if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum))
return 0;
num = (grpnum == -1) ? 0 : grpnum;
num = MIN(num, (Conf.ngroups - 1));
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == num) {
group_assign(gc, cc);
return 1;
}
}
return 0;
}
int
group_autogroup(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct autogroup *ag;
struct group_ctx *gc;
int num = -1, both_match = 0;
if (cc->res_class == NULL || cc->res_name == NULL)
return 0;
TAILQ_FOREACH(ag, &Conf.autogroupq, entry) {
if (strcmp(ag->class, cc->res_class) == 0) {
if ((ag->name != NULL) &&
(strcmp(ag->name, cc->res_name) == 0)) {
num = ag->num;
both_match = 1;
} else if (ag->name == NULL && !both_match)
num = ag->num;
}
}
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;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == num) {
group_assign(gc, cc);
return 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)
xfree(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;
(void)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);
return 0;
}

953
kbfunc.c

File diff suppressed because it is too large Load Diff

541
menu.c
View File

@ -19,14 +19,17 @@
* $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"
@ -34,69 +37,51 @@
#define PROMPT_SCHAR "\xc2\xbb"
#define PROMPT_ECHAR "\xc2\xab"
#define MENUMASK (MOUSEMASK | ButtonMotionMask | KeyPressMask | \
ExposureMask)
#define MENUGRABMASK (MOUSEMASK | ButtonMotionMask | StructureNotifyMask)
enum ctltype {
CTL_NONE = -1,
CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
CTL_ABORT, CTL_ALL
CTL_TAB, CTL_ABORT, CTL_ALL
};
struct menu_ctx {
struct screen_ctx *sc;
Window win;
XftDraw *xftdraw;
struct geom geom;
char searchstr[MENU_MAXENTRY + 1];
char dispstr[MENU_MAXENTRY*2 + 1];
char promptstr[MENU_MAXENTRY + 1];
int hasprompt;
int list;
int listing;
int changed;
int noresult;
int prev;
int entry;
int width;
int num;
int x;
int y;
void (*match)(struct menu_q *, struct menu_q *, char *);
void (*print)(struct menu *, int);
int flags;
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 *);
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 *,
static void menu_handle_move(struct menu_ctx *,
struct menu_q *, int, int);
static struct menu *menu_handle_release(struct menu_ctx *,
struct menu_q *, int, int);
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);
static int menu_keycode(KeyCode, u_int, enum ctltype *,
char *);
void
menu_init(struct screen_ctx *sc)
{
XGCValues gv;
if (sc->menuwin)
XDestroyWindow(X_Dpy, sc->menuwin);
sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
Conf.bwidth,
sc->color[CWM_COLOR_FG_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;
if (sc->gc)
XFreeGC(X_Dpy, sc->gc);
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))
{
@ -105,62 +90,59 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
struct menu *mi = NULL;
XEvent e;
Window focuswin;
int evmask, focusrevert;
int xsave, ysave, xcur, ycur;
int focusrevert, xsave, ysave, xcur, ycur;
TAILQ_INIT(&resultq);
bzero(&mc, sizeof(mc));
xu_ptr_get(sc->rootwin, &xsave, &ysave);
xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y);
(void)memset(&mc, 0, sizeof(mc));
mc.sc = sc;
mc.flags = flags;
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
mc.geom.x = xsave;
mc.geom.y = ysave;
xsave = mc.x;
ysave = mc.y;
if (prompt == NULL) {
evmask = MENUMASK;
mc.promptstr[0] = '\0';
if (mc.flags & CWM_MENU_LIST)
mc.list = 1;
} else {
evmask = MENUMASK | KEYMASK; /* only accept keys if prompt */
(void)snprintf(mc.promptstr, sizeof(mc.promptstr), "%s%s",
prompt, PROMPT_SCHAR);
(void)snprintf(mc.dispstr, sizeof(mc.dispstr), "%s%s%s",
mc.promptstr, mc.searchstr, PROMPT_ECHAR);
mc.width = font_width(sc, mc.dispstr, strlen(mc.dispstr));
mc.hasprompt = 1;
}
(void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr));
if (initial != NULL)
(void)strlcpy(mc.searchstr, initial, sizeof(mc.searchstr));
else
mc.searchstr[0] = '\0';
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
mc.win = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
Conf.bwidth,
sc->xftcolor[CWM_COLOR_MENU_FG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
mc.xftdraw = XftDrawCreate(X_Dpy, mc.win,
sc->visual, sc->colormap);
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);
XSelectInput(X_Dpy, mc.win, MENUMASK);
XMapRaised(X_Dpy, mc.win);
if (xu_ptr_grab(sc->menuwin, MENUGRABMASK, Cursor_question) < 0) {
XUnmapWindow(X_Dpy, sc->menuwin);
return (NULL);
if (XGrabPointer(X_Dpy, mc.win, False, MENUGRABMASK,
GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION],
CurrentTime) != GrabSuccess) {
XftDrawDestroy(mc.xftdraw);
XDestroyWindow(X_Dpy, mc.win);
return NULL;
}
XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
XSetInputFocus(X_Dpy, sc->menuwin, RevertToPointerRoot, CurrentTime);
XSetInputFocus(X_Dpy, mc.win, RevertToPointerRoot, CurrentTime);
/* make sure keybindings don't remove keys from the menu stream */
XGrabKeyboard(X_Dpy, sc->menuwin, True,
XGrabKeyboard(X_Dpy, mc.win, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
for (;;) {
mc.changed = 0;
XWindowEvent(X_Dpy, sc->menuwin, evmask, &e);
XWindowEvent(X_Dpy, mc.win, MENUMASK, &e);
switch (e.type) {
case KeyPress:
@ -169,14 +151,15 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
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(&mc, &resultq,
e.xbutton.x, e.xbutton.y);
break;
case ButtonRelease:
if ((mi = menu_handle_release(&e, &mc, sc, &resultq))
!= NULL)
if ((mi = menu_handle_release(&mc, &resultq,
e.xbutton.x, e.xbutton.y)) != NULL)
goto out;
break;
default:
@ -184,22 +167,53 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
}
}
out:
if (dummy == 0 && mi->dummy) { /* no mouse based match */
xfree(mi);
if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) {
/* no mouse based match */
free(mi);
mi = NULL;
}
XftDrawDestroy(mc.xftdraw);
XDestroyWindow(X_Dpy, mc.win);
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();
xu_ptr_get(sc->rootwin, &xcur, &ycur);
if (xcur == mc.geom.x && ycur == mc.geom.y)
xu_ptr_set(sc->rootwin, xsave, ysave);
XUnmapWindow(X_Dpy, sc->menuwin);
XUngrabPointer(X_Dpy, CurrentTime);
XUngrabKeyboard(X_Dpy, CurrentTime);
return (mi);
return mi;
}
static struct menu *
menu_complete_path(struct menu_ctx *mc)
{
struct screen_ctx *sc = mc->sc;
struct menu *mi, *mr;
struct menu_q menuq;
int mflags = (CWM_MENU_DUMMY);
mr = xcalloc(1, sizeof(*mr));
TAILQ_INIT(&menuq);
if ((mi = menu_filter(sc, &menuq, mc->searchstr, NULL, mflags,
search_match_path, search_print_text)) != NULL) {
mr->abort = mi->abort;
mr->dummy = mi->dummy;
if (mi->text[0] != '\0')
snprintf(mr->text, sizeof(mr->text), "%s \"%s\"",
mc->searchstr, mi->text);
else if (!mr->abort)
strlcpy(mr->text, mc->searchstr, sizeof(mr->text));
}
menuq_clear(&menuq);
return mr;
}
static struct menu *
@ -208,16 +222,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 (menu_keycode(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;
@ -243,191 +263,212 @@ 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;
(void)strlcat(mc->searchstr, str, sizeof(mc->searchstr));
(void)strlcat(mc->searchstr, chr, sizeof(mc->searchstr));
}
mc->noresult = 0;
if (mc->changed && mc->searchstr[0] != '\0') {
(*mc->match)(menuq, resultq, mc->searchstr);
/* If menuq is empty, never show we've failed */
mc->noresult = TAILQ_EMPTY(resultq) && !TAILQ_EMPTY(menuq);
} else if (mc->changed)
TAILQ_INIT(resultq);
if (!mc->list && mc->listing && !mc->changed) {
if (mc->changed) {
if (mc->searchstr[0] != '\0')
(*mc->match)(menuq, resultq, mc->searchstr);
} else if (!mc->list && mc->listing) {
TAILQ_INIT(resultq);
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 screen_ctx *sc = mc->sc;
struct menu *mi;
XineramaScreenInfo *xine;
int xmin, xmax, ymin, ymax;
int n, dy, xsave, ysave;
struct geom area;
int n, xsave, ysave;
XGlyphInfo extents;
if (mc->list) {
if (TAILQ_EMPTY(resultq) && mc->list) {
if (TAILQ_EMPTY(resultq)) {
/* Copy them all over. */
TAILQ_FOREACH(mi, menuq, entry)
TAILQ_INSERT_TAIL(resultq, mi,
resultentry);
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
mc->listing = 1;
} else if (mc->changed)
mc->listing = 0;
}
mc->num = 0;
mc->width = 0;
dy = 0;
if (mc->hasprompt) {
(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s",
mc->promptstr, mc->searchstr, PROMPT_ECHAR);
mc->width = font_width(sc, mc->dispstr, strlen(mc->dispstr));
dy = font_height(sc);
mc->num = 1;
}
(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s",
mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR);
XftTextExtentsUtf8(X_Dpy, sc->xftfont,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr), &extents);
mc->geom.w = extents.xOff;
mc->geom.h = sc->xftfont->height + 1;
mc->num = 1;
TAILQ_FOREACH(mi, resultq, resultentry) {
char *text;
if (mc->print != NULL) {
(*mc->print)(mi, mc->listing);
text = mi->print;
} else {
mi->print[0] = '\0';
text = mi->text;
}
mc->width = MAX(mc->width, font_width(sc, text,
MIN(strlen(text), MENU_MAXENTRY)));
dy += font_height(sc);
(*mc->print)(mi, mc->listing);
XftTextExtentsUtf8(X_Dpy, sc->xftfont,
(const FcChar8*)mi->print,
MIN(strlen(mi->print), MENU_MAXENTRY), &extents);
mc->geom.w = MAX(mc->geom.w, extents.xOff);
mc->geom.h += sc->xftfont->height + 1;
mc->num++;
}
xine = screen_find_xinerama(sc, mc->x, mc->y);
if (xine) {
xmin = xine->x_org;
xmax = xine->x_org + xine->width;
ymin = xine->y_org;
ymax = xine->y_org + xine->height;
} else {
xmin = ymin = 0;
xmax = sc->xmax;
ymax = sc->ymax;
area = screen_area(sc, mc->geom.x, mc->geom.y, 1);
area.w += area.x - Conf.bwidth * 2;
area.h += area.y - Conf.bwidth * 2;
xsave = mc->geom.x;
ysave = mc->geom.y;
/* Never hide the top, or left side, of the menu. */
if (mc->geom.x + mc->geom.w >= area.w)
mc->geom.x = area.w - mc->geom.w;
if (mc->geom.x < area.x) {
mc->geom.x = area.x;
mc->geom.w = MIN(mc->geom.w, (area.w - area.x));
}
if (mc->geom.y + mc->geom.h >= area.h)
mc->geom.y = area.h - mc->geom.h;
if (mc->geom.y < area.y) {
mc->geom.y = area.y;
mc->geom.h = MIN(mc->geom.h, (area.h - area.y));
}
xsave = mc->x;
ysave = mc->y;
if (mc->geom.x != xsave || mc->geom.y != ysave)
xu_ptr_set(sc->rootwin, mc->geom.x, mc->geom.y);
if (mc->x < xmin)
mc->x = xmin;
else if (mc->x + mc->width >= xmax)
mc->x = xmax - mc->width;
XClearWindow(X_Dpy, mc->win);
XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y,
mc->geom.w, mc->geom.h);
if (mc->y + dy >= ymax)
mc->y = ymax - dy;
/* never hide the top of the menu */
if (mc->y < ymin) {
mc->y = ymin;
dy = ymax - ymin;
}
if (mc->x != xsave || mc->y != ysave)
xu_ptr_setpos(sc->rootwin, mc->x, mc->y);
XClearWindow(X_Dpy, sc->menuwin);
XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y, mc->width, dy);
if (mc->hasprompt) {
font_draw(sc, mc->dispstr, strlen(mc->dispstr), sc->menuwin,
0, font_ascent(sc) + 1);
n = 1;
} else
n = 0;
n = 1;
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, sc->xftfont->ascent,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr));
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->geom.y + y > area.h)
break;
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, y,
(const FcChar8*)mi->print, strlen(mi->print));
n++;
}
if (mc->hasprompt && n > 1 && (mc->searchstr[0] != '\0'))
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));
if (n > 1)
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;
int color, 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;
XftDrawRect(mc->xftdraw, &sc->xftcolor[color], 0,
(sc->xftfont->height + 1) * entry, mc->geom.w,
(sc->xftfont->height + 1) + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[color], sc->xftfont,
0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1,
(const FcChar8*)mi->print, strlen(mi->print));
}
static void
menu_handle_move(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
{
mc->prev = mc->entry;
mc->entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
mc->entry = menu_calc_entry(mc, x, 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) {
(void)xu_ptr_regrab(MENUGRABMASK, Cursor_normal);
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height(sc) * mc->entry, mc->width, font_height(sc));
} else
(void)xu_ptr_regrab(MENUGRABMASK, Cursor_default);
XChangeActivePointerGrab(X_Dpy, MENUGRABMASK,
Conf.cursor[CF_NORMAL], CurrentTime);
menu_draw_entry(mc, resultq, mc->entry, 1);
}
}
static struct menu *
menu_handle_release(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
struct menu_q *resultq)
menu_handle_release(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
{
struct menu *mi;
int entry, i = 0;
struct menu *mi;
int entry, i = 1;
entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
if (mc->hasprompt)
i = 1;
entry = menu_calc_entry(mc, x, y);
TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++)
@ -437,45 +478,52 @@ 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->geom.w || y < 0 ||
y > (sc->xftfont->height + 1) * mc->num ||
entry < 0 || entry >= mc->num)
entry = -1;
if (mc->hasprompt && entry == 0)
if (entry == 0)
entry = -1;
return (entry);
return entry;
}
static int
menu_keycode(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
{
int ks;
KeySym ks;
*ctl = CTL_NONE;
*chr = '\0';
chr[0] = '\0';
ks = XKeycodeToKeysym(X_Dpy, kc, (state & ShiftMask) ? 1 : 0);
ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0,
(ev->state & ShiftMask) ? 1 : 0);
/* Look for control characters. */
switch (ks) {
case XK_BackSpace:
*ctl = CTL_ERASEONE;
break;
case XK_KP_Enter:
case XK_Return:
*ctl = CTL_RETURN;
break;
case XK_Tab:
*ctl = CTL_TAB;
break;
case XK_Up:
*ctl = CTL_UP;
break;
@ -487,7 +535,7 @@ menu_keycode(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
break;
}
if (*ctl == CTL_NONE && (state & ControlMask)) {
if (*ctl == CTL_NONE && (ev->state & ControlMask)) {
switch (ks) {
case XK_s:
case XK_S:
@ -511,10 +559,13 @@ menu_keycode(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
case XK_A:
*ctl = CTL_ALL;
break;
case XK_bracketleft:
*ctl = CTL_ABORT;
break;
}
}
if (*ctl == CTL_NONE && (state & Mod1Mask)) {
if (*ctl == CTL_NONE && (ev->state & Mod1Mask)) {
switch (ks) {
case XK_j:
case XK_J:
@ -530,16 +581,40 @@ menu_keycode(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
}
if (*ctl != CTL_NONE)
return (0);
return 0;
/*
* For regular characters, only (part of, actually) Latin 1
* for now.
*/
if (ks < 0x20 || ks > 0x07e)
return (-1);
if (XLookupString(ev, chr, 32, &ks, NULL) < 0)
return -1;
*chr = (char)ks;
return (0);
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);
if (fmt != NULL)
(void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap);
else
mi->text[0] = '\0';
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);
}
}

74
migrate-config.pl Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/perl -p
# Originally by Stuart Henderson in
# <20161206120601.vmimohqh4nafaeah@symphytum.spacehopper.org>
# Ported to Perl and slightly tweaked by Leah Neukirchen.
s/\bbind\b(.*)\bunmap/unbind-key \1/;
s/\bmousebind\b(.*)unmap/unbind-mouse \1/;
s/\bresize\b/window-resize/;
s/\bresizedown\b/window-resize-down/;
s/\bresizeleft\b/window-resize-left/;
s/\bresizeright\b/window-resize-right/;
s/\bresizeup\b/window-resize-up/;
s/\bmove\b/window-move/;
s/\bmovedown\b/window-move-down/;
s/\bmoveleft\b/window-move-left/;
s/\bmoveright\b/window-move-right/;
s/\bmoveup\b/window-move-up/;
s/\bbigmovedown\b/window-move-down-big/;
s/\bbigmoveleft\b/window-move-left-big/;
s/\bbigmoveright\b/window-move-right-big/;
s/\bbigmoveup\b/window-move-up-big/;
s/\bbigptrmovedown\b/pointer-move-down-big/;
s/\bbigptrmoveleft\b/pointer-move-left-big/;
s/\bbigptrmoveright\b/pointer-move-right-big/;
s/\bbigptrmoveup\b/pointer-move-up-big/;
s/\bbigresizedown\b/window-resize-down-big/;
s/\bbigresizeleft\b/window-resize-left-big/;
s/\bbigresizeright\b/window-resize-right-big/;
s/\bbigresizeup\b/window-resize-up-big/;
s/\bbind\b/bind-key/;
s/\bcycle\b/window-cycle/;
s/\bcyclegroup\b/group-cycle/;
s/\bcycleingroup\b/window-cycle-ingroup/;
s/\bdelete\b/window-delete/;
s/\bexec\b/menu-exec/;
s/\bexec_wm\b/menu-exec-wm/;
s/\bfreeze\b/window-freeze/;
s/\bfullscreen\b/window-fullscreen/;
s/\bgroupsearch\b/menu-group/;
s/\bgrouptoggle\b/window-group/;
s/\bhide\b/window-hide/;
s/\bhmaximize\b/window-hmaximize/;
s/\bhtile\b/window-htile/;
s/\blabel\b/window-menu-label/;
s/\blower\b/window-lower/;
s/\bmaximize\b/window-maximize/;
s/\bmenu_cmd\b/menu-cmd/;
s/\bmenu_group\b/menu-group/;
s/\bmenu_unhide\b/menu-window/;
s/\bmenusearch\b/menu-cmd/;
s/\bmousebind\b/bind-mouse/;
s/\bnogroup\b/group-toggle-all/;
s/\bptrmovedown\b/pointer-move-down/;
s/\bptrmoveleft\b/pointer-move-left/;
s/\bptrmoveright\b/pointer-move-right/;
s/\bptrmoveup\b/pointer-move-up/;
s/\braise\b/window-raise/;
s/\brcycle\b/window-rcycle/;
s/\brcyclegroup\b/group-rcycle/;
s/\brcycleingroup\b/window-rcycle-ingroup/;
s/\bsearch\b/menu-window/;
s/\bssh\b/menu-ssh/;
s/\bstick\b/window-stick/;
s/\bvmaximize\b/window-vmaximize/;
s/\bvtile\b/window-vtile/;
s/\bwindow_grouptoggle\b/window-group/;
s/\bwindow_move\b/window-move/;
s/\bwindow_resize\b/window-resize/;
s/\bwindow_hide\b/window-hide/;
s/\bwindow_lower\b/window-lower/;
s/\bwindow_raise\b/window-raise/;
s/\bmovetogroup([1-9])\b/window-movetogroup-\1/;
s/\bgrouponly([1-9])\b/group-only-\1/;
s/\bgroup([1-9])\b/group-toggle-\1/;

View File

@ -1,289 +0,0 @@
/*
* calmwm - the calm window manager
*
* 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
* 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$
*/
#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"
static int mousefunc_sweep_calc(struct client_ctx *, int, int, int, int);
static void mousefunc_sweep_draw(struct client_ctx *);
static int
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;
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);
}
static void
mousefunc_sweep_draw(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
char asize[10]; /* fits "nnnnxnnnn\0" */
int width, width_size, width_name;
(void)snprintf(asize, sizeof(asize), "%dx%d",
(cc->geom.width - cc->hint.basew) / cc->hint.incw,
(cc->geom.height - cc->hint.baseh) / cc->hint.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);
XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0);
XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, width, font_height(sc) * 2);
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, font_height(sc) + font_ascent(sc) + 1);
}
void
mousefunc_window_resize(struct client_ctx *cc, void *arg)
{
XEvent ev;
Time ltime = 0;
struct screen_ctx *sc = cc->sc;
int x = cc->geom.x, y = cc->geom.y;
if (cc->flags & CLIENT_FREEZE)
return;
client_raise(cc);
client_ptrsave(cc);
if (xu_ptr_grab(cc->win, MOUSEMASK, Cursor_resize) < 0)
return;
xu_ptr_setpos(cc->win, cc->geom.width, cc->geom.height);
mousefunc_sweep_draw(cc);
for (;;) {
XMaskEvent(X_Dpy, MOUSEMASK|ExposureMask, &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);
/* don't resize more than 60 times / second */
if ((ev.xmotion.time - ltime) > (1000 / 60)) {
ltime = ev.xmotion.time;
client_resize(cc);
}
break;
case ButtonRelease:
if (ltime)
client_resize(cc);
XUnmapWindow(X_Dpy, sc->menuwin);
XReparentWindow(X_Dpy, sc->menuwin, sc->rootwin, 0, 0);
xu_ptr_ungrab();
/* Make sure the pointer stays within the window. */
if (cc->ptr.x > cc->geom.width)
cc->ptr.x = cc->geom.width - cc->bwidth;
if (cc->ptr.y > cc->geom.height)
cc->ptr.y = cc->geom.height - cc->bwidth;
client_ptrwarp(cc);
return;
}
}
/* NOTREACHED */
}
void
mousefunc_window_move(struct client_ctx *cc, void *arg)
{
XEvent ev;
Time ltime = 0;
int px, py;
client_raise(cc);
if (cc->flags & CLIENT_FREEZE)
return;
if (xu_ptr_grab(cc->win, MOUSEMASK, Cursor_move) < 0)
return;
xu_ptr_getpos(cc->win, &px, &py);
for (;;) {
XMaskEvent(X_Dpy, MOUSEMASK|ExposureMask, &ev);
switch (ev.type) {
case Expose:
client_draw_border(cc);
break;
case MotionNotify:
cc->geom.x = ev.xmotion.x_root - px - cc->bwidth;
cc->geom.y = ev.xmotion.y_root - py - cc->bwidth;
cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.width, cc->sc->xmax,
cc->bwidth, Conf.snapdist);
cc->geom.y += client_snapcalc(cc->geom.y,
cc->geom.height, cc->sc->ymax,
cc->bwidth, Conf.snapdist);
/* don't move more than 60 times / second */
if ((ev.xmotion.time - ltime) > (1000 / 60)) {
ltime = ev.xmotion.time;
client_move(cc);
}
break;
case ButtonRelease:
if (ltime)
client_move(cc);
xu_ptr_ungrab();
return;
}
}
/* NOTREACHED */
}
void
mousefunc_window_grouptoggle(struct client_ctx *cc, void *arg)
{
group_sticky_toggle_enter(cc);
}
void
mousefunc_window_lower(struct client_ctx *cc, void *arg)
{
client_ptrsave(cc);
client_lower(cc);
}
void
mousefunc_window_raise(struct client_ctx *cc, void *arg)
{
client_raise(cc);
}
void
mousefunc_window_hide(struct client_ctx *cc, void *arg)
{
client_hide(cc);
}
void
mousefunc_menu_group(struct client_ctx *cc, void *arg)
{
group_menu(arg);
}
void
mousefunc_menu_unhide(struct client_ctx *cc, void *arg)
{
struct screen_ctx *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)
if (cc->flags & CLIENT_HIDDEN) {
wname = (cc->label) ? cc->label : cc->name;
if (wname == NULL)
continue;
mi = xcalloc(1, sizeof(*mi));
(void)strlcpy(mi->text, wname, sizeof(mi->text));
mi->ctx = cc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL) {
cc = (struct client_ctx *)mi->ctx;
client_unhide(cc);
if (old_cc != NULL)
client_ptrsave(old_cc);
client_ptrwarp(cc);
} else {
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
}
void
mousefunc_menu_cmd(struct client_ctx *cc, void *arg)
{
struct screen_ctx *sc;
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));
(void)strlcpy(mi->text, cmd->label, sizeof(mi->text));
mi->ctx = cmd;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(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);
}
}

365
parse.y
View File

@ -21,8 +21,8 @@
%{
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "queue.h"
#include <ctype.h>
#include <err.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 *, FILE *);
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;
@ -67,18 +70,20 @@ typedef struct {
%}
%token FONTNAME STICKY GAP MOUSEBIND
%token AUTOGROUP BIND COMMAND IGNORE
%token YES NO BORDERWIDTH MOVEAMOUNT
%token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
%token FONTNAME STICKY GAP
%token AUTOGROUP COMMAND IGNORE WM
%token YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE
%token COLOR SNAPDIST
%token ACTIVEBORDER INACTIVEBORDER
%token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
%token GROUPBORDER UNGROUPBORDER
%token MENUBG MENUFG FONTCOLOR
%token MENUBG MENUFG
%token FONTCOLOR FONTSELCOLOR
%token ERROR
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> yesno
%type <v.string> string
%type <v.string> string numberstring
%%
grammar : /* empty */
@ -101,6 +106,17 @@ string : string STRING {
| STRING
;
numberstring : NUMBER {
char *s;
if (asprintf(&s, "%lld", $1) == -1) {
yyerror("string: asprintf");
YYERROR;
}
$$ = s;
}
| STRING
;
yesno : YES { $$ = 1; }
| NO { $$ = 0; }
;
@ -110,92 +126,178 @@ main : FONTNAME STRING {
conf->font = $2;
}
| STICKY yesno {
if ($2 == 0)
conf->flags &= ~CONF_STICKY_GROUPS;
else
conf->flags |= CONF_STICKY_GROUPS;
conf->stickygroups = $2;
}
| BORDERWIDTH NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid borderwidth");
YYERROR;
}
conf->bwidth = $2;
}
| HTILE NUMBER {
if ($2 < 0 || $2 > 99) {
yyerror("invalid htile percent");
YYERROR;
}
conf->htile = $2;
}
| VTILE NUMBER {
if ($2 < 0 || $2 > 99) {
yyerror("invalid vtile percent");
YYERROR;
}
conf->vtile = $2;
}
| MOVEAMOUNT NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid movemount");
YYERROR;
}
conf->mamount = $2;
}
| SNAPDIST NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid snapdist");
YYERROR;
}
conf->snapdist = $2;
}
| COMMAND STRING string {
conf_cmd_add(conf, $3, $2, 0);
if (strlen($3) >= PATH_MAX) {
yyerror("%s command path too long", $2);
free($2);
free($3);
YYERROR;
}
conf_cmd_add(conf, $2, $3);
free($2);
free($3);
}
| WM STRING string {
if (strlen($3) >= PATH_MAX) {
yyerror("%s wm path too long", $2);
free($2);
free($3);
YYERROR;
}
conf_wm_add(conf, $2, $3);
free($2);
free($3);
}
| AUTOGROUP NUMBER STRING {
if ($2 < 0 || $2 > 9) {
yyerror("invalid autogroup");
free($3);
yyerror("autogroup number out of range: %d", $2);
YYERROR;
}
group_make_autogroup(conf, $3, $2);
conf_autogroup(conf, $2, NULL, $3);
free($3);
}
| AUTOGROUP NUMBER STRING ',' STRING {
if ($2 < 0 || $2 > 9) {
yyerror("invalid autogroup");
free($3);
free($5);
YYERROR;
}
conf_autogroup(conf, $2, $3, $5);
free($3);
free($5);
}
| IGNORE STRING {
struct winmatch *wm;
wm = xcalloc(1, sizeof(*wm));
(void)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);
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");
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);
| BINDKEY numberstring string {
if (!conf_bind_key(conf, $2, $3)) {
yyerror("invalid bind-key: %s %s", $2, $3);
free($2);
free($3);
YYERROR;
}
free($2);
free($3);
}
| UNBINDKEY numberstring {
if (!conf_bind_key(conf, $2, NULL)) {
yyerror("invalid unbind-key: %s", $2);
free($2);
YYERROR;
}
free($2);
}
| BINDMOUSE numberstring string {
if (!conf_bind_mouse(conf, $2, $3)) {
yyerror("invalid bind-mouse: %s %s", $2, $3);
free($2);
free($3);
YYERROR;
}
free($2);
free($3);
}
| UNBINDMOUSE numberstring {
if (!conf_bind_mouse(conf, $2, NULL)) {
yyerror("invalid unbind-mouse: %s", $2);
free($2);
YYERROR;
}
free($2);
}
;
color : COLOR colors
;
colors : ACTIVEBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_ACTIVE].name);
conf->color[CWM_COLOR_BORDER_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_BG_MENU].name);
conf->color[CWM_COLOR_BG_MENU].name = $2;
free(conf->color[CWM_COLOR_MENU_BG]);
conf->color[CWM_COLOR_MENU_BG] = $2;
}
| MENUFG STRING {
free(conf->color[CWM_COLOR_FG_MENU].name);
conf->color[CWM_COLOR_FG_MENU].name = $2;
free(conf->color[CWM_COLOR_MENU_FG]);
conf->color[CWM_COLOR_MENU_FG] = $2;
}
| FONTCOLOR STRING {
free(conf->color[CWM_COLOR_FONT].name);
conf->color[CWM_COLOR_FONT].name = $2;
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;
}
;
%%
@ -208,7 +310,7 @@ struct keywords {
int
yyerror(const char *fmt, ...)
{
va_list ap;
va_list ap;
file->errors++;
va_start(ap, fmt);
@ -232,7 +334,8 @@ lookup(char *s)
static const struct keywords keywords[] = {
{ "activeborder", ACTIVEBORDER},
{ "autogroup", AUTOGROUP},
{ "bind", BIND},
{ "bind-key", BINDKEY},
{ "bind-mouse", BINDMOUSE},
{ "borderwidth", BORDERWIDTH},
{ "color", COLOR},
{ "command", COMMAND},
@ -240,16 +343,22 @@ lookup(char *s)
{ "fontname", FONTNAME},
{ "gap", GAP},
{ "groupborder", GROUPBORDER},
{ "htile", HTILE},
{ "ignore", IGNORE},
{ "inactiveborder", INACTIVEBORDER},
{ "menubg", MENUBG},
{ "menufg", MENUFG},
{ "mousebind", MOUSEBIND},
{ "moveamount", MOVEAMOUNT},
{ "no", NO},
{ "selfont", FONTSELCOLOR},
{ "snapdist", SNAPDIST},
{ "sticky", STICKY},
{ "unbind-key", UNBINDKEY},
{ "unbind-mouse", UNBINDMOUSE},
{ "ungroupborder", UNGROUPBORDER},
{ "urgencyborder", URGENCYBORDER},
{ "vtile", VTILE},
{ "wm", WM},
{ "yes", YES}
};
const struct keywords *p;
@ -278,7 +387,7 @@ lgetc(int quotec)
if (parsebuf) {
/* Read character from the parsebuffer instead of input. */
if (parseindex >= 0) {
c = parsebuf[parseindex++];
c = (unsigned char)parsebuf[parseindex++];
if (c != '\0')
return (c);
parsebuf = NULL;
@ -287,12 +396,13 @@ lgetc(int quotec)
}
if (pushback_index)
return (pushback_buffer[--pushback_index]);
return ((unsigned char)pushback_buffer[--pushback_index]);
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);
}
@ -310,7 +420,7 @@ lgetc(int quotec)
}
while (c == EOF) {
if (popfile() == EOF)
if (file == topfile || popfile() == EOF)
return (EOF);
c = getc(file->stream);
}
@ -327,10 +437,10 @@ lungetc(int c)
if (parseindex >= 0)
return (c);
}
if (pushback_index < MAXPUSHBACK-1)
return (pushback_buffer[pushback_index++] = c);
else
if (pushback_index + 1 >= MAXPUSHBACK)
return (EOF);
pushback_buffer[pushback_index++] = c;
return (c);
}
int
@ -339,11 +449,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 = (unsigned char)pushback_buffer[--pushback_index];
else
c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
@ -384,7 +496,8 @@ yylex(void)
} else if (c == '\\') {
if ((next = lgetc(quotec)) == EOF)
return (0);
if (next == quotec || c == ' ' || c == '\t')
if (next == quotec || next == ' ' ||
next == '\t')
c = next;
else if (next == '\n') {
file->lineno++;
@ -394,6 +507,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");
@ -411,7 +527,7 @@ yylex(void)
if (c == '-' || isdigit(c)) {
do {
*p++ = c;
if ((unsigned)(p-buf) >= sizeof(buf)) {
if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
@ -434,13 +550,14 @@ yylex(void)
} else {
nodigits:
while (p > buf + 1)
lungetc(*--p);
c = *--p;
lungetc((unsigned char)*--p);
c = (unsigned char)*--p;
if (c == '-')
return (c);
}
}
/* 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 != '>' && \
@ -449,7 +566,7 @@ nodigits:
if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') {
do {
*p++ = c;
if ((unsigned)(p-buf) >= sizeof(buf)) {
if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
@ -470,18 +587,13 @@ nodigits:
}
struct file *
pushfile(const char *name)
pushfile(const char *name, FILE *stream)
{
struct file *nfile;
nfile = xcalloc(1, sizeof(struct file));
nfile->name = xstrdup(name);
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
free(nfile->name);
free(nfile);
return (NULL);
}
nfile->stream = stream;
nfile->lineno = 1;
TAILQ_INSERT_TAIL(&files, nfile, entry);
return (nfile);
@ -492,95 +604,38 @@ 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;
FILE *stream;
int errors = 0;
conf = xcalloc(1, sizeof(*conf));
conf = xconf;
if ((file = pushfile(filename)) == NULL) {
free(conf);
stream = fopen(filename, "r");
if (stream == NULL) {
if (errno == ENOENT)
return (0);
warn("%s", filename);
return (-1);
}
(void)strlcpy(conf->conf_path, filename, sizeof(conf->conf_path));
conf_init(conf);
file = pushfile(filename, stream);
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->snapdist = conf->snapdist;
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);
}
(void)strlcpy(xconf->termpath, conf->termpath,
sizeof(xconf->termpath));
(void)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->font = conf->font;
}
free(conf);
return (errors ? -1 : 0);
}

648
queue.h Normal file
View File

@ -0,0 +1,648 @@
/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch 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)
/*
* XOR Simple queue definitions.
*/
#define XSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqx_first; /* first element */ \
struct type **sqx_last; /* addr of last next element */ \
unsigned long sqx_cookie; \
}
#define XSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqx_next; /* next element */ \
}
/*
* XOR Simple queue access methods.
*/
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
(unsigned long)(ptr)))
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
#define XSIMPLEQ_END(head) NULL
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
#define XSIMPLEQ_FOREACH(var, head, field) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) != XSIMPLEQ_END(head); \
(var) = XSIMPLEQ_NEXT(head, var, field))
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
(var) = (tvar))
/*
* XOR Simple queue functions.
*/
#define XSIMPLEQ_INIT(head) do { \
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqx_next = (head)->sqx_first) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
(elm)->field.sqx_next)->field.sqx_next) \
== XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = \
XSIMPLEQ_XOR(head, &(elm)->field.sqx_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_ */

38
reallocarray.c Normal file
View File

@ -0,0 +1,38 @@
/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */
/*
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
*
* 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/types.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
/*
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
*/
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
void *
reallocarray(void *optr, size_t nmemb, size_t size)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
return NULL;
}
return realloc(optr, size * nmemb);
}

332
screen.c
View File

@ -18,29 +18,106 @@
* $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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static struct geom screen_apply_gap(struct screen_ctx *, struct geom);
static void screen_scan(struct screen_ctx *);
void
screen_init(int which)
{
struct screen_ctx *sc;
XSetWindowAttributes attr;
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->colormap = DefaultColormap(X_Dpy, sc->which);
sc->visual = DefaultVisual(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);
conf_group(sc);
sc->group_last = sc->group_active;
screen_update_geometry(sc);
xu_ewmh_net_desktop_names(sc);
xu_ewmh_net_number_of_desktops(sc);
xu_ewmh_net_showing_desktop(sc);
xu_ewmh_net_virtual_roots(sc);
attr.cursor = Conf.cursor[CF_NORMAL];
attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
EnterWindowMask | PropertyChangeMask | ButtonPressMask;
XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr);
if (Conf.xrandr)
XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
screen_scan(sc);
screen_updatestackingorder(sc);
TAILQ_INSERT_TAIL(&Screenq, sc, entry);
XSync(X_Dpy, False);
}
static void
screen_scan(struct screen_ctx *sc)
{
struct client_ctx *cc, *active = NULL;
Window *wins, w0, w1, rwin, cwin;
unsigned int nwins, i, mask;
int rx, ry, wx, wy;
XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin,
&rx, &ry, &wx, &wy, &mask);
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (i = 0; i < nwins; i++) {
if ((cc = client_init(wins[i], sc)) != NULL)
if (cc->win == cwin)
active = cc;
}
XFree(wins);
}
if (active)
client_set_active(active);
}
struct screen_ctx *
screen_fromroot(Window rootwin)
screen_find(Window win)
{
struct screen_ctx *sc;
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == rootwin)
return (sc);
/* XXX FAIL HERE */
return (TAILQ_FIRST(&Screenq));
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->rootwin == win)
return sc;
}
warnx("%s: failure win 0x%lx", __func__, win);
return NULL;
}
void
@ -48,86 +125,183 @@ 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)
cc->stackingorder = s++;
}
XFree(wins);
}
}
struct region_ctx *
region_find(struct screen_ctx *sc, int x, int y)
{
struct region_ctx *rc;
TAILQ_FOREACH(rc, &sc->regionq, entry) {
if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) &&
(y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) {
break;
}
}
return rc;
}
struct geom
screen_area(struct screen_ctx *sc, int x, int y, int apply_gap)
{
struct region_ctx *rc;
struct geom area = sc->view;
TAILQ_FOREACH(rc, &sc->regionq, entry) {
if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) &&
(y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) {
area = rc->view;
break;
}
}
if (apply_gap)
area = screen_apply_gap(sc, area);
return area;
}
void
screen_update_geometry(struct screen_ctx *sc)
{
struct region_ctx *rc;
sc->view.x = 0;
sc->view.y = 0;
sc->view.w = DisplayWidth(X_Dpy, sc->which);
sc->view.h = DisplayHeight(X_Dpy, sc->which);
sc->work = screen_apply_gap(sc, sc->view);
while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) {
TAILQ_REMOVE(&sc->regionq, rc, entry);
free(rc);
}
if (Conf.xrandr) {
XRRScreenResources *sr;
XRRCrtcInfo *ci;
int i;
sr = XRRGetScreenResources(X_Dpy, sc->rootwin);
for (i = 0, ci = NULL; i < sr->ncrtc; i++) {
ci = XRRGetCrtcInfo(X_Dpy, sr, sr->crtcs[i]);
if (ci == NULL)
continue;
if (ci->noutput == 0) {
XRRFreeCrtcInfo(ci);
continue;
}
rc = xmalloc(sizeof(*rc));
rc->num = i;
rc->view.x = ci->x;
rc->view.y = ci->y;
rc->view.w = ci->width;
rc->view.h = ci->height;
rc->work = screen_apply_gap(sc, rc->view);
TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
XRRFreeCrtcInfo(ci);
}
XRRFreeScreenResources(sr);
} else {
rc = xmalloc(sizeof(*rc));
rc->num = 0;
rc->view.x = 0;
rc->view.y = 0;
rc->view.w = DisplayWidth(X_Dpy, sc->which);
rc->view.h = DisplayHeight(X_Dpy, sc->which);
rc->work = screen_apply_gap(sc, rc->view);
TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
}
xu_ewmh_net_desktop_geometry(sc);
xu_ewmh_net_desktop_viewport(sc);
xu_ewmh_net_workarea(sc);
}
static struct geom
screen_apply_gap(struct screen_ctx *sc, struct geom geom)
{
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;
}
/* Bring back clients which are beyond the screen. */
void
screen_assert_clients_within(struct screen_ctx *sc)
{
struct client_ctx *cc;
int top, left, right, bottom;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->sc != sc)
continue;
cc->stackingorder = s++;
top = cc->geom.y;
left = cc->geom.x;
right = cc->geom.x + cc->geom.w + (cc->bwidth * 2) - 1;
bottom = cc->geom.y + cc->geom.h + (cc->bwidth * 2) - 1;
if ((top > sc->view.h || left > sc->view.w) ||
(bottom < 0 || right < 0)) {
cc->geom.x = sc->gap.left;
cc->geom.y = sc->gap.top;
client_move(cc);
}
}
XFree(wins);
}
void
screen_init_xinerama(struct screen_ctx *sc)
screen_prop_win_create(struct screen_ctx *sc, Window win)
{
XineramaScreenInfo *info;
int no;
sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win,
sc->visual, sc->colormap);
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)
{
XineramaScreenInfo *info;
int i;
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);
}
return (NULL);
XMapWindow(X_Dpy, sc->prop.win);
}
void
screen_update_geometry(struct screen_ctx *sc, int width, int height)
screen_prop_win_destroy(struct screen_ctx *sc)
{
long geom[2], workareas[CALMWM_NGROUPS][4];
int 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);
/* 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);
}
XChangeProperty(X_Dpy, sc->rootwin, _NET_WORKAREA,
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)workareas, CALMWM_NGROUPS * 4);
XftDrawDestroy(sc->prop.xftdraw);
XDestroyWindow(X_Dpy, sc->prop.win);
}
void
screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...)
{
va_list ap;
char *text;
XGlyphInfo extents;
va_start(ap, fmt);
xvasprintf(&text, fmt, ap);
va_end(ap);
XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
strlen(text), &extents);
XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height);
XClearWindow(X_Dpy, sc->prop.win);
XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
sc->xftfont, 0, sc->xftfont->ascent + 1,
(const FcChar8*)text, strlen(text));
free(text);
}

323
search.c
View File

@ -18,87 +18,96 @@
* $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"
static int strsubmatch(char *, char *, int);
#define PATH_ANY 0x0001
#define PATH_EXEC 0x0002
/*
* Match: label, title, class.
*/
static void match_path_type(struct menu_q *, char *, int);
static int match_substr(char *, char *, int);
static int
match_substr(char *sub, char *str, int zeroidx)
{
size_t len, sublen;
unsigned int n, flen;
if (sub == NULL || str == NULL)
return 0;
len = strlen(str);
sublen = strlen(sub);
if (sublen > len)
return 0;
if (zeroidx)
flen = 0;
else
flen = len - sublen;
for (n = 0; n <= flen; n++)
if (strncasecmp(sub, str + n, sublen) == 0)
return 1;
return 0;
}
void
search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct winname *wn;
struct menu *mi, *tierp[4], *before = NULL;
TAILQ_INIT(resultq);
struct menu *mi, *tierp[3], *before = NULL;
struct client_ctx *cc;
struct winname *wn;
(void)memset(tierp, 0, sizeof(tierp));
/*
* In order of rank:
*
* 1. Look through labels.
* 2. Look at title history, from present to past.
* 3. Look at window class name.
*/
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
int tier = -1, t;
struct client_ctx *cc = mi->ctx;
cc = (struct client_ctx *)mi->ctx;
/* First, try to match on labels. */
if (cc->label != NULL && strsubmatch(search, cc->label, 0)) {
cc->matchname = cc->label;
/* Match on label. */
if (match_substr(search, cc->label, 0))
tier = 0;
}
/* Then, on window names. */
/* Match on window name history, from present to past. */
if (tier < 0) {
TAILQ_FOREACH_REVERSE(wn, &cc->nameq, winname_q, entry)
if (strsubmatch(search, wn->name, 0)) {
cc->matchname = wn->name;
tier = 2;
TAILQ_FOREACH_REVERSE(wn, &cc->nameq, name_q, entry)
if (match_substr(search, wn->name, 0)) {
tier = 1;
break;
}
}
/* 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;
tier = 3;
}
/* Match on window resource class. */
if ((tier < 0) && match_substr(search, cc->res_class, 0))
tier = 2;
if (tier < 0)
continue;
/*
* De-rank a client one tier if it's the current
* window. Furthermore, this is denoted by a "!" when
* printing the window name in the search menu.
*/
if (cc == client_current() && tier < nitems(tierp) - 1)
/* Current window is ranked down. */
if ((tier < nitems(tierp) - 1) && (cc->flags & CLIENT_ACTIVE))
tier++;
/* Clients that are hidden get ranked one up. */
if (cc->flags & CLIENT_HIDDEN && tier > 0)
/* Hidden window is ranked up. */
if ((tier > 0) && (cc->flags & CLIENT_HIDDEN))
tier--;
assert(tier < nitems(tierp));
/*
* If you have a tierp, insert after it, and make it
* the new tierp. If you don't have a tierp, find the
@ -119,106 +128,162 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
}
void
search_print_client(struct menu *mi, int list)
search_match_cmd(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct client_ctx *cc;
char flag = ' ';
struct menu *mi;
struct cmd_ctx *cmd;
cc = mi->ctx;
if (cc == client_current())
flag = '!';
else if (cc->flags & CLIENT_HIDDEN)
flag = '&';
if (list)
cc->matchname = cc->name;
(void)snprintf(mi->print, sizeof(mi->print), "%c%s", flag,
cc->matchname);
if (!list && cc->matchname != cc->name &&
strlen(mi->print) < sizeof(mi->print) - 1) {
const char *marker = "";
char buf[MENU_MAXENTRY + 1];
int diff;
diff = sizeof(mi->print) - 1 - strlen(mi->print);
/* One for the ':' */
diff -= 1;
if (strlen(cc->name) > diff) {
marker = "..";
diff -= 2;
} else {
diff = strlen(cc->name);
}
(void)strlcpy(buf, mi->print, sizeof(buf));
(void)snprintf(mi->print, sizeof(mi->print),
"%s:%.*s%s", buf, diff, cc->name, marker);
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
cmd = (struct cmd_ctx *)mi->ctx;
if (match_substr(search, cmd->name, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
void
search_match_group(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
struct group_ctx *gc;
char *s;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
gc = (struct group_ctx *)mi->ctx;
xasprintf(&s, "%d %s", gc->num, gc->name);
if (match_substr(search, s, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
free(s);
}
}
static void
match_path_type(struct menu_q *resultq, char *search, int flag)
{
struct menu *mi;
char *pattern;
glob_t g;
int i;
xasprintf(&pattern, "%s*", search);
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;
mi = xcalloc(1, sizeof(*mi));
(void)strlcpy(mi->text, g.gl_pathv[i], sizeof(mi->text));
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
globfree(&g);
free(pattern);
}
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 (match_substr(search, mi->text, 1) == 0 &&
fnmatch(search, mi->text, 0) == FNM_NOMATCH)
continue;
TAILQ_FOREACH(mj, resultq, resultentry) {
r = strcmp(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);
}
if (TAILQ_EMPTY(resultq))
match_path_type(resultq, search, PATH_EXEC);
}
void
search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
TAILQ_INIT(resultq);
match_path_type(resultq, search, PATH_ANY);
}
void
search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry)
if (strsubmatch(search, mi->text, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
void
search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi, *mj;
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) {
TAILQ_INSERT_BEFORE(mj, mi, resultentry);
break;
}
}
if (mj == NULL)
if (match_substr(search, mi->text, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
static int
strsubmatch(char *sub, char *str, int zeroidx)
void
search_match_wm(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
size_t len, sublen;
u_int n, flen;
struct menu *mi;
struct cmd_ctx *wm;
if (sub == NULL || str == NULL)
return (0);
len = strlen(str);
sublen = strlen(sub);
if (sublen > len)
return (0);
if (!zeroidx)
flen = len - sublen;
else
flen = 0;
for (n = 0; n <= flen; n++)
if (strncasecmp(sub, str + n, sublen) == 0)
return (1);
return (0);
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
wm = (struct cmd_ctx *)mi->ctx;
if ((match_substr(search, wm->name, 0)) ||
(match_substr(search, wm->path, 0)))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
void
search_print_client(struct menu *mi, int listing)
{
struct client_ctx *cc = (struct client_ctx *)mi->ctx;
char flag = ' ';
if (cc->flags & CLIENT_ACTIVE)
flag = '!';
else if (cc->flags & CLIENT_HIDDEN)
flag = '&';
(void)snprintf(mi->print, sizeof(mi->print), "(%d) %c[%s] %s",
(cc->gc) ? cc->gc->num : 0, flag,
(cc->label) ? cc->label : "", cc->name);
}
void
search_print_cmd(struct menu *mi, int listing)
{
struct cmd_ctx *cmd = (struct cmd_ctx *)mi->ctx;
(void)snprintf(mi->print, sizeof(mi->print), "%s", cmd->name);
}
void
search_print_group(struct menu *mi, int listing)
{
struct group_ctx *gc = (struct group_ctx *)mi->ctx;
(void)snprintf(mi->print, sizeof(mi->print),
(group_holds_only_hidden(gc)) ? "%d: [%s]" : "%d: %s",
gc->num, gc->name);
}
void
search_print_text(struct menu *mi, int listing)
{
(void)snprintf(mi->print, sizeof(mi->print), "%s", mi->text);
}
void
search_print_wm(struct menu *mi, int listing)
{
struct cmd_ctx *wm = (struct cmd_ctx *)mi->ctx;
(void)snprintf(mi->print, sizeof(mi->print), "%s [%s]",
wm->name, wm->path);
}

62
strlcat.c Normal file
View File

@ -0,0 +1,62 @@
/* $OpenBSD: strlcat.c,v 1.14 2015/01/15 03:54:12 millert Exp $ */
/*
* Copyright (c) 1998, 2015 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 dsize (unlike strncat, dsize is the
* full size of dst, not space left). At most dsize-1 characters
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t dsize)
{
const char *odst = dst;
const char *osrc = src;
size_t n = dsize;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end. */
while (n-- != 0 && *dst != '\0')
dst++;
dlen = dst - odst;
n = dsize - dlen;
if (n-- == 0)
return(dlen + strlen(src));
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
n--;
}
src++;
}
*dst = '\0';
return(dlen + (src - osrc)); /* count does not include NUL */
}
#endif /* !HAVE_STRLCAT */

57
strlcpy.c Normal file
View File

@ -0,0 +1,57 @@
/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */
/*
* Copyright (c) 1998, 2015 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 string src to buffer dst of size dsize. At most dsize-1
* chars will be copied. Always NUL terminates (unless dsize == 0).
* Returns strlen(src); if retval >= dsize, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 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.7 2013/04/17 18:40:58 tedu 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 <errno.h>
#include <limits.h>
#include <stdlib.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;
int error = 0;
char *ep;
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 */

2
update.sh Executable file
View File

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

75
util.c
View File

@ -18,19 +18,21 @@
* $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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#define MAXARGLEN 20
static void log_msg(const char *, va_list);
void
u_spawn(char *argstr)
@ -38,8 +40,7 @@ u_spawn(char *argstr)
switch (fork()) {
case 0:
u_exec(argstr);
err(1, "%s", argstr);
break;
exit(1);
case -1:
warn("fork");
default:
@ -50,8 +51,10 @@ u_spawn(char *argstr)
void
u_exec(char *argstr)
{
#define MAXARGLEN 20
char *args[MAXARGLEN], **ap = args;
char **end = &args[MAXARGLEN - 1], *tmp;
char **end = &args[MAXARGLEN - 2], *tmp;
char *s = argstr;
while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) {
if (**ap == '\0')
@ -74,8 +77,64 @@ u_exec(char *argstr)
}
}
}
*ap = NULL;
(void)setsid();
(void)execvp(args[0], args);
warn("%s", s);
}
char *
u_argv(char * const *argv)
{
size_t siz = 0;
int i;
char *p;
if (argv == 0)
return NULL;
for (i = 0; argv[i]; i++)
siz += strlen(argv[i]) + 1;
if (siz == 0)
return NULL;
p = xmalloc(siz);
strlcpy(p, argv[0], siz);
for (i = 1; argv[i]; i++) {
strlcat(p, " ", siz);
strlcat(p, argv[i], siz);
}
return p;
}
static void
log_msg(const char *msg, va_list ap)
{
char *fmt;
if (asprintf(&fmt, "%s\n", msg) == -1) {
vfprintf(stderr, msg, ap);
fprintf(stderr, "\n");
} else {
vfprintf(stderr, fmt, ap);
free(fmt);
}
fflush(stderr);
}
void
log_debug(int level, const char *func, const char *msg, ...)
{
char *fmt;
va_list ap;
if (Conf.debug < level)
return;
va_start(ap, msg);
xasprintf(&fmt, "debug%d: %s: %s", level, func, msg);
log_msg(fmt, ap);
free(fmt);
va_end(ap);
}

376
xevents.c
View File

@ -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,78 +43,72 @@ 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, XK_ISO_Level3_Shift };
static void
xev_handle_maprequest(XEvent *ee)
{
XMapRequestEvent *e = &ee->xmaprequest;
struct client_ctx *cc = NULL, *old_cc;
XWindowAttributes xattr;
struct screen_ctx *sc;
struct client_ctx *cc, *old_cc;
if ((old_cc = client_current()) != NULL)
client_ptrsave(old_cc);
LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window);
if ((cc = client_find(e->window)) == NULL) {
XGetWindowAttributes(X_Dpy, e->window, &xattr);
cc = client_new(e->window, screen_fromroot(xattr.root), 1);
}
if ((sc = screen_find(e->parent)) == NULL)
return;
if ((cc->flags & CLIENT_IGNORE) == 0)
client_ptrwarp(cc);
if ((old_cc = client_current(sc)) != NULL)
client_ptr_save(old_cc);
if ((cc = client_find(e->window)) == NULL)
cc = client_init(e->window, NULL);
if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
client_ptr_warp(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);
LOG_DEBUG3("window: 0x%lx", e->window);
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) {
xu_set_wm_state(cc->win, WithdrawnState);
} else {
if (!(cc->flags & CLIENT_HIDDEN))
client_remove(cc);
}
}
XUngrabServer(X_Dpy);
}
static void
@ -122,8 +117,10 @@ xev_handle_destroynotify(XEvent *ee)
XDestroyWindowEvent *e = &ee->xdestroywindow;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL)
client_delete(cc);
client_remove(cc);
}
static void
@ -134,34 +131,40 @@ xev_handle_configurerequest(XEvent *ee)
struct screen_ctx *sc;
XWindowChanges wc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL) {
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);
xu_configure(cc);
client_config(cc);
} else {
/* let it do what it wants, it'll be ours when we map it. */
wc.x = e->x;
@ -183,32 +186,37 @@ xev_handle_propertynotify(XEvent *ee)
struct screen_ctx *sc;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL) {
switch (e->atom) {
case XA_WM_NORMAL_HINTS:
client_getsizehints(cc);
client_get_sizehints(cc);
break;
case XA_WM_NAME:
client_setname(cc);
client_set_name(cc);
break;
case XA_WM_HINTS:
client_wm_hints(cc);
client_draw_border(cc);
break;
case XA_WM_TRANSIENT_FOR:
client_transient(cc);
client_draw_border(cc);
if (cc->gc)
group_movetogroup(cc, cc->gc->num);
break;
default:
/* do nothing */
if (e->atom == ewmh[_NET_WM_NAME])
client_set_name(cc);
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);
if (e->atom == ewmh[_NET_DESKTOP_NAMES]) {
if ((sc = screen_find(e->window)) != NULL)
xu_ewmh_net_desktop_names(sc);
}
}
}
static void
@ -217,75 +225,93 @@ xev_handle_enternotify(XEvent *ee)
XCrossingEvent *e = &ee->xcrossing;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
Last_Event_Time = e->time;
if ((cc = client_find(e->window)) != NULL)
client_setactive(cc, 1);
client_set_active(cc);
}
static void
xev_handle_leavenotify(XEvent *ee)
{
client_leave(NULL);
}
/* We can split this into two event handlers. */
static void
xev_handle_buttonpress(XEvent *ee)
{
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc, fakecc;
struct client_ctx *cc;
struct screen_ctx *sc;
struct mousebinding *mb;
struct bind_ctx *mb;
sc = screen_fromroot(e->root);
cc = client_find(e->window);
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
/* Ignore caps lock and numlock */
e->state &= ~(Mod2Mask | LockMask);
if ((sc = screen_find(e->root)) == NULL)
return;
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
if (e->button == mb->button && e->state == mb->modmask)
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(mb, &Conf.mousebindq, entry) {
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)
mb->cargs->xev = CWM_XEV_BTN;
switch (mb->context) {
case CWM_CONTEXT_CC:
if (((cc = client_find(e->window)) == NULL) &&
((cc = client_current(sc)) == NULL))
return;
cc = &fakecc;
cc->sc = screen_fromroot(e->window);
} else if (cc == NULL) /* (mb->context == MOUSEBIND_CTX_WIN */
return;
(*mb->callback)(cc, e);
(*mb->callback)(cc, mb->cargs);
break;
case CWM_CONTEXT_SC:
(*mb->callback)(sc, mb->cargs);
break;
case CWM_CONTEXT_NONE:
(*mb->callback)(NULL, mb->cargs);
break;
}
}
static void
xev_handle_buttonrelease(XEvent *ee)
{
struct client_ctx *cc;
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc;
if ((cc = client_current()) != NULL)
group_sticky_toggle_exit(cc);
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((cc = client_find(e->window)) != NULL) {
if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) {
cc->flags &= ~CLIENT_HIGHLIGHT;
client_draw_border(cc);
}
}
}
static void
xev_handle_keypress(XEvent *ee)
{
XKeyEvent *e = &ee->xkey;
struct client_ctx *cc = NULL, fakecc;
struct keybinding *kb;
struct client_ctx *cc;
struct screen_ctx *sc;
struct bind_ctx *kb;
KeySym keysym, skeysym;
int modshift;
unsigned int modshift;
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1);
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
/* we don't care about caps lock and numlock here */
e->state &= ~(LockMask | Mod2Mask);
if ((sc = screen_find(e->root)) == NULL)
return;
TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
if (keysym != kb->keysym && skeysym == kb->keysym)
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
if (keysym != kb->press.keysym && skeysym == kb->press.keysym)
modshift = ShiftMask;
else
modshift = 0;
@ -293,28 +319,30 @@ 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 (((cc = client_find(e->window)) == NULL) &&
(cc = client_current()) == NULL)
kb->cargs->xev = CWM_XEV_KEY;
switch (kb->context) {
case CWM_CONTEXT_CC:
if (((cc = client_find(e->subwindow)) == NULL) &&
((cc = client_current(sc)) == NULL))
return;
} else {
cc = &fakecc;
cc->sc = screen_fromroot(e->window);
(*kb->callback)(cc, kb->cargs);
break;
case CWM_CONTEXT_SC:
(*kb->callback)(sc, kb->cargs);
break;
case CWM_CONTEXT_NONE:
(*kb->callback)(NULL, kb->cargs);
break;
}
(*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)
@ -322,26 +350,31 @@ 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();
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
if (keysym != XK_Alt_L && keysym != XK_Alt_R)
if ((sc = screen_find(e->root)) == NULL)
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]) {
if ((cc = client_current(sc)) != NULL) {
if (sc->cycling) {
sc->cycling = 0;
client_mtf(cc);
}
if (cc->flags & CLIENT_HIGHLIGHT) {
cc->flags &= ~CLIENT_HIGHLIGHT;
client_draw_border(cc);
}
}
XUngrabKeyboard(X_Dpy, CurrentTime);
break;
}
}
}
@ -349,34 +382,69 @@ 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);
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) == NULL)
return;
if (e->message_type == xa_wm_change_state && e->format == 32 &&
e->data.l[0] == IconicState)
client_hide(cc);
if (e->message_type == cwmh[WM_CHANGE_STATE]) {
if ((cc = client_find(e->window)) != NULL) {
if (e->data.l[0] == IconicState)
client_hide(cc);
}
} else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) {
if ((cc = client_find(e->window)) != NULL) {
client_close(cc);
}
} else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) {
if ((cc = client_find(e->window)) != NULL) {
if ((old_cc = client_current(NULL)) != NULL)
client_ptr_save(old_cc);
client_show(cc);
client_ptr_warp(cc);
}
} else if (e->message_type == ewmh[_NET_WM_DESKTOP]) {
if ((cc = client_find(e->window)) != NULL) {
/*
* The EWMH spec states that if the cardinal returned
* is 0xFFFFFFFF (-1) then the window should appear
* on all desktops, in our case, group 0.
*/
if (e->data.l[0] == (unsigned long)-1)
group_movetogroup(cc, 0);
else
if (e->data.l[0] >= 0 &&
e->data.l[0] < Conf.ngroups)
group_movetogroup(cc, e->data.l[0]);
}
} else if (e->message_type == ewmh[_NET_WM_STATE]) {
if ((cc = client_find(e->window)) != NULL) {
xu_ewmh_handle_net_wm_state_msg(cc,
e->data.l[0], e->data.l[1], e->data.l[2]);
}
} else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) {
if ((sc = screen_find(e->window)) != NULL) {
if (e->data.l[0] >= 0 &&
e->data.l[0] < Conf.ngroups)
group_only(sc, e->data.l[0]);
}
}
}
static void
xev_handle_randr(XEvent *ee)
{
XRRScreenChangeNotifyEvent *rev = (XRRScreenChangeNotifyEvent *)ee;
XRRScreenChangeNotifyEvent *e = (XRRScreenChangeNotifyEvent *)ee;
struct screen_ctx *sc;
int i;
i = XRRRootToScreen(X_Dpy, rev->root);
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->which == (u_int)i) {
XRRUpdateConfiguration(ee);
screen_update_geometry(sc, rev->width, rev->height);
screen_init_xinerama(sc);
}
}
LOG_DEBUG3("size: %d/%d", e->width, e->height);
if ((sc = screen_find(e->root)) == NULL)
return;
XRRUpdateConfiguration(ee);
screen_update_geometry(sc);
screen_assert_clients_within(sc);
}
/*
@ -387,15 +455,15 @@ static void
xev_handle_mappingnotify(XEvent *ee)
{
XMappingEvent *e = &ee->xmapping;
struct keybinding *kb;
struct screen_ctx *sc;
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_ungrab(&Conf, kb);
LOG_DEBUG3("window: 0x%lx", e->window);
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
@ -404,22 +472,22 @@ xev_handle_expose(XEvent *ee)
XExposeEvent *e = &ee->xexpose;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL && e->count == 0)
client_draw_border(cc);
}
volatile sig_atomic_t xev_quit = 0;
void
xev_loop(void)
xev_process(void)
{
XEvent e;
while (xev_quit == 0) {
while (XPending(X_Dpy)) {
XNextEvent(X_Dpy, &e);
if (e.type - Randr_ev == RRScreenChangeNotify)
if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify)
xev_handle_randr(&e);
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL))
(*xev_handlers[e.type])(&e);
}
}

View File

@ -18,14 +18,17 @@
* $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,26 @@ 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);
return p;
}
void
xfree(void *p)
void *
xreallocarray(void *ptr, size_t nmemb, size_t size)
{
free(p);
void *p;
p = reallocarray(ptr, nmemb, size);
if (p == NULL)
errx(1, "xreallocarray: out of memory (new_size %zu bytes)",
nmemb * size);
return p;
}
char *
@ -66,5 +81,30 @@ 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 = xvasprintf(ret, fmt, ap);
va_end(ap);
return i;
}
int
xvasprintf(char **ret, const char *fmt, va_list ap)
{
int i;
i = vasprintf(ret, fmt, ap);
if (i == -1)
err(1, "vasprintf");
return i;
}

683
xutil.c
View File

@ -18,160 +18,54 @@
* $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_ptr_get(Window win, int *x, int *y)
{
return (XGrabPointer(X_Dpy, win, False, mask,
GrabModeAsync, GrabModeAsync,
None, curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
Window w0, w1;
int tmp0, tmp1;
unsigned int tmp2;
int
xu_ptr_regrab(int mask, Cursor curs)
{
return (XChangeActivePointerGrab(X_Dpy, mask,
curs, CurrentTime) == GrabSuccess ? 0 : -1);
XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
}
void
xu_ptr_ungrab(void)
{
XUngrabPointer(X_Dpy, CurrentTime);
}
void
xu_btn_grab(Window win, int mask, u_int btn)
{
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, int mask, u_int btn)
{
int i;
for (i = 0; i < nitems(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);
}
void
xu_ptr_setpos(Window win, int x, int y)
xu_ptr_set(Window win, int x, int y)
{
XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
}
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 < nitems(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 < nitems(ign_mods); i++)
XUngrabKey(X_Dpy, code, (mask | ign_mods[i]), win);
}
void
xu_configure(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);
}
void
xu_sendmsg(Window win, Atom atm, long val)
{
XEvent e;
(void)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_get_prop(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
xu_getstrprop(Window win, Atom atm, char **text) {
xu_get_strprop(Window win, Atom atm, char **text) {
XTextProperty prop;
char **list;
int nitems = 0;
@ -179,8 +73,10 @@ xu_getstrprop(Window win, Atom atm, char **text) {
*text = NULL;
XGetTextProperty(X_Dpy, win, &prop, atm);
if (!prop.nitems)
return (0);
if (!prop.nitems) {
XFree(prop.value);
return 0;
}
if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
&nitems) == Success && nitems > 0 && *list) {
@ -188,7 +84,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 {
@ -196,110 +92,477 @@ xu_getstrprop(Window win, Atom atm, char **text) {
}
XFreeStringList(list);
}
XFree(prop.value);
return (nitems);
}
int
xu_getstate(struct client_ctx *cc, int *state)
{
long *p = NULL;
if (xu_getprop(cc->win, WM_STATE, WM_STATE, 2L, (u_char **)&p) <= 0)
return (-1);
*state = (int)*p;
XFree((char *)p);
return (0);
return nitems;
}
void
xu_setstate(struct client_ctx *cc, int state)
xu_send_clientmsg(Window win, Atom proto, Time ts)
{
long dat[2];
XClientMessageEvent cm;
dat[0] = state;
dat[1] = None;
(void)memset(&cm, 0, sizeof(cm));
cm.type = ClientMessage;
cm.window = win;
cm.message_type = cwmh[WM_PROTOCOLS];
cm.format = 32;
cm.data.l[0] = proto;
cm.data.l[1] = ts;
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);
XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm);
}
void
xu_setwmname(struct screen_ctx *sc)
xu_get_wm_state(Window win, long *state)
{
/*
* set up the _NET_SUPPORTED hint with all netwm atoms that we
* know about.
long *p;
*state = -1;
if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
(unsigned char **)&p) > 0) {
*state = *p;
XFree(p);
}
}
void
xu_set_wm_state(Window win, long state)
{
long data[] = { state, None };
XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
PropModeReplace, (unsigned char *)data, 2);
}
void
xu_xorcolor(XftColor a, XftColor b, XftColor *r)
{
r->pixel = a.pixel ^ b.pixel;
r->color.red = a.color.red ^ b.color.red;
r->color.green = a.color.green ^ b.color.green;
r->color.blue = a.color.blue ^ b.color.blue;
r->color.alpha = 0xffff;
}
void
xu_atom_init(void)
{
char *cwmhints[] = {
"WM_STATE",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
"WM_PROTOCOLS",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"WM_CHANGE_STATE",
};
char *ewmhints[] = {
"_NET_SUPPORTED",
"_NET_SUPPORTING_WM_CHECK",
"_NET_ACTIVE_WINDOW",
"_NET_CLIENT_LIST",
"_NET_CLIENT_LIST_STACKING",
"_NET_NUMBER_OF_DESKTOPS",
"_NET_CURRENT_DESKTOP",
"_NET_DESKTOP_VIEWPORT",
"_NET_DESKTOP_GEOMETRY",
"_NET_VIRTUAL_ROOTS",
"_NET_SHOWING_DESKTOP",
"_NET_DESKTOP_NAMES",
"_NET_WORKAREA",
"_NET_WM_NAME",
"_NET_WM_DESKTOP",
"_NET_CLOSE_WINDOW",
"_NET_WM_STATE",
"_NET_WM_STATE_STICKY",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_HIDDEN",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_DEMANDS_ATTENTION",
"_NET_WM_STATE_SKIP_PAGER",
"_NET_WM_STATE_SKIP_TASKBAR",
"_CWM_WM_STATE_FREEZE",
};
XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
}
/* Root Window Properties */
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 *)Conf.wmname, strlen(Conf.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_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_workarea(struct screen_ctx *sc)
{
unsigned long *workarea;
int i, ngroups = Conf.ngroups;
workarea = xreallocarray(NULL, ngroups * 4, sizeof(unsigned long));
for (i = 0; i < ngroups; i++) {
workarea[4 * i + 0] = sc->work.x;
workarea[4 * i + 1] = sc->work.y;
workarea[4 * i + 2] = sc->work.w;
workarea[4 * i + 3] = sc->work.h;
}
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea,
ngroups * 4);
free(workarea);
}
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 = xreallocarray(NULL, 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_client_list_stacking(struct screen_ctx *sc)
{
struct client_ctx *cc;
Window *winlist;
int i = 0, j;
TAILQ_FOREACH(cc, &sc->clientq, entry)
i++;
if (i == 0)
return;
j = i;
winlist = xreallocarray(NULL, i, sizeof(*winlist));
TAILQ_FOREACH(cc, &sc->clientq, entry)
winlist[--j] = cc->win;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST_STACKING],
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_number_of_desktops(struct screen_ctx *sc)
{
long ndesks = Conf.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, _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));
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
}
unsigned long
xu_getcolor(struct screen_ctx *sc, char *name)
void
xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
{
XColor color, tmp;
/* We don't support virtual roots, so delete if set by previous wm. */
XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]);
}
if (!XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, sc->which),
name, &color, &tmp)) {
warnx("XAllocNamedColor error: '%s'", name);
return 0;
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_get_prop(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++;
}
}
return color.pixel;
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 = xreallocarray(NULL, 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);
free(p);
}
/* Application Window Properties */
int
xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n)
{
long *p;
if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
(unsigned char **)&p) <= 0)
return 0;
*n = *p;
XFree(p);
return 1;
}
void
xu_freecolor(struct screen_ctx *sc, unsigned long pixel)
xu_ewmh_set_net_wm_desktop(struct client_ctx *cc)
{
XFreeColors(X_Dpy, DefaultColormap(X_Dpy, sc->which), &pixel, 1, 0L);
long num = 0xffffffff;
if (cc->gc)
num = cc->gc->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_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
(unsigned char **)&p)) <= 0)
return NULL;
state = xreallocarray(NULL, *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;
struct handlers {
Atom atom;
int flag;
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 },
{ _NET_WM_STATE_SKIP_PAGER,
CLIENT_SKIP_PAGER,
client_toggle_skip_pager},
{ _NET_WM_STATE_SKIP_TASKBAR,
CLIENT_SKIP_TASKBAR,
client_toggle_skip_taskbar},
{ _CWM_WM_STATE_FREEZE,
CLIENT_FREEZE,
client_toggle_freeze },
};
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].flag))
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_REMOVE:
if (cc->flags & handlers[i].flag)
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_VERT])
client_toggle_vmaximize(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ])
client_toggle_hmaximize(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);
if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_PAGER])
client_toggle_skip_pager(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR])
client_toggle_skip_taskbar(cc);
if (atoms[i] == ewmh[_CWM_WM_STATE_FREEZE])
client_toggle_freeze(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 = xreallocarray(NULL, (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_VERT] &&
oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] &&
oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] &&
oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] &&
oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION] &&
oatoms[i] != ewmh[_NET_WM_STATE_SKIP_PAGER] &&
oatoms[i] != ewmh[_NET_WM_STATE_SKIP_TASKBAR] &&
oatoms[i] != ewmh[_CWM_WM_STATE_FREEZE])
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_VMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT];
if (cc->flags & CLIENT_HMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ];
}
if (cc->flags & CLIENT_URGENCY)
atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION];
if (cc->flags & CLIENT_SKIP_PAGER)
atoms[j++] = ewmh[_NET_WM_STATE_SKIP_PAGER];
if (cc->flags & CLIENT_SKIP_TASKBAR)
atoms[j++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR];
if (cc->flags & CLIENT_FREEZE)
atoms[j++] = ewmh[_CWM_WM_STATE_FREEZE];
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);
}