Compare commits

...

408 Commits
v5.5 ... linux

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

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

ok kn@ op@
2022-10-15 16:06:07 +00:00
Leah Neukirchen
c55191fdc5 README: update 2022-04-30 17:11:23 +02:00
okan
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
okan
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
okan
496bcc879d whitespace 2022-02-26 15:19:18 +00:00
okan
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
okan
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
Leah Neukirchen
35b0da9202 Makefile: use implicit rule for yacc
Closes #15.
2021-11-30 16:43:23 +01:00
okan
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
okan
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
Leah Neukirchen
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
Leah Neukirchen
9fb725f417 README: update 2020-05-22 21:40:11 +02:00
Leah Neukirchen
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
tobias
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
A. Tammy
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
tobias
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
okan
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
okan
6afdd483c7 zap stray tabs 2020-03-24 14:48:29 +00:00
okan
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
okan
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
okan
49f839e194 Trim event_mask to those that the root window actually needs. 2020-03-20 15:16:31 +00:00
okan
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
okan
3d1a8028c2 Remove ColormaskChange from event-mask since there's no event handler. 2020-02-28 13:38:35 +00:00
okan
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
okan
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
Leah Neukirchen
450ab06ab7 parse.y: use "queue.h"
Found by Alexander Müller <ddondy@gmail.com>.
2020-02-10 14:41:00 +01:00
okan
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
okan
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
okan
83de84b7f8 add, then use, xvasprintf, checking for appropriate return. 2020-01-22 19:58:35 +00:00
okan
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
Leah Neukirchen
669e3406ec README: update 2020-01-04 21:45:17 +01:00
Leah Neukirchen
f2a2839cec Makefile: use gpg2 2020-01-04 21:45:05 +01:00
okan
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
okan
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
okan
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
okan
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
okan
9a7528f5b9 Check the atom type on propertynotify before iterating. 2019-03-11 15:25:46 +00:00
okan
e55c0d48fa use screen_find() for xrandr crtc changes 2019-03-10 22:53:11 +00:00
okan
5bc2098c6f Find the managed screen from the parent window for client_current(). 2019-03-10 20:38:28 +00:00
okan
5071baa2aa Print window id in hex; while here, remove unnecessary newline. 2019-03-08 20:33:30 +00:00
okan
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
okan
eab4b7e4b5 extend verbose logging for key/button events 2019-03-08 15:04:39 +00:00
okan
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
okan
2a3c2b5231 add parans for readibility 2019-03-08 13:17:26 +00:00
okan
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
okan
01be5b4e4a check cc->gc directly 2019-03-07 13:24:44 +00:00
okan
823566a653 zip extra lines 2019-03-07 13:24:10 +00:00
okan
aa79351d2e gc clientq inside groups, instead use the better maintained one per-screen 2019-03-07 13:14:41 +00:00
okan
b26202724a shuffle deck chairs: rename group actions to match intent for clarity 2019-03-07 12:54:21 +00:00
okan
9efa6c8c85 same thing as screen_find() 2019-03-06 13:32:19 +00:00
okan
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
okan
9d5b0e5d22 fix a few misplaced (and misnamed) ewmh root window functions 2019-03-04 14:48:59 +00:00
okan
fda68a40de _NET_WORKAREA needs ngroups, so screen_update_geometry() needs to come after
conf_group().
2019-03-04 14:36:02 +00:00
okan
0c0551b8bf simplify xftcolor config 2019-03-04 13:33:39 +00:00
okan
9d25218458 Tie group number and name together during config. 2019-03-01 14:32:01 +00:00
Leah Neukirchen
412b0c9ef4 README: update links 2019-03-01 12:29:38 +01:00
okan
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
okan
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
okan
8cd6d1154c Selectively hide and show clients based on state; merge client_unhide() and
client_show().
2019-02-28 13:11:53 +00:00
Leah Neukirchen
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
okan
cd4be1c17a Add a configtest flag (-n).
based on a diff from Sascha Paunovic.
2019-02-25 18:07:48 +00:00
okan
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
okan
43cd19378e simplify screen 'area' usage for initial client placement 2019-02-23 19:17:17 +00:00
okan
953cf1ce89 restore order from before r1.248 (vtile/vtile containment changes). 2019-02-22 20:52:13 +00:00
okan
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
okan
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
okan
c307e37dcb fix missing includes 2019-02-13 15:43:24 +00:00
okan
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
okan
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
okan
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
okan
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
okan
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
okan
b4d4eba6af cvsimport
* refs/heads/master:
  Remove unused prototype; from Ross L Richardson.
2018-07-16 14:19:23 +00:00
okan
9999c3e6e0 Remove unused prototype; from Ross L Richardson. 2018-07-16 14:19:23 +00:00
Leah Neukirchen
a9dbac8209 README: update 2018-05-14 15:31:34 +02:00
Leah Neukirchen
fae50566fc Makefile: drop unused fontconfig dependency 2018-05-14 14:47:24 +02:00
anton
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
anton
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
okan
b1929b5ed6 Store the screen's visual type and colormap. 2018-02-13 15:43:15 +00:00
okan
13763662c9 Consolidate region 'view' and 'area'. 2018-02-13 15:06:22 +00:00
okan
e6bf7429b3 limit scope of screen_apply_gap() 2018-02-09 20:08:07 +00:00
okan
1a5f80bd0b Clean up conf_file/homedir and conf_init() bits. 2018-02-09 19:54:54 +00:00
Leah Neukirchen
c6745ee21c calmwm: replace INFTIM with -1 2018-02-09 20:11:41 +01:00
okan
f3211427c1 Use screen's saved view instead of re-querying the server. 2018-02-06 15:05:20 +00:00
okan
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
okan
14c17b5f9b Slightly expand and expose verbose debugging. 2018-02-04 22:56:26 +00:00
okan
8623c7add7 add debugging for x events 2018-02-02 13:50:22 +00:00
okan
34e15dbd7a Add a simple debug logging mechanism. 2018-02-02 13:40:55 +00:00
okan
9bf750b054 Simplification; use asprintf where appropriate now. 2018-02-02 13:27:25 +00:00
okan
174537f29e Use func attributes where appropriate. 2018-02-01 15:17:51 +00:00
okan
f34e659ca7 Fix wins comparison declaration since it's unsigned from XQueryTree(). 2018-01-23 16:18:59 +00:00
okan
03a2e9cf05 Generate name_to_func[] in a clean and readable fashion. 2018-01-23 16:00:21 +00:00
okan
5324f9a4e3 Shrink tier[] by one after removing matchname in r1.55. 2018-01-23 13:51:39 +00:00
okan
8675b5e158 If the requested group number is invalid, bail but don't kill cwm. 2018-01-23 13:48:49 +00:00
okan
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
okan
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
okan
5672d5d203 Typo, from Julien Steinhauser. 2017-12-30 22:25:09 +00:00
okan
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
okan
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
okan
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
okan
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
okan
5ddaed415c add helper function client_show to bring together like actions for unhide/raise 2017-12-29 12:54:54 +00:00
okan
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
okan
3d7c82936e Use poll and XNextEvent to replace XNextEvent blocking inside the x11 event
handler.
2017-12-27 17:04:35 +00:00
okan
03e5d86952 zap stray that snuck in 2017-12-22 21:30:01 +00:00
okan
dfaf44c0ac Return the connection number for the display. 2017-12-22 21:27:45 +00:00
okan
c5d03b0853 Fix a few comments and while here, wrap some long lines. 2017-12-22 21:21:44 +00:00
okan
18a53717ae Use a variable to keep track of flags for menu_filter(). 2017-12-19 19:38:43 +00:00
okan
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
okan
5e5d06f063 remove duplicate ExposureMask 2017-12-15 13:13:17 +00:00
okan
64ff5f399a move variable declarations up, to match everything else 2017-12-13 15:10:17 +00:00
okan
add109c006 clean up after previous (noprompt removal) 2017-12-12 15:37:16 +00:00
okan
888f6c5319 Unconditionally show prompt on menus, regardless of invocation. 2017-12-11 20:58:18 +00:00
okan
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
okan
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
okan
f0524fe07a give command and group menus their own match callbacks 2017-12-07 16:03:10 +00:00
okan
b06ddae624 stash dir into conf since it'll be of use 2017-12-07 15:47:14 +00:00
okan
0d76265e23 organize this a bit better 2017-12-07 15:40:54 +00:00
okan
592e8efaee spacing 2017-12-07 15:39:47 +00:00
okan
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
okan
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
Leah Neukirchen
c36c5063e8 README: update 2017-10-17 19:01:20 +02:00
okan
7d7e256396 Case matters for menu matching on executables; from ben@lloyd.im. 2017-09-06 14:15:13 +00:00
okan
087b803b45 cvsimport
* refs/heads/master:
  Case matters for menu matching on executables; from ben@lloyd.im.
2017-09-06 14:15:13 +00:00
Leah Neukirchen
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
okan
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
okan
60ed85c50f remove extra parentheses 2017-07-14 18:01:46 +00:00
okan
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
okan
aefc0bf0fb rename one function, matching others, to help upcoming change 2017-07-12 16:56:26 +00:00
Leah Neukirchen
8908e5ec42 drop fgetln.c 2017-07-11 13:17:45 +02:00
Leah Neukirchen
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
okan
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
okan
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
okan
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
okan
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
okan
16a2ccc225 drop obsolete comment 2017-05-09 18:43:40 +00:00
okan
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
okan
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
okan
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
okan
4d85e2e2a6 Switch bwidth type; unfortunately X11 is inconsistent. 2017-04-26 21:10:54 +00:00
Leah Neukirchen
a8527b5661 calmwm: sighdlr: use -1 instead of WAIT_ANY 2017-04-25 22:26:57 +02:00
okan
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
okan
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
okan
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
okan
ffcfff3dea search_match_path() isn't supposed to return anything. 2017-04-24 15:06:11 +00:00
okan
56b44257a5 Move queue init to caller so it's only called once in a few cases. 2017-04-24 13:35:25 +00:00
okan
15a6fcc851 sort 2017-04-24 13:31:19 +00:00
okan
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
okan
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
okan
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
okan
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
okan
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
okan
48b465bcff cvsimport
* refs/heads/master:
  Typo on binding name; from Redouan Ait Mallouk.
2017-01-15 21:07:44 +00:00
Leah Neukirchen
7ce071fdaa Leahize 2017-01-23 21:21:58 +01:00
okan
f908118a3a Typo on binding name; from Redouan Ait Mallouk. 2017-01-15 21:07:44 +00:00
okan
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
okan
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
okan
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
Christian Neukirchen
44216b65c9 add config migration script 2016-12-16 14:31:33 +01:00
Christian Neukirchen
840dc39292 fix build 2016-12-16 14:29:50 +01:00
Christian Neukirchen
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
okan
8b4666cb92 stray newlines 2016-12-06 21:59:33 +00:00
okan
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
okan
89e4e7fb14 Consistent use of menuq_add for ssh menu. 2016-12-06 21:09:22 +00:00
okan
2ae4797297 Now that dim.{x,y} are available early, use them before requiring a
MotionNotify event.
2016-12-06 21:03:58 +00:00
okan
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
okan
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
okan
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
okan
5b7e66a13b use the correct type 2016-12-02 16:50:19 +00:00
okan
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
okan
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
okan
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
okan
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
okan
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
okan
db02592e5c revert previous; upcoming changes will hopefully deal with these more
naturally.
2016-11-15 00:22:02 +00:00
okan
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
okan
ed22d7a944 Define callbacks, then default bindings. 2016-11-14 21:25:59 +00:00
okan
64e133b289 Reorganize for upcoming changes. 2016-11-14 19:56:05 +00:00
okan
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
okan
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
okan
db93599a0f Make it clear these are flags. 2016-10-24 20:44:08 +00:00
okan
972e28d58c Remove duplicate check that strsubmatch() already does; while here, fix a
comment.
2016-10-24 18:57:12 +00:00
okan
eb43101591 Sprinkle __func__ in appropriate error messages. 2016-10-24 17:39:38 +00:00
okan
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
okan
b1af1bedd0 clean up search_match_client(); no behaviour change 2016-10-22 19:16:43 +00:00
okan
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
okan
38eac7d7e9 remove another unused proto 2016-10-12 16:11:15 +00:00
okan
15bf703e1c Rename 2 kbfunc to match closer to what they do 2016-10-06 14:53:52 +00:00
okan
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
okan
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
okan
e7dcb17e1e More accurate to say 'toggle', rather than 'select', for group[n]/nogroup. 2016-10-05 14:01:23 +00:00
okan
c131620d7b Add CM-a for 'nogroup' (CM-0 stays for now); update manpage to reflect. 2016-10-05 13:35:17 +00:00
okan
d40820d3f3 Stash wmname into conf. 2016-10-05 13:10:59 +00:00
okan
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
okan
5c13775d31 Calculate client nameqlen in client_setname(), the only place it's
needed/used.
2016-10-04 15:52:32 +00:00
okan
9306c1fbd3 Turn CALMWM_NGROUPS define into variable, ngroups. 2016-10-04 15:18:20 +00:00
okan
8aa5033d12 Start simplifying menu code; and in turn, remove a cursor no longer
needed.
2016-10-03 18:43:49 +00:00
okan
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
okan
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
okan
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
okan
1d3a6905f1 remove unused proto 2016-09-30 21:44:51 +00:00
okan
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
okan
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
okan
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
okan
f63b2e2341 Switch to XWindowEvent() pulling out events that match the mask *and*
window.
2016-09-30 15:05:02 +00:00
okan
24d6348e52 no need to unmap menu window again 2016-09-29 00:30:40 +00:00
okan
0e8c706633 Mechanical change: move screen menu bits to their own struct. 2016-09-29 00:21:55 +00:00
okan
92b81d3df5 Inline Xft draw and extents wrappers; too much abstraction. 2016-09-28 17:06:33 +00:00
okan
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
okan
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
okan
3947f1268a Allow ctrl-[ for abort (esc); from Benjamin Scher Purcell 2016-09-20 19:58:54 +00:00
okan
08631748fd de-static client_inbound() 2016-09-20 19:11:19 +00:00
okan
74e4a3c60b remove debug that accidentally snuck in 2016-09-20 18:23:12 +00:00
okan
9cf3174696 Get rid of curcc, instead cycle through the queue; removes the need for
client_none().
2016-09-20 18:21:32 +00:00
okan
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
okan
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
okan
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
okan
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
okan
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
okan
9124a561e3 init label 2016-09-13 17:42:58 +00:00
okan
a94254137c Limit mouse resize to hints within the client; matches kbd resize
behaviour.
2016-09-13 13:42:28 +00:00
okan
e1234b75b8 Switch to just malloc since we need initialize most everything anyway. 2016-09-12 13:48:41 +00:00
okan
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
okan
c82c3f0835 Simplify group_holds_only_hidden(); from Vadim Vygonets. 2016-09-02 16:07:11 +00:00
okan
67a9eaa440 Simplify toggling flags; from Vadim Vygonets. 2016-09-02 15:08:44 +00:00
okan
44f3fefe11 Do not draw borders on ignored clients when returning from fullscreen;
from Vadim Vygonets.
2016-09-01 18:38:52 +00:00
okan
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
okan
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
okan
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
matthieu
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
matthieu
5015c850bd add a column to previous commit for consitency. 2016-08-13 09:59:48 +00:00
matthieu
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
Christian Neukirchen
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
okan
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
Christian Neukirchen
a889f32af6 menu: menuq_add: avoid passing null pointers to vsnprintf. 2016-04-02 17:21:04 +02:00
okan
4ac6daa27d Fix a typo from r1.201; fixes window_grouptoggle binding. 2015-11-17 15:19:19 +00:00
okan
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
okan
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
okan
e8d77b6890 Inline the only use of mousefunc_sweep_calc. 2015-11-17 14:31:28 +00:00
Christian Neukirchen
669631e5fa Run pledge only on OpenBSD. 2015-11-16 14:47:21 +01:00
okan
323672ace7 more client vs screen context differences 2015-11-12 21:28:03 +00:00
okan
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
okan
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
okan
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
okan
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
okan
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
okan
09565bc423 Use position on root to figure out region. 2015-11-10 22:06:57 +00:00
okan
e20110f344 Start cleaning up name vs function differences; replace magic numbers. 2015-11-10 20:05:33 +00:00
okan
31c74e9157 Clean up unused defines. 2015-11-09 20:15:23 +00:00
okan
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
okan
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
okan
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
okan
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
okan
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
okan
47a10cc055 Lost fix from r1.112; add comment.
Reported (again!) by Peter Kane.
2015-08-28 12:07:28 +00:00
okan
28d4001eca Mechanical change: group->gc 2015-08-27 18:53:14 +00:00
okan
f467838e7b Add consistent checks against NULL. 2015-08-27 18:42:56 +00:00
okan
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
okan
5391659629 Re-add lost chunk in group_cycle from r1.113. 2015-08-27 17:43:44 +00:00
okan
6d53091429 Further simplify _NET_WM_DESKTOP handling using new group_assign(). 2015-08-25 20:35:49 +00:00
okan
871ed39ccf oops; remove left over debug print 2015-08-25 19:52:02 +00:00
okan
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
okan
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
okan
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
okan
97db17d056 Don't allow freeze operations on fullscreen (consistent with what
fullscreen does).
2015-08-24 14:57:19 +00:00
okan
4269ea0463 Sort _NET_WM_STATE Atoms like the spec. 2015-08-24 14:56:10 +00:00
okan
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
okan
ae9306748b Add Xkb modifier to ignore mask; from Alexander Polakov. 2015-08-21 16:53:48 +00:00
okan
cd3bbb1c9c Fix whitespace. 2015-08-21 16:52:37 +00:00
okan
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
okan
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
okan
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
okan
a79253abdc Leave command list order from .cwmrc alone; remove sort. 2015-08-21 15:55:39 +00:00
okan
871c9f24ba Bring group and client cycle closer together. 2015-08-21 15:52:49 +00:00
okan
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
okan
c0f2d0cc75 cvsimport 2015-07-12 14:31:47 +00:00
okan
0d13b7c220 introduce 'groupsearch' for group menu search; matches on either group
number/shortcut and/or name.
2015-07-12 14:31:47 +00:00
okan
5c2decc8d0 simplify menu print text 2015-07-12 14:21:09 +00:00
okan
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
Christian Neukirchen
6716719593 cvsimport 2015-07-01 16:40:15 +02:00
okan
cb900def7f style 2015-07-01 14:36:42 +00:00
okan
8efaf33cfb no longer need to pass down format strings after introducing search_print_cmd 2015-06-30 19:02:24 +00:00
okan
5d8027b75d Introduce a callback for cmd menu printing, special-casing 'lock' and
'term'.
2015-06-30 18:54:12 +00:00
okan
c87953eb64 keep cmdq sorted 2015-06-30 18:44:29 +00:00
okan
6b8b856217 style and spacing nits 2015-06-30 18:42:50 +00:00
okan
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
okan
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
okan
e7b4045ece move client_find down 2015-06-28 19:54:37 +00:00
okan
17720de4ab replace assert usage 2015-06-28 19:50:46 +00:00
okan
18f63629fd region is a _ctx, so name it so 2015-06-26 18:54:25 +00:00
okan
66bc416217 Mechanical change from xinerama to region backed areas. 2015-06-26 17:17:46 +00:00
okan
90288e2fc3 collect 'gap' applications 2015-06-26 16:11:21 +00:00
okan
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
okan
4454948f21 cvsimport 2015-06-09 13:02:15 +00:00
okan
ee400b08a8 typo in comment; from jsteinha [at] iutbayonne.univ-pau.fr 2015-06-09 13:02:15 +00:00
okan
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
okan
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
okan
8888c44888 add client label 2015-06-08 15:34:03 +00:00
okan
9180bb1a06 stash window dimensions 2015-06-08 15:11:29 +00:00
okan
8653c88c90 clean up 2015-06-08 15:08:44 +00:00
okan
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
okan
cd5827491c use the same geometry struct in menus as everywhere else 2015-06-05 18:43:36 +00:00
okan
a4cba7e113 add a flag to indicate a menu list instead of overloading 'prompt' 2015-06-05 15:01:51 +00:00
okan
f22a3b1065 redundant check for 'list' 2015-06-05 14:54:04 +00:00
okan
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
okan
b1c2046efd merge kbd and mouse grouptoggle 2015-05-21 00:37:04 +00:00
okan
31d4fe9f33 fill in mwm defines 2015-05-20 23:54:39 +00:00
kspillner
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
kspillner
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
James McDonald
7c4a8f3b51 Fix stat patch to use lstat(2) and fix logic 2015-04-23 21:42:34 +02:00
James McDonald
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
Christian Neukirchen
da1021c60c queue.h: update. 2015-04-06 19:47:04 +02:00
Christian Neukirchen
ffbfc32999 add reallocarray.c 2015-03-29 18:12:47 +02:00
okan
557cd382dc plug a leak 2015-03-29 00:21:05 +00:00
okan
bc21e4aa37 cvsimport 2015-03-29 00:21:05 +00:00
okan
a4a414b68b Introduce a xreallocarray and convert a few xcalloc instances that do
not require zero'ing.
2015-03-28 23:12:47 +00:00
okan
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
okan
5b64e1540c plug a leak 2015-03-28 21:55:48 +00:00
okan
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
okan
ac42dff6c1 simplify error messages; discussed with doug@ 2015-02-14 18:24:12 +00:00
Christian Neukirchen
668da8493a cvsimport 2015-02-11 14:20:35 +00:00
Christian Neukirchen
81c05b3a51 Merge pull request #8 from afh/pull/macosx
Use _POSIX_HOST_NAME_MAX
2015-02-11 15:19:03 +01:00
Alexis Hildebrandt
9cfb9225ed Use _POSIX_HOST_NAME_MAX
to be able to compile under Mac OS X.
2015-02-11 15:11:43 +01:00
okan
7e0c2e7728 calloc -> malloc 2015-01-24 18:16:59 +00:00
Christian Neukirchen
976d77c76d update imported str*.c 2015-01-24 19:04:46 +01:00
Christian Neukirchen
b7a8c11750 update README 2015-01-24 18:31:20 +01:00
Christian Neukirchen
e4ccad30ce cvsimport 2015-01-24 17:03:58 +00:00
okan
d8fa58bb62 use malloc over calloc here 2015-01-23 20:26:36 +00:00
okan
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
Christian Neukirchen
742732a1c8 cvsimport 2015-01-22 11:06:01 +01:00
okan
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
okan
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
okan
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
Christian Neukirchen
089ec389dd also sign releases with signify 2014-12-15 16:00:31 +01:00
Daniel Néri
56fad8271d Fix reference to OpenBSD's CVS repo web interface 2014-12-15 15:51:15 +01:00
okan
9cada703b9 no need for calloc(1,.. here, malloc is fine since we initialize 2014-10-08 15:31:01 +00:00
okan
501aa2bcf3 cvsimport 2014-10-08 15:31:01 +00:00
okan
836c9f49d6 make group_init work like other *_init's 2014-10-08 12:48:51 +00:00
okan
c700f7e19a cvsimport 2014-09-27 19:04:32 +00:00
okan
e9dbd150ea these have nothing to do with 'sticky', but rather group membership; rename. 2014-09-27 19:04:32 +00:00
okan
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
okan
845e82015c Update _NET_CURRENT_DESKTOP with the screen's group_active->num. 2014-09-23 14:25:08 +00:00
okan
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
okan
2540b3f4fe cvsimport 2014-09-18 13:56:58 +00:00
okan
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
okan
8fd0f43ec2 these client actions are just toggles; less confusing with better names 2014-09-17 18:41:44 +00:00
okan
458dd31b93 ewmh states _NET_WM_STATE_STICKY should not alter position 2014-09-17 18:09:30 +00:00
okan
736d973f46 Use a similarly named check as sticky for hidden check in a group. 2014-09-17 16:32:53 +00:00
okan
7eef4eb63d don't toggle _WM_STATE_HIDDEN here yet 2014-09-17 16:30:21 +00:00
okan
a61812d52d Implement EWMH _NET_WM_STATE_HIDDEN. 2014-09-17 16:00:44 +00:00
okan
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
okan
4b6dc96398 use similiar style for client flags 2014-09-15 13:00:49 +00:00
okan
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
okan
d27fc99784 fold in 'active' into 'flags' 2014-09-10 20:30:38 +00:00
okan
20c1113fdd move the check for an empty queue up during cycle 2014-09-08 21:24:27 +00:00
okan
aac16013d2 name the group client queue appropriately, like other queues 2014-09-08 21:15:14 +00:00
okan
b64ce8558c more style nits and wrapping 2014-09-08 20:37:02 +00:00
okan
bc70374264 since mruq has been folded in, rename mru-named functions 2014-09-08 20:32:40 +00:00
okan
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
okan
a7f3f29ea9 Now that a group knows its screen, only pass down the group_ctx. 2014-09-08 13:51:29 +00:00
okan
5b46f0f7d8 Add screen_ctx to group_ctx, and populate on init. 2014-09-07 20:57:26 +00:00
okan
3d12b6d1d9 more style nits 2014-09-07 19:27:30 +00:00
okan
be091b3523 screen_fromroot -> screen_find 2014-09-07 17:38:38 +00:00
okan
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
Christian Neukirchen
9735927809 update.sh: fix mirror 2014-09-07 17:49:00 +02:00
Christian Neukirchen
9e560bd745 cvsimport 2014-09-07 15:47:44 +00:00
okan
f7af0ebd72 Few style nits; extend 'path' to MAXPATHLEN. 2014-09-06 18:50:43 +00:00
okan
977992626a generic sighandler 2014-09-06 16:24:32 +00:00
okan
140f8b9eba style nit 2014-09-06 16:18:08 +00:00
okan
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
okan
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
okan
87d604f119 bring menu bits closer together 2014-09-02 14:08:39 +00:00
okan
231abb818a add explicit paran and drop some empty lines bringing like functions closer 2014-09-01 18:17:32 +00:00
okan
becc7f260c Merge group_menu into mousefunc_menu_group. 2014-09-01 18:04:58 +00:00
okan
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
okan
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
okan
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
okan
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
okan
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
okan
b31b09dfc2 Purely mechanical; unify 'num', 'no' and 'shortcut'. 2014-08-20 15:15:29 +00:00
okan
30da2211dd Split off group window restacking. 2014-08-20 13:42:27 +00:00
okan
7f45471538 whitespace 2014-08-20 12:35:39 +00:00
okan
5d22b261a6 fix a misleading comment and function name 2014-08-20 12:33:12 +00:00
okan
6c835fd585 Remove unnecessary memset since reload was replaced with restart;
from Kent Spillner.
2014-08-19 18:39:41 +00:00
okan
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
okan
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
Christian Neukirchen
870418ff68 README: mention OS X
Thanks to Alexis Hildebrandt for testing.
2014-06-23 16:40:30 +02:00
Christian Neukirchen
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
okan
e9cb5b743f plug memleak (currently unused func); from Tiago Cunha 2014-04-21 12:52:14 +00:00
okan
a483e23e94 cvsimport 2014-04-21 12:52:14 +00:00
Christian Neukirchen
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
Christian Neukirchen
4935bb73c0 builds on FreeBSD too 2014-03-14 16:43:40 +01:00
Christian Neukirchen
625be76fb9 Makefile: add sign action 2014-03-13 19:24:15 +01:00
28 changed files with 4297 additions and 3576 deletions

View File

@ -3,34 +3,33 @@
PROG= cwm
PREFIX= /usr/local
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 parse.y
kbfunc.c parse.y
OBJS= calmwm.o screen.o xmalloc.o client.o menu.o \
search.o util.o xutil.o conf.o xevents.o group.o \
kbfunc.o mousefunc.o strlcpy.o strlcat.o y.tab.o \
strtonum.o fgetln.o
kbfunc.o strlcpy.o strlcat.o parse.o \
strtonum.o reallocarray.o
PKG_CONFIG?= pkg-config
CPPFLAGS+= `pkg-config --cflags fontconfig x11 xft xinerama xrandr`
CPPFLAGS+= `${PKG_CONFIG} --cflags x11 xft xrandr`
CFLAGS= -Wall -O2 -g -D_GNU_SOURCE
CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE
LDFLAGS+= `pkg-config --libs fontconfig x11 xft xinerama xrandr`
LDFLAGS+= `${PKG_CONFIG} --libs x11 xft xrandr`
MANPREFIX= ${PREFIX}/share/man
MANPREFIX?= ${PREFIX}/share/man
all: ${PROG}
clean:
rm -f ${OBJS} ${PROG} y.tab.c
rm -f ${OBJS} ${PROG} parse.c
y.tab.c: parse.y
yacc parse.y
${PROG}: ${OBJS} y.tab.o
${PROG}: ${OBJS}
${CC} ${OBJS} ${LDFLAGS} -o ${PROG}
.c.o:
@ -45,3 +44,11 @@ install: ${PROG}
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

75
README
View File

@ -1,5 +1,4 @@
This is a port of OpenBSD's excellent cwm[0] to Linux and other
Unices.
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
@ -7,15 +6,17 @@ Unices.
pleasant aesthetic.
This port requires pkg-config, Xft, Xinerama and Xrandr. The included Makefile
should work with both GNU make and BSD make.
should work with both GNU make and BSD make. It has been built successfully on
OpenBSD, FreeBSD, NetBSD, OS X 10.9 and Linux.
This version actively tracks changes in the OpenBSD CVS repository.
Releases are roughly coordinated.
The revision controlled version is at https://github.com/chneukirchen/cwm
Releases can be found at http://chneukirchen.org/releases
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 Freenode to talk about cwm.
You are welcome to join the IRC channel ##cwm on irc.libera.chat
to talk about cwm.
ChangeLog:
@ -82,7 +83,65 @@ Changes made between OpenBSD 5.5 and 5.4
* 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.
--Christian Neukirchen <chneukirchen@gmail.com>
* Support building on FreeBSD and OS X.
* Support for sticky windows (_NET_WM_STATE_STICKY).
* Internal cleanups and bug fixes.
[0]: http://www.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/
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/

167
calmwm.c
View File

@ -18,15 +18,16 @@
* $OpenBSD$
*/
#include <sys/param.h>
#include <sys/types.h>
#include "queue.h"
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <pwd.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@ -35,48 +36,52 @@
#include "calmwm.h"
Display *X_Dpy;
Time Last_Event_Time = CurrentTime;
Atom cwmh[CWMH_NITEMS];
Atom ewmh[EWMH_NITEMS];
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;
struct screen_ctx_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct client_ctx_q Clientq = TAILQ_HEAD_INITIALIZER(Clientq);
int HasRandr, Randr_ev;
struct conf Conf;
const char *homedir;
volatile sig_atomic_t cwm_status;
static void sigchld_cb(int);
void usage(void);
static void sighdlr(int);
static int x_errorhandler(Display *, XErrorEvent *);
static void x_init(const char *);
static void x_restart(char **);
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 *conf_path, *display_name = NULL;
char **cwm_argv;
int ch;
struct passwd *pw;
char *display_name = NULL;
char *fallback;
int ch, xfd, nflag = 0;
struct pollfd pfd[1];
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
warnx("no locale support");
mbtowc(NULL, NULL, MB_CUR_MAX);
cwm_argv = argv;
while ((ch = getopt(argc, argv, "c:d:")) != -1) {
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();
}
@ -84,46 +89,49 @@ 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");
if ((homedir = getenv("HOME")) == NULL || *homedir == '\0') {
pw = getpwuid(getuid());
if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0')
homedir = pw->pw_dir;
else
homedir = "/";
if (parse_config(Conf.conf_file, &Conf) == -1) {
warnx("error parsing config file");
if (nflag)
return 1;
}
if (nflag)
return 0;
if (conf_file == NULL)
xasprintf(&conf_path, "%s/%s", homedir, CONFFILE);
else
conf_path = xstrdup(conf_file);
if (access(conf_path, R_OK) != 0) {
if (conf_file != NULL)
warn("%s", conf_file);
free(conf_path);
conf_path = NULL;
}
conf_init(&Conf);
if (conf_path && (parse_config(conf_path, &Conf) == -1))
warnx("config file %s has errors", conf_path);
free(conf_path);
x_init(display_name);
xfd = x_init(display_name);
cwm_status = CWM_RUNNING;
while (cwm_status == CWM_RUNNING)
xev_process();
x_teardown();
if (cwm_status == CWM_RESTART)
x_restart(cwm_argv);
return (0);
#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;
}
static void
static int
x_init(const char *dpyname)
{
int i;
@ -136,20 +144,15 @@ x_init(const char *dpyname)
XSync(X_Dpy, False);
XSetErrorHandler(x_errorhandler);
HasRandr = XRRQueryExtension(X_Dpy, &Randr_ev, &i);
Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i);
conf_atoms();
xu_atom_init();
conf_cursor(&Conf);
for (i = 0; i < ScreenCount(X_Dpy); i++)
screen_init(i);
}
static void
x_restart(char **args)
{
(void)setsid();
(void)execvp(args[0], args);
return ConnectionNumber(X_Dpy);
}
static void
@ -165,10 +168,7 @@ x_teardown(void)
XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which),
&sc->xftcolor[i]);
XftDrawDestroy(sc->xftdraw);
XftFontClose(X_Dpy, sc->xftfont);
XUnmapWindow(X_Dpy, sc->menuwin);
XDestroyWindow(X_Dpy, sc->menuwin);
XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin);
}
XUngrabPointer(X_Dpy, CurrentTime);
@ -184,8 +184,7 @@ static int
x_wmerrorhandler(Display *dpy, XErrorEvent *e)
{
errx(1, "root window unavailable - perhaps another wm is running?");
return (0);
return 0;
}
static int
@ -201,20 +200,30 @@ x_errorhandler(Display *dpy, XErrorEvent *e)
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;
}
@ -224,7 +233,7 @@ 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);
}

578
calmwm.h
View File

@ -21,11 +21,26 @@
#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 **);
size_t strlcpy(char *, const char *, size_t);
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>
@ -34,10 +49,14 @@ size_t strlcat(char *, const char *, size_t);
#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))
@ -47,73 +66,41 @@ size_t strlcat(char *, const char *, size_t);
#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 BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define MENUMASK (MOUSEMASK|ButtonMotionMask|ExposureMask)
#define MENUGRABMASK (MOUSEMASK|ButtonMotionMask|StructureNotifyMask)
#define KEYMASK (KeyPressMask|ExposureMask)
#define IGNOREMODMASK (LockMask|Mod2Mask)
/* 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
/* menu */
#define CWM_MENU_DUMMY 0x0001
#define CWM_MENU_FILE 0x0002
#define ARG_CHAR 0x0001
#define ARG_INT 0x0002
#define CWM_TILE_HORIZ 0x0001
#define CWM_TILE_VERT 0x0002
#define CWM_GAP 0x0001
#define CWM_NOGAP 0x0002
#define CWM_WIN 0x0001
#define CWM_QUIT 0x0000
#define CWM_RUNNING 0x0001
#define CWM_RESTART 0x0002
union arg {
char *c;
int i;
enum cwm_status {
CWM_QUIT,
CWM_RUNNING,
CWM_EXEC_WM
};
union press {
KeySym keysym;
unsigned int button;
};
enum cursor_font {
CF_DEFAULT,
CF_MOVE,
CF_NORMAL,
CF_QUESTION,
CF_MOVE,
CF_RESIZE,
CF_QUESTION,
CF_NITEMS
};
enum color {
CWM_COLOR_BORDER_ACTIVE,
CWM_COLOR_BORDER_INACTIVE,
@ -133,7 +120,6 @@ struct geom {
int w;
int h;
};
struct gap {
int top;
int bottom;
@ -145,17 +131,17 @@ 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;
Colormap colormap;
unsigned int bwidth; /* border width */
int bwidth; /* border width */
int obwidth; /* original border width */
struct geom geom, savegeom, fullgeom;
struct {
long flags; /* defined hints */
@ -174,6 +160,10 @@ struct client_ctx {
int x; /* x position */
int y; /* y position */
} ptr;
struct {
int h; /* height */
int w; /* width */
} dim;
#define CLIENT_HIDDEN 0x0001
#define CLIENT_IGNORE 0x0002
#define CLIENT_VMAXIMIZED 0x0004
@ -186,94 +176,117 @@ struct client_ctx {
#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 active;
int stackingorder;
struct winname_q nameq;
#define CLIENT_MAXNAMEQLEN 5
int nameqlen;
struct name_q nameq;
char *name;
char *label;
char *matchname;
struct group_ctx *group;
XClassHint ch;
XWMHints *wmh;
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);
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 area;
struct geom view; /* viewable area */
struct geom work; /* workable area, gap-applied */
};
TAILQ_HEAD(region_ctx_q, region_ctx);
TAILQ_HEAD(region_q, region_ctx);
struct screen_ctx {
TAILQ_ENTRY(screen_ctx) entry;
int which;
Window rootwin;
Window menuwin;
int cycling;
int hideall;
int snapdist;
struct geom view; /* viewable area */
struct geom work; /* workable area, gap-applied */
struct gap gap;
struct cycle_entry_q mruq;
struct region_ctx_q regionq;
XftColor xftcolor[CWM_COLOR_NITEMS];
XftDraw *xftdraw;
XftFont *xftfont;
#define CALMWM_NGROUPS 10
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 binding {
TAILQ_ENTRY(binding) entry;
void (*callback)(struct client_ctx *, union arg *);
union arg argument;
struct cargs {
char *cmd;
int flag;
enum {
CWM_XEV_KEY,
CWM_XEV_BTN
} xev;
};
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 press press;
int flags;
int argtype;
union {
KeySym keysym;
unsigned int button;
} press;
};
TAILQ_HEAD(keybinding_q, binding);
TAILQ_HEAD(mousebinding_q, binding);
TAILQ_HEAD(keybind_q, bind_ctx);
TAILQ_HEAD(mousebind_q, bind_ctx);
struct cmd {
TAILQ_ENTRY(cmd) entry;
struct cmd_ctx {
TAILQ_ENTRY(cmd_ctx) entry;
char *name;
char path[MAXPATHLEN];
char *path;
};
TAILQ_HEAD(cmd_q, cmd);
TAILQ_HEAD(cmd_q, cmd_ctx);
TAILQ_HEAD(wm_q, cmd_ctx);
#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;
@ -288,50 +301,71 @@ struct menu {
TAILQ_HEAD(menu_q, menu);
struct conf {
struct keybinding_q keybindingq;
struct mousebinding_q mousebindingq;
struct autogroupwin_q autogroupq;
struct keybind_q keybindq;
struct mousebind_q mousebindq;
struct autogroup_q autogroupq;
struct ignore_q ignoreq;
struct cmd_q cmdq;
#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;
char *color[CWM_COLOR_NITEMS];
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
char known_hosts[MAXPATHLEN];
#define CONF_FONT "sans-serif:pixelsize=14:bold"
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 {
#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)
extern Display *X_Dpy;
extern Time Last_Event_Time;
extern struct screen_ctx_q Screenq;
extern struct client_ctx_q Clientq;
extern struct conf Conf;
extern const char *homedir;
extern int HasRandr, Randr_ev;
enum {
enum cwmh {
WM_STATE,
WM_DELETE_WINDOW,
WM_TAKE_FOCUS,
@ -341,11 +375,12 @@ enum {
WM_CHANGE_STATE,
CWMH_NITEMS
};
enum {
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,
@ -358,162 +393,183 @@ enum {
_NET_WM_DESKTOP,
_NET_CLOSE_WINDOW,
_NET_WM_STATE,
#define _NET_WM_STATES_NITEMS 4
#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 {
enum net_wm_state {
_NET_WM_STATE_REMOVE,
_NET_WM_STATE_ADD,
_NET_WM_STATE_TOGGLE
};
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_applysizehints(struct client_ctx *);
void client_apply_sizehints(struct client_ctx *);
void client_close(struct client_ctx *);
void client_config(struct client_ctx *);
struct client_ctx *client_current(void);
void client_cycle(struct screen_ctx *, int);
void client_cycle_leave(struct screen_ctx *);
void client_delete(struct client_ctx *);
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_fullscreen(struct client_ctx *);
long client_get_wm_state(struct client_ctx *);
void client_getsizehints(struct client_ctx *);
void client_get_sizehints(struct client_ctx *);
void client_hide(struct client_ctx *);
void client_hmaximize(struct client_ctx *);
void client_htile(struct client_ctx *);
void client_lower(struct client_ctx *);
void client_map(struct client_ctx *);
void client_maximize(struct client_ctx *);
void client_msg(struct client_ctx *, Atom, Time);
void client_move(struct client_ctx *);
int client_inbound(struct client_ctx *, int, int);
struct client_ctx *client_init(Window, struct screen_ctx *);
void client_ptrsave(struct client_ctx *);
void client_ptrwarp(struct client_ctx *);
void client_lower(struct client_ctx *);
void client_move(struct client_ctx *);
void client_mtf(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_remove(struct client_ctx *);
void client_resize(struct client_ctx *, int);
void client_send_delete(struct client_ctx *);
void client_set_wm_state(struct client_ctx *, long);
void client_setactive(struct client_ctx *);
void client_setname(struct client_ctx *);
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_urgency(struct client_ctx *);
void client_vmaximize(struct client_ctx *);
void client_vtile(struct client_ctx *);
void client_warp(struct client_ctx *);
void client_wm_hints(struct client_ctx *);
void group_alltoggle(struct screen_ctx *);
void group_autogroup(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_menu(struct screen_ctx *);
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_set_state(struct screen_ctx *);
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_exec_path(struct menu_q *,
struct menu_q *, char *);
void search_match_path_any(struct menu_q *, struct menu_q *,
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);
struct geom screen_find_xinerama(struct screen_ctx *,
int, int, int);
struct screen_ctx *screen_fromroot(Window);
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_fullscreen(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_moveresize(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_cwm_status(struct client_ctx *, union arg *);
void kbfunc_exec(struct client_ctx *, union arg *);
void kbfunc_lock(struct client_ctx *, union arg *);
void kbfunc_menu_cmd(struct client_ctx *, union arg *);
void kbfunc_ssh(struct client_ctx *, union arg *);
void kbfunc_term(struct client_ctx *, union arg *);
void kbfunc_tile(struct client_ctx *, union arg *);
void mousefunc_client_grouptoggle(struct client_ctx *,
union arg *);
void mousefunc_client_move(struct client_ctx *,
union arg *);
void mousefunc_client_resize(struct client_ctx *,
union arg *);
void mousefunc_menu_cmd(struct client_ctx *, union arg *);
void mousefunc_menu_group(struct client_ctx *, union arg *);
void mousefunc_menu_unhide(struct client_ctx *,
union arg *);
void 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 *,
const char *, const char *, int,
void (*)(struct menu_q *, struct menu_q *, char *),
void (*)(struct menu *, int));
void menuq_add(struct menu_q *, void *, const char *, ...);
void menuq_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_atoms(void);
void conf_autogroup(struct conf *, int, const char *);
int conf_bind_kbd(struct conf *, const 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 *);
int conf_cmd_add(struct conf *, const char *,
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);
@ -521,54 +577,56 @@ void conf_grab_mouse(Window);
void conf_init(struct conf *);
void conf_ignore(struct conf *, const char *);
void conf_screen(struct screen_ctx *);
void conf_group(struct screen_ctx *);
void xev_process(void);
void xu_btn_grab(Window, int, unsigned int);
void xu_btn_ungrab(Window);
int xu_getprop(Window, Atom, Atom, long, unsigned char **);
int xu_getstrprop(Window, Atom, char **);
void xu_key_grab(Window, unsigned int, KeySym);
void xu_key_ungrab(Window);
void xu_ptr_getpos(Window, int *, int *);
int xu_ptr_grab(Window, unsigned int, Cursor);
int xu_ptr_regrab(unsigned int, Cursor);
void xu_ptr_setpos(Window, int, int);
void xu_ptr_ungrab(void);
void xu_xft_draw(struct screen_ctx *, const char *,
int, int, int);
int xu_xft_width(XftFont *, const char *, int);
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_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_wm_desktop_viewport(struct screen_ctx *);
void xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *);
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 *, long);
void xu_ewmh_net_desktop_names(struct screen_ctx *, char *,
int);
void xu_ewmh_net_wm_desktop(struct client_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);
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 *xmalloc(size_t);
void *xreallocarray(void *, size_t, size_t);
char *xstrdup(const char *);
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_ */

835
client.c

File diff suppressed because it is too large Load Diff

1146
conf.c

File diff suppressed because it is too large Load Diff

353
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: December 16 2013 $
.Dd $Mdocdate$
.Dt CWM 1
.Os
.Sh NAME
@ -23,126 +23,16 @@
.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:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic C
Control.
.It Ic M
Meta.
.It Ic S
Shift.
.It Ic M1
Left mouse button.
.It Ic M2
Middle mouse button.
.It Ic M3
Right mouse button.
.El
.Pp
.Nm
is very simple in its use.
Most of the actions are initiated via key bindings.
The default key bindings are described below;
their functionality is described in more detail later.
.Pp
.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact
.It Ic CM-Return
Spawn a new terminal.
.It Ic CM-Delete
Lock the screen.
.It Ic M-Return
Hide current window.
.It Ic M-Down
Lower current window.
.It Ic M-Up
Raise current window.
.It Ic M-/
Search for windows.
.It Ic C-/
Search for applications.
.It Ic CM-n
Label current window.
.It Ic M-Tab
Cycle through currently visible windows.
.It Ic MS-Tab
Reverse cycle through currently visible windows.
.It Ic CM-x
Delete current window.
.It Ic CM-[n]
Select group n, where n is 1-9.
.It Ic CM-0
Select all groups.
.It Ic CM-g
Toggle group membership of current window.
.It Ic M-Right
Cycle through active groups.
.It Ic M-Left
Reverse cycle through active groups.
.It Ic CMS-f
Toggle freezing geometry of current window.
.It Ic CM-f
Toggle full-screen mode of current window.
.It Ic CM-m
Toggle maximization of current window.
.It Ic CM-=
Toggle vertical maximization of current window.
.It Ic CMS-=
Toggle horizontal maximization of current window.
.It Ic M-?
Spawn
.Dq exec program
dialog.
.It Ic M-.
Spawn
.Dq ssh to
dialog.
This parses
.Pa $HOME/.ssh/known_hosts
to provide host auto-completion.
.Xr ssh 1
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.
.It Ic CMS-r
Restart the running
.Xr cwm 1 .
.It Ic CMS-q
Quit
.Nm .
.El
.Pp
The default mouse bindings are:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic M-M1
Move current window.
.It Ic CM-M1
Toggle group membership of current window.
.It Ic M-M2
Resize current window
.It Ic M-M3
Lower current window.
.It Ic CMS-M3
Hide current window.
.El
.Pp
The options for
.Nm
are as follows:
The options are as follows:
.Bl -tag -width Ds
.It Fl c Ar file
Specify an alternative configuration file.
@ -158,38 +48,159 @@ however,
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
.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 .
.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.
.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 key.
.It Ic M
Meta key.
.It Ic S
Shift key.
.It Ic 4
Mod4 (windows) key.
.It Ic M1
Left mouse button.
.It Ic M2
Middle mouse button.
.It Ic M3
Right mouse button.
.El
.Pp
The default key bindings are:
.Pp
.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact
.It Ic CM-Return
Spawn a new terminal.
.It Ic CM-Delete
Lock the screen.
.It Ic M-Return
Hide current window.
.It Ic M-Down
Lower current window.
.It Ic M-Up
Raise current window.
.It Ic M-slash
Search for windows.
.It Ic C-slash
Search for applications.
.It Ic CM-n
Label current window.
.It Ic M-Tab
Cycle through currently visible windows.
.It Ic MS-Tab
Reverse cycle through currently visible windows.
.It Ic CM-x
Close current window.
.It Ic CM-[n]
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
Cycle through active groups.
.It Ic M-Left
Reverse cycle through active groups.
.It Ic CMS-f
Toggle freezing geometry of current window.
.It Ic CM-s
Toggle stickiness of current window.
.It Ic CM-f
Toggle full-screen 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-equal
Toggle horizontal maximization of current window.
.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-period
Spawn
.Dq ssh to
dialog.
This parses
.Pa $HOME/.ssh/known_hosts
to provide host auto-completion.
.Xr ssh 1
will be executed via the configured terminal emulator.
.It Ic CM-w
Spawn
.Dq exec WindowManager
menu, allowing a switch to another window manager.
.It Ic CMS-r
Restart.
.It Ic CMS-q
Quit.
.El
.Pp
The default mouse bindings are:
.Pp
.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact
.It Ic M-M1
Move current window.
.It Ic CM-M1
Toggle group membership of current window.
.It Ic M-M2
Resize current window
.It Ic M-M3
Lower current window.
.It Ic CMS-M3
Hide current window.
.El
.Pp
The following key bindings may be used to navigate
search and exec dialogs:
.Pp
.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,
@ -202,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.
@ -244,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
@ -265,8 +259,11 @@ 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

2
cwm.pub Normal file
View File

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

637
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: December 16 2013 $
.Dd $Mdocdate$
.Dt CWMRC 5
.Os
.Sh NAME
@ -37,10 +37,8 @@ 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 windowclass
.It Ic autogroup Ar group windowname,windowclass
.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
@ -62,75 +60,91 @@ used to override
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 key binding, or replacement of a default
key binding.
.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 a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h .
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 key binding.
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.
.Pp
.It Ic color groupborder Ar color
Set the color of the border while grouping a window.
.Pp
.It Ic color inactiveborder Ar color
Set the color of the inactive border.
.Pp
.It Ic color menubg Ar color
Set menu background color.
.Pp
.It Ic color menufg Ar color
Set menu foreground color.
.Pp
.It Ic color urgencyborder Ar color
Set the color of the border of a window indicating urgency.
.Pp
.It Ic color ungroupborder Ar color
Set the color of the border while ungrouping a window.
.Pp
.It Ic command Ar name path
Every
.Ar name
@ -138,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
@ -154,13 +168,11 @@ The defaults are
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
@ -171,71 +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.
.It 4
Scroll up mouse button.
.It 5
Scroll down 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"
@ -259,231 +477,24 @@ ignore xapm
ignore xclock
# Key bindings
bind CM-r label
bind CS-Return "xterm -e top"
bind 4-o unmap
bind CM-equal unmap
bind CMS-equal unmap
bind C4-equal vmaximize
bind C4S-equal hmaximize
bind M-1 grouponly1
bind M-2 grouponly2
bind M-3 grouponly3
bind MS-1 movetogroup1
bind MS-2 movetogroup2
bind MS-3 movetogroup3
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
# Mouse bindings
mousebind M-2 window_lower
mousebind M-3 window_resize
bind-mouse M-2 window-lower
bind-mouse M-3 window-resize
.Ed
.Sh BIND COMMAND LIST
.Bl -tag -width 18n -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 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 fullscreen
Full-screen current window (gap + border removed).
.It maximize
Maximize current window (gap + border honored).
.It vmaximize
Vertically maximize current window (gap + border honored).
.It hmaximize
Horizontally maximize current window (gap + border honored).
.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.
.It htile
Current window is placed at the top of the screen and maximized
horizontally, other windows in its group share remaining screen space.
.It vtile
Current window is placed on the left of the screen and maximized
vertically, other windows in its group share remaining screen space.
.El
.Sh MOUSEBIND COMMAND LIST
.Bl -tag -width 18n -compact
.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 cyclegroup
Forward cycle through groups.
.It rcyclegroup
Reverse cycle through groups.
.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

106
fgetln.c
View File

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

540
group.c
View File

@ -19,12 +19,12 @@
* $OpenBSD$
*/
#include <sys/param.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>
@ -32,78 +32,83 @@
#include "calmwm.h"
static void group_assign(struct group_ctx *, 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
void
group_assign(struct group_ctx *gc, struct client_ctx *cc)
{
if (gc == NULL)
gc = TAILQ_FIRST(&cc->sc->groupq);
if (cc->group == gc)
return;
if ((gc != NULL) && (gc->num == 0))
gc = NULL;
if (cc->group != NULL)
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
cc->gc = gc;
TAILQ_INSERT_TAIL(&gc->clients, cc, group_entry);
cc->group = gc;
xu_ewmh_net_wm_desktop(cc);
xu_ewmh_set_net_wm_desktop(cc);
}
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;
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 = xcalloc((gc->highstack + 1), sizeof(*winlist));
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) {
@ -113,56 +118,33 @@ group_show(struct screen_ctx *sc, struct group_ctx *gc)
}
}
XRestackWindows(X_Dpy, winlist, gc->nhidden);
XRestackWindows(X_Dpy, winlist, nwins);
free(winlist);
gc->hidden = 0;
group_setactive(sc, gc->shortcut);
}
void
group_init(struct screen_ctx *sc)
{
int i;
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);
for (i = 0; i < CALMWM_NGROUPS; i++) {
TAILQ_INIT(&sc->groups[i].clients);
sc->groups[i].hidden = 0;
sc->groups[i].shortcut = i;
TAILQ_INSERT_TAIL(&sc->groupq, &sc->groups[i], entry);
}
xu_ewmh_net_wm_desktop_viewport(sc);
xu_ewmh_net_wm_number_of_desktops(sc);
xu_ewmh_net_showing_desktop(sc);
xu_ewmh_net_virtual_roots(sc);
group_setactive(sc, 1);
}
void
group_set_state(struct screen_ctx *sc)
group_init(struct screen_ctx *sc, int num, const char *name)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry)
group_fix_hidden_state(gc);
gc = xmalloc(sizeof(*gc));
gc->sc = sc;
gc->name = xstrdup(name);
gc->num = num;
TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
if (num == 1)
group_set_active(gc);
}
static void
group_setactive(struct screen_ctx *sc, long idx)
void
group_set_active(struct group_ctx *gc)
{
sc->group_active = &sc->groups[idx];
struct screen_ctx *sc = gc->sc;
xu_ewmh_net_current_desktop(sc, idx);
sc->group_active = gc;
xu_ewmh_net_current_desktop(sc);
}
void
@ -171,83 +153,61 @@ group_movetogroup(struct client_ctx *cc, int idx)
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
errx(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_assign(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 = sc->group_active;
if (gc == cc->group) {
if (cc->gc == gc) {
group_assign(NULL, cc);
cc->flags |= CLIENT_UNGROUP;
} else {
group_assign(gc, cc);
cc->flags |= CLIENT_GROUP;
}
client_draw_border(cc);
}
void
group_sticky_toggle_exit(struct client_ctx *cc)
{
cc->flags &= ~CLIENT_HIGHLIGHT;
client_draw_border(cc);
}
/*
* if group_hidetoggle would produce no effect, toggle the group's hidden state
*/
static void
group_fix_hidden_state(struct group_ctx *gc)
int
group_holds_only_sticky(struct group_ctx *gc)
{
struct 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)
errx(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);
/* make clients stick to empty group */
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
@ -255,208 +215,164 @@ group_only(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
errx(1, "group_only: index out of range (%d)", idx);
if (sc->group_last != sc->group_active)
sc->group_last = sc->group_active;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->shortcut == idx)
group_show(sc, gc);
if (gc->num == idx)
group_show(gc);
else
group_hide(sc, gc);
group_hide(gc);
}
}
/*
* 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);
}
void
group_menu(struct screen_ctx *sc)
{
struct group_ctx *gc;
struct menu *mi;
struct menu_q menuq;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (TAILQ_EMPTY(&gc->clients))
continue;
menuq_add(&menuq, gc, gc->hidden ? "%d: [%s]" : "%d: %s",
gc->shortcut, sc->group_names[gc->shortcut]);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL && mi->ctx != NULL) {
gc = (struct group_ctx *)mi->ctx;
(gc->hidden) ? group_show(sc, gc) : group_hide(sc, gc);
}
menuq_clear(&menuq);
}
void
group_alltoggle(struct screen_ctx *sc)
group_toggle(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->group_hideall)
group_show(sc, gc);
else
group_hide(sc, gc);
if (gc->num == idx) {
if (group_holds_only_hidden(gc))
group_show(gc);
else
group_hide(gc);
}
}
sc->group_hideall = !sc->group_hideall;
}
void
group_autogroup(struct client_ctx *cc)
group_toggle_all(struct screen_ctx *sc)
{
struct screen_ctx *sc = cc->sc;
struct autogroupwin *aw;
struct group_ctx *gc;
int no = -1, both_match = 0;
long *grpno;
if (cc->ch.res_class == NULL || cc->ch.res_name == NULL)
return;
if (xu_getprop(cc->win, ewmh[_NET_WM_DESKTOP],
XA_CARDINAL, 1, (unsigned char **)&grpno) > 0) {
if (*grpno == -1)
no = 0;
else if (*grpno > CALMWM_NGROUPS || *grpno < 0)
no = CALMWM_NGROUPS - 1;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->hideall)
group_show(gc);
else
no = *grpno;
XFree(grpno);
} else {
TAILQ_FOREACH(aw, &Conf.autogroupq, entry) {
if (strcmp(aw->class, cc->ch.res_class) == 0) {
if ((aw->name != NULL) &&
(strcmp(aw->name, cc->ch.res_name) == 0)) {
no = aw->num;
both_match = 1;
} else if (aw->name == NULL && !both_match)
no = aw->num;
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);
}
}
}
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->shortcut == no) {
group_assign(gc, cc);
return;
}
}
if (Conf.flags & CONF_STICKY_GROUPS)
group_assign(sc->group_active, cc);
else
group_assign(NULL, cc);
}
void
group_update_names(struct screen_ctx *sc)
group_cycle(struct screen_ctx *sc, int flags)
{
char **strings, *p;
unsigned char *prop_ret;
int i = 0, j = 0, nstrings = 0, n = 0, setnames = 0;
struct group_ctx *newgc, *oldgc, *showgroup = NULL;
if ((j = xu_getprop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
prop_ret[j - 1] = '\0'; /* paranoia */
while (i < j) {
if (prop_ret[i++] == '\0')
nstrings++;
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 = xcalloc((nstrings < CALMWM_NGROUPS ? CALMWM_NGROUPS :
nstrings), sizeof(*strings));
p = (char *)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 = 0;
while (n < CALMWM_NGROUPS)
strings[n++] = xstrdup(shortcut_to_name[i++]);
}
if (prop_ret != NULL)
XFree(prop_ret);
if (sc->group_nonames != 0)
free(sc->group_names);
sc->group_names = strings;
sc->group_nonames = n;
if (setnames)
group_set_names(sc);
}
static void
group_set_names(struct screen_ctx *sc)
{
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;
}
xu_ewmh_net_desktop_names(sc, p, len);
return 0;
}

905
kbfunc.c

File diff suppressed because it is too large Load Diff

338
menu.c
View File

@ -19,12 +19,13 @@
* $OpenBSD$
*/
#include <sys/param.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>
@ -36,6 +37,10 @@
#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,
@ -44,37 +49,34 @@ enum ctltype {
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 height;
int width;
int num;
int flags;
int x;
int y;
void (*match)(struct menu_q *, struct menu_q *, char *);
void (*print)(struct menu *, int);
};
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 menu_q *);
static struct menu *menu_handle_release(XEvent *, struct menu_ctx *,
struct menu_q *);
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_calc_entry(struct menu_ctx *, int, int);
static struct menu *menu_complete_path(struct menu_ctx *);
static struct menu *menu_complete_path(struct menu_ctx *);
static int menu_keycode(XKeyEvent *, enum ctltype *, char *);
struct menu *
@ -88,58 +90,59 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const 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);
xu_ptr_get(sc->rootwin, &xsave, &ysave);
(void)memset(&mc, 0, sizeof(mc));
xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y);
xsave = mc.x;
ysave = mc.y;
mc.sc = sc;
mc.flags = flags;
if (prompt != NULL) {
evmask = MENUMASK | KEYMASK; /* accept keys as well */
(void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr));
mc.hasprompt = 1;
} else {
evmask = MENUMASK;
mc.list = 1;
}
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
mc.geom.x = xsave;
mc.geom.y = ysave;
if (mc.flags & CWM_MENU_LIST)
mc.list = 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);
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,
Conf.cursor[CF_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:
@ -151,11 +154,12 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
menu_draw(&mc, menuq, &resultq);
break;
case MotionNotify:
menu_handle_move(&e, &mc, &resultq);
menu_handle_move(&mc, &resultq,
e.xbutton.x, e.xbutton.y);
break;
case ButtonRelease:
if ((mi = menu_handle_release(&e, &mc, &resultq))
!= NULL)
if ((mi = menu_handle_release(&mc, &resultq,
e.xbutton.x, e.xbutton.y)) != NULL)
goto out;
break;
default:
@ -169,48 +173,47 @@ out:
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);
XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, 1, 1);
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;
char *path = NULL;
int mflags = (CWM_MENU_DUMMY);
path = xcalloc(1, sizeof(mr->text));
mr = xcalloc(1, sizeof(*mr));
TAILQ_INIT(&menuq);
if ((mi = menu_filter(mc->sc, &menuq, mc->searchstr, NULL,
CWM_MENU_DUMMY, search_match_path_any, NULL)) != NULL) {
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;
strlcpy(path, mi->text, sizeof(mi->text));
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);
if (path[0] != '\0')
snprintf(mr->text, sizeof(mr->text), "%s \"%s\"",
mc->searchstr, path);
else if (!mr->abort)
strlcpy(mr->text, mc->searchstr, sizeof(mr->text));
free(path);
return (mr);
return mr;
}
static struct menu *
@ -225,7 +228,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
wchar_t wc;
if (menu_keycode(&e->xkey, &ctl, chr) < 0)
return (NULL);
return NULL;
switch (ctl) {
case CTL_ERASEONE:
@ -260,13 +263,13 @@ 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;
@ -281,7 +284,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
if ((mc->flags & CWM_MENU_FILE) &&
(strncmp(mc->searchstr, mi->text,
strlen(mi->text))) == 0)
return (menu_complete_path(mc));
return menu_complete_path(mc);
/*
* Put common prefix of the results into searchstr
@ -302,11 +305,11 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
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;
}
@ -316,20 +319,15 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
(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
@ -337,98 +335,87 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
{
struct screen_ctx *sc = mc->sc;
struct menu *mi;
struct geom xine;
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;
mc->height = 0;
if (mc->hasprompt) {
(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s",
mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR);
mc->width = xu_xft_width(sc->xftfont, mc->dispstr,
strlen(mc->dispstr));
mc->height = sc->xftfont->height + 1;
mc->num = 1;
}
(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->ascent + sc->xftfont->descent;
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, xu_xft_width(sc->xftfont, text,
MIN(strlen(text), MENU_MAXENTRY)));
mc->height += sc->xftfont->height + 1;
(*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->ascent + sc->xftfont->descent;
mc->num++;
}
xine = screen_find_xinerama(sc, mc->x, mc->y, CWM_GAP);
xine.w += xine.x - Conf.bwidth * 2;
xine.h += xine.y - Conf.bwidth * 2;
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->x;
ysave = mc->y;
xsave = mc->geom.x;
ysave = mc->geom.y;
/* Never hide the top, or left side, of the menu. */
if (mc->x + mc->width >= xine.w)
mc->x = xine.w - mc->width;
if (mc->x < xine.x) {
mc->x = xine.x;
mc->width = MIN(mc->width, (xine.w - xine.x));
if (mc->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->y + mc->height >= xine.h)
mc->y = xine.h - mc->height;
if (mc->y < xine.y) {
mc->y = xine.y;
mc->height = MIN(mc->height, (xine.h - xine.y));
if (mc->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));
}
if (mc->x != xsave || mc->y != ysave)
xu_ptr_setpos(sc->rootwin, mc->x, mc->y);
if (mc->geom.x != xsave || mc->geom.y != ysave)
xu_ptr_set(sc->rootwin, mc->geom.x, mc->geom.y);
XClearWindow(X_Dpy, sc->menuwin);
XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y,
mc->width, mc->height);
XClearWindow(X_Dpy, mc->win);
XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y,
mc->geom.w, mc->geom.h);
if (mc->hasprompt) {
xu_xft_draw(sc, mc->dispstr, CWM_COLOR_MENU_FONT,
0, sc->xftfont->ascent);
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;
int y = n * (sc->xftfont->ascent + sc->xftfont->descent);
/* Stop drawing when menu doesn't fit inside the screen. */
if (mc->y + y > xine.h)
if (mc->geom.y + y >= area.h)
break;
xu_xft_draw(sc, text, CWM_COLOR_MENU_FONT, 0, y);
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, y + sc->xftfont->ascent,
(const FcChar8*)mi->print, strlen(mi->print));
n++;
}
if (mc->hasprompt && n > 1)
if (n > 1)
menu_draw_entry(mc, resultq, 1, 1);
}
@ -438,11 +425,7 @@ menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
{
struct screen_ctx *sc = mc->sc;
struct menu *mi;
char *text;
int color, i = 0;
if (mc->hasprompt)
i = 1;
int color, i = 1, y;
TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++)
@ -450,21 +433,21 @@ menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
if (mi == NULL)
return;
color = active ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
text = mi->print[0] != '\0' ? mi->print : mi->text;
XftDrawRect(sc->xftdraw, &sc->xftcolor[color], 0,
(sc->xftfont->height + 1) * entry, mc->width,
(sc->xftfont->height + 1) + sc->xftfont->descent);
color = active ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
xu_xft_draw(sc, text, color,
0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1);
y = entry * (sc->xftfont->ascent + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
XftDrawRect(mc->xftdraw, &sc->xftcolor[color], 0, y,
mc->geom.w, sc->xftfont->ascent + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[color], sc->xftfont, 0, y + sc->xftfont->ascent,
(const FcChar8*)mi->print, strlen(mi->print));
}
static void
menu_handle_move(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq)
menu_handle_move(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
{
mc->prev = mc->entry;
mc->entry = menu_calc_entry(mc, e->xbutton.x, e->xbutton.y);
mc->entry = menu_calc_entry(mc, x, y);
if (mc->prev == mc->entry)
return;
@ -472,22 +455,19 @@ menu_handle_move(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq)
if (mc->prev != -1)
menu_draw_entry(mc, resultq, mc->prev, 0);
if (mc->entry != -1) {
(void)xu_ptr_regrab(MENUGRABMASK, Conf.cursor[CF_NORMAL]);
XChangeActivePointerGrab(X_Dpy, MENUGRABMASK,
Conf.cursor[CF_NORMAL], CurrentTime);
menu_draw_entry(mc, resultq, mc->entry, 1);
} else
(void)xu_ptr_regrab(MENUGRABMASK, Conf.cursor[CF_DEFAULT]);
}
}
static struct menu *
menu_handle_release(XEvent *e, struct menu_ctx *mc, 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;
int entry, i = 1;
entry = menu_calc_entry(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++)
@ -497,7 +477,7 @@ menu_handle_release(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq)
mi->text[0] = '\0';
mi->dummy = 1;
}
return (mi);
return mi;
}
static int
@ -506,37 +486,37 @@ menu_calc_entry(struct menu_ctx *mc, int x, int y)
struct screen_ctx *sc = mc->sc;
int entry;
entry = y / (sc->xftfont->height + 1);
entry = y / (sc->xftfont->ascent + sc->xftfont->descent);
/* in bounds? */
if (x < 0 || x > mc->width || y < 0 ||
y > (sc->xftfont->height + 1) * mc->num ||
if (x < 0 || x > mc->geom.w || y < 0 ||
y > (sc->xftfont->ascent + sc->xftfont->descent) * 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(XKeyEvent *ev, enum ctltype *ctl, char *chr)
{
KeySym ks;
unsigned int state = ev->state;
*ctl = CTL_NONE;
chr[0] = '\0';
ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0,
(state & ShiftMask) ? 1 : 0);
(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;
@ -554,7 +534,7 @@ menu_keycode(XKeyEvent *ev, 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:
@ -578,10 +558,13 @@ menu_keycode(XKeyEvent *ev, 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:
@ -597,12 +580,12 @@ menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
}
if (*ctl != CTL_NONE)
return (0);
return 0;
if (XLookupString(ev, chr, 32, &ks, NULL) < 0)
return (-1);
return -1;
return (0);
return 0;
}
void
@ -615,7 +598,10 @@ menuq_add(struct menu_q *mq, void *ctx, const char *fmt, ...)
mi->ctx = ctx;
va_start(ap, fmt);
(void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap);
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);

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,245 +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 "queue.h"
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "calmwm.h"
static void mousefunc_sweep_calc(struct client_ctx *, int, int, int, int);
static void mousefunc_sweep_draw(struct client_ctx *);
static void
mousefunc_sweep_calc(struct client_ctx *cc, int x, int y, int mx, int my)
{
cc->geom.w = abs(x - mx) - cc->bwidth;
cc->geom.h = abs(y - my) - cc->bwidth;
client_applysizehints(cc);
cc->geom.x = x <= mx ? x : x - cc->geom.w;
cc->geom.y = y <= my ? y : y - cc->geom.h;
}
static void
mousefunc_sweep_draw(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
char s[14]; /* fits " nnnn x nnnn \0" */
(void)snprintf(s, sizeof(s), " %4d x %-4d ",
(cc->geom.w - cc->hint.basew) / cc->hint.incw,
(cc->geom.h - cc->hint.baseh) / cc->hint.inch);
XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0);
XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0,
xu_xft_width(sc->xftfont, s, strlen(s)), sc->xftfont->height);
XMapWindow(X_Dpy, sc->menuwin);
XClearWindow(X_Dpy, sc->menuwin);
xu_xft_draw(sc, s, CWM_COLOR_MENU_FONT, 0, sc->xftfont->ascent + 1);
}
void
mousefunc_client_resize(struct client_ctx *cc, union arg *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, Conf.cursor[CF_RESIZE]) < 0)
return;
xu_ptr_setpos(cc->win, cc->geom.w, cc->geom.h);
mousefunc_sweep_draw(cc);
for (;;) {
XMaskEvent(X_Dpy, MOUSEMASK, &ev);
switch (ev.type) {
case MotionNotify:
mousefunc_sweep_calc(cc, x, y,
ev.xmotion.x_root, ev.xmotion.y_root);
/* don't resize more than 60 times / second */
if ((ev.xmotion.time - ltime) > (1000 / 60)) {
ltime = ev.xmotion.time;
client_resize(cc, 1);
mousefunc_sweep_draw(cc);
}
break;
case ButtonRelease:
if (ltime)
client_resize(cc, 1);
XUnmapWindow(X_Dpy, sc->menuwin);
XReparentWindow(X_Dpy, sc->menuwin, sc->rootwin, 0, 0);
xu_ptr_ungrab();
/* Make sure the pointer stays within the window. */
if (cc->ptr.x > cc->geom.w)
cc->ptr.x = cc->geom.w - cc->bwidth;
if (cc->ptr.y > cc->geom.h)
cc->ptr.y = cc->geom.h - cc->bwidth;
client_ptrwarp(cc);
return;
}
}
/* NOTREACHED */
}
void
mousefunc_client_move(struct client_ctx *cc, union arg *arg)
{
XEvent ev;
Time ltime = 0;
struct screen_ctx *sc = cc->sc;
struct geom xine;
int px, py;
client_raise(cc);
if (cc->flags & CLIENT_FREEZE)
return;
if (xu_ptr_grab(cc->win, MOUSEMASK, Conf.cursor[CF_MOVE]) < 0)
return;
xu_ptr_getpos(cc->win, &px, &py);
for (;;) {
XMaskEvent(X_Dpy, MOUSEMASK, &ev);
switch (ev.type) {
case MotionNotify:
cc->geom.x = ev.xmotion.x_root - px - cc->bwidth;
cc->geom.y = ev.xmotion.y_root - py - cc->bwidth;
xine = screen_find_xinerama(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP);
cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.x + cc->geom.w + (cc->bwidth * 2),
xine.x, xine.x + xine.w, sc->snapdist);
cc->geom.y += client_snapcalc(cc->geom.y,
cc->geom.y + cc->geom.h + (cc->bwidth * 2),
xine.y, xine.y + xine.h, sc->snapdist);
/* 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_client_grouptoggle(struct client_ctx *cc, union arg *arg)
{
group_sticky_toggle_enter(cc);
}
void
mousefunc_menu_group(struct client_ctx *cc, union arg *arg)
{
group_menu(cc->sc);
}
void
mousefunc_menu_unhide(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc = cc->sc;
struct client_ctx *old_cc;
struct menu *mi;
struct menu_q menuq;
char *wname;
old_cc = client_current();
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &Clientq, entry)
if (cc->flags & CLIENT_HIDDEN) {
wname = (cc->label) ? cc->label : cc->name;
if (wname == NULL)
continue;
menuq_add(&menuq, cc, "(%d) %s",
cc->group->shortcut, wname);
}
if (TAILQ_EMPTY(&menuq))
return;
if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0,
NULL, NULL)) != NULL) {
cc = (struct client_ctx *)mi->ctx;
client_unhide(cc);
if (old_cc != NULL)
client_ptrsave(old_cc);
client_ptrwarp(cc);
}
menuq_clear(&menuq);
}
void
mousefunc_menu_cmd(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc = cc->sc;
struct cmd *cmd;
struct menu *mi;
struct menu_q menuq;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry)
menuq_add(&menuq, cmd, "%s", cmd->name);
if (TAILQ_EMPTY(&menuq))
return;
if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0,
NULL, NULL)) != NULL)
u_spawn(((struct cmd *)mi->ctx)->path);
menuq_clear(&menuq);
}

186
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>
@ -45,11 +45,13 @@ static struct file {
int lineno;
int errors;
} *file, *topfile;
struct file *pushfile(const char *);
struct file *pushfile(const char *, FILE *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...);
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);
@ -68,9 +70,10 @@ 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 URGENCYBORDER
%token GROUPBORDER UNGROUPBORDER
@ -80,7 +83,7 @@ typedef struct {
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> yesno
%type <v.string> string
%type <v.string> string numberstring
%%
grammar : /* empty */
@ -103,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; }
;
@ -112,72 +126,95 @@ 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 > UINT_MAX) {
yyerror("invalid borderwidth: %d", $2);
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: %d", $2);
yyerror("invalid movemount");
YYERROR;
}
conf->mamount = $2;
}
| SNAPDIST NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid snapdist: %d", $2);
yyerror("invalid snapdist");
YYERROR;
}
conf->snapdist = $2;
}
| COMMAND STRING string {
if (!conf_cmd_add(conf, $2, $3)) {
yyerror("command name/path too long");
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("invalid autogroup: %d", $2);
YYERROR;
}
conf_autogroup(conf, $2, $3);
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 {
conf_ignore(conf, $2);
free($2);
}
| BIND STRING string {
if (!conf_bind_kbd(conf, $2, $3)) {
yyerror("invalid bind: %s %s", $2, $3);
free($2);
free($3);
YYERROR;
}
free($2);
free($3);
}
| GAP NUMBER NUMBER NUMBER NUMBER {
if ($2 < 0 || $2 > INT_MAX ||
$3 < 0 || $3 > INT_MAX ||
$4 < 0 || $4 > INT_MAX ||
$5 < 0 || $5 > INT_MAX) {
yyerror("invalid gap: %d %d %d %d",
$2, $3, $4, $5);
yyerror("invalid gap");
YYERROR;
}
conf->gap.top = $2;
@ -185,9 +222,9 @@ main : FONTNAME STRING {
conf->gap.left = $4;
conf->gap.right = $5;
}
| MOUSEBIND STRING string {
if (!conf_bind_mouse(conf, $2, $3)) {
yyerror("invalid mousebind: %s %s", $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;
@ -195,6 +232,32 @@ main : FONTNAME STRING {
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
@ -271,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},
@ -279,18 +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;
@ -319,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;
@ -328,7 +396,7 @@ 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) {
@ -369,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
@ -385,7 +453,7 @@ findeol(void)
/* skip to either EOF or the first real EOL */
while (1) {
if (pushback_index)
c = pushback_buffer[--pushback_index];
c = (unsigned char)pushback_buffer[--pushback_index];
else
c = lgetc(0);
if (c == '\n') {
@ -428,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++;
@ -438,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");
@ -455,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());
}
@ -478,8 +550,8 @@ yylex(void)
} else {
nodigits:
while (p > buf + 1)
lungetc(*--p);
c = *--p;
lungetc((unsigned char)*--p);
c = (unsigned char)*--p;
if (c == '-')
return (c);
}
@ -494,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());
}
@ -515,19 +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) {
warn("%s", nfile->name);
free(nfile->name);
free(nfile);
return (NULL);
}
nfile->stream = stream;
nfile->lineno = 1;
TAILQ_INSERT_TAIL(&files, nfile, entry);
return (nfile);
@ -552,13 +618,19 @@ popfile(void)
int
parse_config(const char *filename, struct conf *xconf)
{
FILE *stream;
int errors = 0;
conf = xconf;
if ((file = pushfile(filename)) == NULL) {
stream = fopen(filename, "r");
if (stream == NULL) {
if (errno == ENOENT)
return (0);
warn("%s", filename);
return (-1);
}
file = pushfile(filename, stream);
topfile = file;
yyparse();

90
queue.h
View File

@ -1,5 +1,5 @@
/* $OpenBSD: "queue.h",v 1.36 2012/04/11 13:29:14 naddy Exp $ */
/* $NetBSD: "queue.h",v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/* $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
@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)"queue.h" 8.5 (Berkeley) 8/20/94
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
@ -316,6 +316,86 @@ struct { \
(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.
*/
@ -553,12 +633,12 @@ struct { \
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head).cqh_last = (elm2); \
(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); \
(head)->cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \

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);
}

276
screen.c
View File

@ -18,11 +18,13 @@
* $OpenBSD$
*/
#include <sys/param.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>
@ -30,66 +32,92 @@
#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;
Window *wins, w0, w1;
XSetWindowAttributes rootattr;
unsigned int nwins, i;
XSetWindowAttributes attr;
sc = xcalloc(1, sizeof(*sc));
sc = xmalloc(sizeof(*sc));
TAILQ_INIT(&sc->mruq);
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);
group_init(sc);
rootattr.cursor = Conf.cursor[CF_NORMAL];
rootattr.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|
PropertyChangeMask|EnterWindowMask|LeaveWindowMask|
ColormapChangeMask|BUTTONMASK;
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);
XChangeWindowAttributes(X_Dpy, sc->rootwin,
CWEventMask|CWCursor, &rootattr);
attr.cursor = Conf.cursor[CF_NORMAL];
attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
EnterWindowMask | PropertyChangeMask | ButtonPressMask;
XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr);
/* Deal with existing clients. */
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (i = 0; i < nwins; i++)
(void)client_init(wins[i], sc);
XFree(wins);
}
screen_updatestackingorder(sc);
group_set_state(sc);
if (HasRandr)
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
@ -105,75 +133,175 @@ screen_updatestackingorder(struct screen_ctx *sc)
if ((cc = client_find(wins[i])) == NULL ||
cc->flags & CLIENT_HIDDEN)
continue;
cc->stackingorder = s++;
}
XFree(wins);
}
}
/*
* Find which xinerama screen the coordinates (x,y) is on.
*/
struct geom
screen_find_xinerama(struct screen_ctx *sc, int x, int y, int flags)
struct region_ctx *
region_find(struct screen_ctx *sc, int x, int y)
{
struct region_ctx *region;
struct geom geom = sc->work;
struct region_ctx *rc;
TAILQ_FOREACH(region, &sc->regionq, entry) {
if (x >= region->area.x && x < region->area.x+region->area.w &&
y >= region->area.y && y < region->area.y+region->area.h) {
geom = region->area;
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;
}
}
if (flags & CWM_GAP) {
geom.x += sc->gap.left;
geom.y += sc->gap.top;
geom.w -= (sc->gap.left + sc->gap.right);
geom.h -= (sc->gap.top + sc->gap.bottom);
return 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;
}
}
return (geom);
if (apply_gap)
area = screen_apply_gap(sc, area);
return area;
}
void
screen_update_geometry(struct screen_ctx *sc)
{
XineramaScreenInfo *info = NULL;
struct region_ctx *region;
int info_no = 0, i;
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);
sc->work.x = sc->view.x + sc->gap.left;
sc->work.y = sc->view.y + sc->gap.top;
sc->work.w = sc->view.w - (sc->gap.left + sc->gap.right);
sc->work.h = sc->view.h - (sc->gap.top + sc->gap.bottom);
/* RandR event may have a CTRC added or removed. */
if (XineramaIsActive(X_Dpy))
info = XineramaQueryScreens(X_Dpy, &info_no);
while ((region = TAILQ_FIRST(&sc->regionq)) != NULL) {
TAILQ_REMOVE(&sc->regionq, region, entry);
free(region);
while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) {
TAILQ_REMOVE(&sc->regionq, rc, entry);
free(rc);
}
for (i = 0; i < info_no; i++) {
region = xmalloc(sizeof(*region));
region->num = i;
region->area.x = info[i].x_org;
region->area.y = info[i].y_org;
region->area.w = info[i].width;
region->area.h = info[i].height;
TAILQ_INSERT_TAIL(&sc->regionq, region, entry);
if (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);
}
if (info)
XFree(info);
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;
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);
}
}
}
void
screen_prop_win_create(struct screen_ctx *sc, Window win)
{
sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win,
sc->visual, sc->colormap);
XMapWindow(X_Dpy, sc->prop.win);
}
void
screen_prop_win_destroy(struct screen_ctx *sc)
{
XftDrawDestroy(sc->prop.xftdraw);
XDestroyWindow(X_Dpy, sc->prop.win);
}
void
screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...)
{
va_list ap;
char *text;
XGlyphInfo extents;
va_start(ap, fmt);
xvasprintf(&text, fmt, ap);
va_end(ap);
XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
strlen(text), &extents);
XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height);
XClearWindow(X_Dpy, sc->prop.win);
XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
sc->xftfont, 0, sc->xftfont->ascent + 1,
(const FcChar8*)text, strlen(text));
free(text);
}

306
search.c
View File

@ -18,14 +18,14 @@
* $OpenBSD$
*/
#include <sys/param.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>
@ -36,77 +36,78 @@
#define PATH_ANY 0x0001
#define PATH_EXEC 0x0002
static void search_match_path(struct menu_q *, struct menu_q *,
char *, int);
static void search_match_path_exec(struct menu_q *, struct menu_q *,
char *);
static int strsubmatch(char *, char *, int);
static void match_path_type(struct menu_q *, char *, int);
static int match_substr(char *, char *, int);
/*
* Match: label, title, class.
*/
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->ch.res_class, 0)) {
cc->matchname = cc->ch.res_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
@ -127,92 +128,56 @@ 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;
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);
}
}
if (cc == client_current())
flag = '!';
else if (cc->flags & CLIENT_HIDDEN)
flag = '&';
void
search_match_group(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
struct group_ctx *gc;
char *s;
if (list)
cc->matchname = cc->name;
(void)snprintf(mi->print, sizeof(mi->print), "(%d) %c%s",
cc->group->shortcut, 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) {
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
search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search, int flag)
match_path_type(struct menu_q *resultq, char *search, int flag)
{
char pattern[MAXPATHLEN];
glob_t g;
int i;
TAILQ_INIT(resultq);
(void)strlcpy(pattern, search, sizeof(pattern));
(void)strlcat(pattern, "*", sizeof(pattern));
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;
menuq_add(resultq, NULL, "%s", g.gl_pathv[i]);
mi = xcalloc(1, sizeof(*mi));
(void)strlcpy(mi->text, g.gl_pathv[i], sizeof(mi->text));
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
globfree(&g);
}
static void
search_match_path_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
return (search_match_path(menuq, resultq, search, PATH_EXEC));
}
void
search_match_path_any(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
return (search_match_path(menuq, resultq, search, PATH_ANY));
}
void
search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry)
if (strsubmatch(search, mi->text, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
free(pattern);
}
void
@ -222,13 +187,12 @@ search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
int r;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
if (strsubmatch(search, mi->text, 1) == 0 &&
if (match_substr(search, mi->text, 1) == 0 &&
fnmatch(search, mi->text, 0) == FNM_NOMATCH)
continue;
TAILQ_FOREACH(mj, resultq, resultentry) {
r = strcasecmp(mi->text, mj->text);
r = strcmp(mi->text, mj->text);
if (r < 0)
TAILQ_INSERT_BEFORE(mj, mi, resultentry);
if (r <= 0)
@ -237,39 +201,89 @@ search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
if (mj == NULL)
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
if (TAILQ_EMPTY(resultq))
match_path_type(resultq, search, PATH_EXEC);
}
void
search_match_exec_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
search_match_exec(menuq, resultq, search);
if (TAILQ_EMPTY(resultq))
search_match_path_exec(menuq, resultq, search);
TAILQ_INIT(resultq);
match_path_type(resultq, search, PATH_ANY);
}
static int
strsubmatch(char *sub, char *str, int zeroidx)
void
search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
size_t len, sublen;
unsigned int n, flen;
struct menu *mi;
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) {
if (match_substr(search, mi->text, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
void
search_match_wm(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
struct cmd_ctx *wm;
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);
}

View File

@ -1,7 +1,7 @@
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
/* $OpenBSD: strlcat.c,v 1.14 2015/01/15 03:54:12 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* 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
@ -18,44 +18,45 @@
/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
#ifndef HAVE_STRLCAT
#include <sys/types.h>
#include <string.h>
#include "calmwm.h"
#ifndef HAVE_STRLCAT
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* 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 siz)
strlcat(char *dst, const char *src, size_t dsize)
{
char *d = dst;
const char *s = src;
size_t n = siz;
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 && *d != '\0')
d++;
dlen = d - dst;
n = siz - 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(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
if (n-- == 0)
return(dlen + strlen(src));
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
n--;
}
s++;
src++;
}
*d = '\0';
*dst = '\0';
return(dlen + (s - src)); /* count does not include NUL */
return(dlen + (src - osrc)); /* count does not include NUL */
}
#endif /* !HAVE_STRLCAT */

View File

@ -1,7 +1,7 @@
/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */
/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* 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
@ -18,40 +18,40 @@
/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
#ifndef HAVE_STRLCPY
#include <sys/types.h>
#include <string.h>
#include "calmwm.h"
#ifndef HAVE_STRLCPY
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
* 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 siz)
strlcpy(char *dst, const char *src, size_t dsize)
{
char *d = dst;
const char *s = src;
size_t n = siz;
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
} while (--n != 0);
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
/* 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(s - src - 1); /* count does not include NUL */
return(src - osrc - 1); /* count does not include NUL */
}
#endif /* !HAVE_STRLCPY */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */
/*
* Copyright (c) 2004 Ted Unangst and Todd Miller
@ -20,21 +20,21 @@
/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */
#ifndef HAVE_STRTONUM
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
char *ep;
int error = 0;
char *ep;
struct errval {
const char *errstr;
int err;
@ -47,9 +47,9 @@ strtonum(const char *numstr, long long minval, long long maxval,
ev[0].err = errno;
errno = 0;
if (minval > maxval)
if (minval > maxval) {
error = INVALID;
else {
} else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;

View File

@ -1 +1,2 @@
GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d :pserver:anoncvs:anoncvs@mirror.planetunix.net:/cvs xenocara/app/cwm
#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

71
util.c
View File

@ -18,11 +18,13 @@
* $OpenBSD$
*/
#include <sys/param.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>
@ -30,7 +32,7 @@
#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);
}

273
xevents.c
View File

@ -24,11 +24,12 @@
* management of the xevent's.
*/
#include <sys/param.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>
@ -68,22 +69,28 @@ void (*xev_handlers[LASTEvent])(XEvent *) = {
};
static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R,
XK_Control_L, XK_Control_R };
XK_Control_L, XK_Control_R, XK_ISO_Level3_Shift };
static void
xev_handle_maprequest(XEvent *ee)
{
XMapRequestEvent *e = &ee->xmaprequest;
struct client_ctx *cc = NULL, *old_cc;
struct screen_ctx *sc;
struct client_ctx *cc, *old_cc;
if ((old_cc = client_current()))
client_ptrsave(old_cc);
LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window);
if ((sc = screen_find(e->parent)) == NULL)
return;
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) == 0))
client_ptrwarp(cc);
if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
client_ptr_warp(cc);
}
static void
@ -92,12 +99,14 @@ xev_handle_unmapnotify(XEvent *ee)
XUnmapEvent *e = &ee->xunmap;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL) {
if (e->send_event) {
client_set_wm_state(cc, WithdrawnState);
xu_set_wm_state(cc->win, WithdrawnState);
} else {
if (!(cc->flags & CLIENT_HIDDEN))
client_delete(cc);
client_remove(cc);
}
}
}
@ -108,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
@ -120,6 +131,8 @@ 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;
@ -173,13 +186,15 @@ 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);
@ -187,17 +202,19 @@ xev_handle_propertynotify(XEvent *ee)
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) {
if (e->atom == ewmh[_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);
}
}
}
@ -208,67 +225,92 @@ 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);
client_set_active(cc);
}
/* 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 binding *mb;
struct client_ctx *cc;
struct screen_ctx *sc;
struct bind_ctx *mb;
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((sc = screen_find(e->root)) == NULL)
return;
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
TAILQ_FOREACH(mb, &Conf.mousebindq, entry) {
if (e->button == mb->press.button && e->state == mb->modmask)
break;
}
if (mb == NULL)
return;
if (mb->flags & CWM_WIN) {
mb->cargs->xev = CWM_XEV_BTN;
switch (mb->context) {
case CWM_CONTEXT_CC:
if (((cc = client_find(e->window)) == NULL) &&
(cc = client_current()) == NULL)
((cc = client_current(sc)) == NULL))
return;
} else {
if (e->window != e->root)
return;
cc = &fakecc;
cc->sc = screen_fromroot(e->window);
(*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;
}
(*mb->callback)(cc, &mb->argument);
}
static void
xev_handle_buttonrelease(XEvent *ee)
{
struct client_ctx *cc;
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc;
if ((cc = client_current()))
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 binding *kb;
struct client_ctx *cc;
struct screen_ctx *sc;
struct bind_ctx *kb;
KeySym keysym, skeysym;
unsigned int modshift;
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((sc = screen_find(e->root)) == NULL)
return;
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
if (keysym != kb->press.keysym && skeysym == kb->press.keysym)
modshift = ShiftMask;
else
@ -277,22 +319,26 @@ xev_handle_keypress(XEvent *ee)
if ((kb->modmask | modshift) != e->state)
continue;
if (kb->press.keysym == (modshift == 0 ? keysym : skeysym))
if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym))
break;
}
if (kb == NULL)
return;
if (kb->flags & CWM_WIN) {
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);
}
/*
@ -303,15 +349,30 @@ xev_handle_keyrelease(XEvent *ee)
{
XKeyEvent *e = &ee->xkey;
struct screen_ctx *sc;
struct client_ctx *cc;
KeySym keysym;
unsigned int i;
sc = screen_fromroot(e->root);
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((sc = screen_find(e->root)) == NULL)
return;
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
for (i = 0; i < nitems(modkeys); i++) {
if (keysym == modkeys[i]) {
client_cycle_leave(sc);
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;
}
}
@ -324,58 +385,66 @@ xev_handle_clientmessage(XEvent *ee)
struct client_ctx *cc, *old_cc;
struct screen_ctx *sc;
sc = screen_fromroot(e->window);
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) == NULL && e->window != sc->rootwin)
return;
if (e->message_type == cwmh[WM_CHANGE_STATE] && e->format == 32 &&
e->data.l[0] == IconicState)
client_hide(cc);
if (e->message_type == ewmh[_NET_CLOSE_WINDOW])
client_send_delete(cc);
if (e->message_type == ewmh[_NET_ACTIVE_WINDOW] && e->format == 32) {
if ((old_cc = client_current()))
client_ptrsave(old_cc);
client_ptrwarp(cc);
if (e->message_type == 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]);
}
}
if (e->message_type == ewmh[_NET_WM_DESKTOP] && e->format == 32) {
/*
* The EWMH spec states that if the cardinal returned is
* 0xFFFFFFFF (-1) then the window should appear on all
* desktops, which in our case is assigned to group 0.
*/
if (e->data.l[0] == (unsigned long)-1)
group_movetogroup(cc, 0);
else
group_movetogroup(cc, e->data.l[0]);
}
if (e->message_type == ewmh[_NET_WM_STATE] && e->format == 32)
xu_ewmh_handle_net_wm_state_msg(cc,
e->data.l[0], e->data.l[1], e->data.l[2]);
if (e->message_type == ewmh[_NET_CURRENT_DESKTOP] && e->format == 32)
group_only(sc, e->data.l[0]);
}
static void
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 == i) {
XRRUpdateConfiguration(ee);
screen_update_geometry(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);
}
/*
@ -388,6 +457,8 @@ xev_handle_mappingnotify(XEvent *ee)
XMappingEvent *e = &ee->xmapping;
struct screen_ctx *sc;
LOG_DEBUG3("window: 0x%lx", e->window);
XRefreshKeyboardMapping(e);
if (e->request == MappingKeyboard) {
TAILQ_FOREACH(sc, &Screenq, entry)
@ -401,6 +472,8 @@ 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);
}
@ -410,9 +483,11 @@ xev_process(void)
{
XEvent e;
XNextEvent(X_Dpy, &e);
if (e.type - Randr_ev == RRScreenChangeNotify)
xev_handle_randr(&e);
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
(*xev_handlers[e.type])(&e);
while (XPending(X_Dpy)) {
XNextEvent(X_Dpy, &e);
if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify)
xev_handle_randr(&e);
else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL))
(*xev_handlers[e.type])(&e);
}
}

View File

@ -18,11 +18,12 @@
* $OpenBSD$
*/
#include <sys/param.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>
@ -42,7 +43,7 @@ xmalloc(size_t siz)
if ((p = malloc(siz)) == NULL)
err(1, "malloc");
return (p);
return p;
}
void *
@ -57,7 +58,19 @@ xcalloc(size_t no, size_t siz)
if ((p = calloc(no, siz)) == NULL)
err(1, "calloc");
return (p);
return p;
}
void *
xreallocarray(void *ptr, size_t nmemb, size_t size)
{
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 *
@ -68,7 +81,7 @@ xstrdup(const char *str)
if ((p = strdup(str)) == NULL)
err(1, "strdup");
return (p);
return p;
}
int
@ -78,11 +91,20 @@ xasprintf(char **ret, const char *fmt, ...)
int i;
va_start(ap, fmt);
i = vasprintf(ret, fmt, ap);
i = xvasprintf(ret, fmt, ap);
va_end(ap);
if (i < 0 || *ret == NULL)
err(1, "asprintf");
return (i);
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;
}

443
xutil.c
View File

@ -18,11 +18,12 @@
* $OpenBSD$
*/
#include <sys/param.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>
@ -30,70 +31,8 @@
#include "calmwm.h"
static unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask };
void
xu_btn_grab(Window win, int mask, unsigned int btn)
{
unsigned int i;
for (i = 0; i < nitems(ign_mods); i++)
XGrabButton(X_Dpy, btn, (mask | ign_mods[i]), win,
False, BUTTONMASK, GrabModeAsync,
GrabModeSync, None, None);
}
void
xu_btn_ungrab(Window win)
{
XUngrabButton(X_Dpy, AnyButton, AnyModifier, win);
}
void
xu_key_grab(Window win, unsigned int mask, KeySym keysym)
{
KeyCode code;
unsigned int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XkbKeycodeToKeysym(X_Dpy, code, 0, 0) != keysym) &&
(XkbKeycodeToKeysym(X_Dpy, code, 0, 1) == keysym))
mask |= ShiftMask;
for (i = 0; i < nitems(ign_mods); i++)
XGrabKey(X_Dpy, code, (mask | ign_mods[i]), win,
True, GrabModeAsync, GrabModeAsync);
}
void
xu_key_ungrab(Window win)
{
XUngrabKey(X_Dpy, AnyKey, AnyModifier, win);
}
int
xu_ptr_grab(Window win, unsigned int mask, Cursor curs)
{
return (XGrabPointer(X_Dpy, win, False, mask,
GrabModeAsync, GrabModeAsync,
None, curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
int
xu_ptr_regrab(unsigned int mask, Cursor curs)
{
return (XChangeActivePointerGrab(X_Dpy, mask,
curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
void
xu_ptr_ungrab(void)
{
XUngrabPointer(X_Dpy, CurrentTime);
}
void
xu_ptr_getpos(Window win, int *x, int *y)
xu_ptr_get(Window win, int *x, int *y)
{
Window w0, w1;
int tmp0, tmp1;
@ -103,13 +42,13 @@ xu_ptr_getpos(Window win, int *x, int *y)
}
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);
}
int
xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p)
{
Atom realtype;
unsigned long n, extra;
@ -117,16 +56,16 @@ xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type,
&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;
@ -134,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) {
@ -151,10 +92,101 @@ xu_getstrprop(Window win, Atom atm, char **text) {
}
XFreeStringList(list);
}
XFree(prop.value);
return (nitems);
return nitems;
}
void
xu_send_clientmsg(Window win, Atom proto, Time ts)
{
XClientMessageEvent cm;
(void)memset(&cm, 0, sizeof(cm));
cm.type = ClientMessage;
cm.window = win;
cm.message_type = cwmh[WM_PROTOCOLS];
cm.format = 32;
cm.data.l[0] = proto;
cm.data.l[1] = ts;
XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm);
}
void
xu_get_wm_state(Window win, long *state)
{
long *p;
*state = -1;
if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
(unsigned char **)&p) > 0) {
*state = *p;
XFree(p);
}
}
void
xu_set_wm_state(Window win, long state)
{
long data[] = { state, None };
XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
PropModeReplace, (unsigned char *)data, 2);
}
void
xu_xorcolor(XftColor a, XftColor b, XftColor *r)
{
r->pixel = a.pixel ^ b.pixel;
r->color.red = a.color.red ^ b.color.red;
r->color.green = a.color.green ^ b.color.green;
r->color.blue = a.color.blue ^ b.color.blue;
r->color.alpha = 0xffff;
}
void
xu_atom_init(void)
{
char *cwmhints[] = {
"WM_STATE",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
"WM_PROTOCOLS",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"WM_CHANGE_STATE",
};
char *ewmhints[] = {
"_NET_SUPPORTED",
"_NET_SUPPORTING_WM_CHECK",
"_NET_ACTIVE_WINDOW",
"_NET_CLIENT_LIST",
"_NET_CLIENT_LIST_STACKING",
"_NET_NUMBER_OF_DESKTOPS",
"_NET_CURRENT_DESKTOP",
"_NET_DESKTOP_VIEWPORT",
"_NET_DESKTOP_GEOMETRY",
"_NET_VIRTUAL_ROOTS",
"_NET_SHOWING_DESKTOP",
"_NET_DESKTOP_NAMES",
"_NET_WORKAREA",
"_NET_WM_NAME",
"_NET_WM_DESKTOP",
"_NET_CLOSE_WINDOW",
"_NET_WM_STATE",
"_NET_WM_STATE_STICKY",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_HIDDEN",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_DEMANDS_ATTENTION",
"_NET_WM_STATE_SKIP_PAGER",
"_NET_WM_STATE_SKIP_TASKBAR",
"_CWM_WM_STATE_FREEZE",
};
XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
}
/* Root Window Properties */
@ -176,8 +208,8 @@ xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME],
cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)WMNAME,
strlen(WMNAME));
cwmh[UTF8_STRING], 8, PropModeReplace,
(unsigned char *)Conf.wmname, strlen(Conf.wmname));
}
void
@ -189,22 +221,33 @@ xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
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)
{
long workareas[CALMWM_NGROUPS][4];
int i;
unsigned long *workarea;
int i, ngroups = Conf.ngroups;
for (i = 0; i < CALMWM_NGROUPS; i++) {
workareas[i][0] = sc->work.x;
workareas[i][1] = sc->work.y;
workareas[i][2] = sc->work.w;
workareas[i][3] = sc->work.h;
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 *)workareas,
CALMWM_NGROUPS * 4);
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea,
ngroups * 4);
free(workarea);
}
void
@ -214,19 +257,40 @@ xu_ewmh_net_client_list(struct screen_ctx *sc)
Window *winlist;
int i = 0, j = 0;
TAILQ_FOREACH(cc, &Clientq, entry)
TAILQ_FOREACH(cc, &sc->clientq, entry)
i++;
if (i == 0)
return;
winlist = xcalloc(i, sizeof(*winlist));
TAILQ_FOREACH(cc, &Clientq, entry)
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)
{
@ -235,19 +299,9 @@ xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
}
void
xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *sc)
xu_ewmh_net_number_of_desktops(struct screen_ctx *sc)
{
long viewports[2] = {0, 0};
/* We don't support large desktops, so this is (0, 0). */
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
}
void
xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *sc)
{
long ndesks = CALMWM_NGROUPS;
long ndesks = Conf.ngroups;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
@ -274,27 +328,90 @@ xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
}
void
xu_ewmh_net_current_desktop(struct screen_ctx *sc, long idx)
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 *)&idx, 1);
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
}
void
xu_ewmh_net_desktop_names(struct screen_ctx *sc, char *data, int n)
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++;
}
}
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 *)data, n);
cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len);
free(p);
}
/* Application Window Properties */
void
xu_ewmh_net_wm_desktop(struct client_ctx *cc)
int
xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n)
{
long no = cc->group->shortcut;
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_ewmh_set_net_wm_desktop(struct client_ctx *cc)
{
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 *)&no, 1);
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
}
Atom *
@ -302,15 +419,15 @@ xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
{
Atom *state, *p = NULL;
if ((*n = xu_getprop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
(unsigned char **)&p)) <= 0)
return (NULL);
return NULL;
state = xcalloc(*n, sizeof(Atom));
state = xreallocarray(NULL, *n, sizeof(Atom));
(void)memcpy(state, p, *n * sizeof(Atom));
XFree((char *)p);
return (state);
return state;
}
void
@ -318,23 +435,38 @@ xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
Atom first, Atom second)
{
unsigned int i;
static struct handlers {
int atom;
int property;
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_vmaximize },
client_toggle_vmaximize },
{ _NET_WM_STATE_MAXIMIZED_HORZ,
CLIENT_HMAXIMIZED,
client_hmaximize },
client_toggle_hmaximize },
{ _NET_WM_STATE_HIDDEN,
CLIENT_HIDDEN,
client_toggle_hidden },
{ _NET_WM_STATE_FULLSCREEN,
CLIENT_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++) {
@ -343,11 +475,11 @@ xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
continue;
switch (action) {
case _NET_WM_STATE_ADD:
if ((cc->flags & handlers[i].property) == 0)
if (!(cc->flags & handlers[i].flag))
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_REMOVE:
if (cc->flags & handlers[i].property)
if (cc->flags & handlers[i].flag)
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_TOGGLE:
@ -364,14 +496,24 @@ xu_ewmh_restore_net_wm_state(struct client_ctx *cc)
atoms = xu_ewmh_get_net_wm_state(cc, &n);
for (i = 0; i < n; i++) {
if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ])
client_hmaximize(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_STICKY])
client_toggle_sticky(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT])
client_vmaximize(cc);
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_fullscreen(cc);
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);
}
@ -383,25 +525,40 @@ xu_ewmh_set_net_wm_state(struct client_ctx *cc)
int n, i, j;
oatoms = xu_ewmh_get_net_wm_state(cc, &n);
atoms = xcalloc((n + _NET_WM_STATES_NITEMS), sizeof(Atom));
atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom));
for (i = j = 0; i < n; i++) {
if (oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] &&
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_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_HMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ];
if (cc->flags & CLIENT_VMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT];
if (cc->flags & CLIENT_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);
@ -409,31 +566,3 @@ xu_ewmh_set_net_wm_state(struct client_ctx *cc)
XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
free(atoms);
}
void
xu_xorcolor(XftColor a, XftColor b, XftColor *r)
{
r->pixel = a.pixel ^ b.pixel;
r->color.red = a.color.red ^ b.color.red;
r->color.green = a.color.green ^ b.color.green;
r->color.blue = a.color.blue ^ b.color.blue;
r->color.alpha = 0xffff;
}
int
xu_xft_width(XftFont *xftfont, const char *text, int len)
{
XGlyphInfo extents;
XftTextExtentsUtf8(X_Dpy, xftfont, (const FcChar8*)text,
len, &extents);
return (extents.xOff);
}
void
xu_xft_draw(struct screen_ctx *sc, const char *text, int color, int x, int y)
{
XftDrawStringUtf8(sc->xftdraw, &sc->xftcolor[color], sc->xftfont,
x, y, (const FcChar8*)text, strlen(text));
}