]> git.mdlowis.com Git - proto/lwm.git/commitdiff
initial commit
authorMichael D. Lowis <mike@mdlowis.com>
Mon, 6 May 2019 02:49:01 +0000 (22:49 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Mon, 6 May 2019 02:49:01 +0000 (22:49 -0400)
22 files changed:
AUTHORS [new file with mode: 0644]
BUGS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0755]
Imakefile [new file with mode: 0644]
README [new file with mode: 0644]
README.md [new file with mode: 0644]
TODO [new file with mode: 0644]
client.c [new file with mode: 0644]
cursor.c [new file with mode: 0644]
disp.c [new file with mode: 0644]
error.c [new file with mode: 0644]
ewmh.c [new file with mode: 0644]
ewmh.h [new file with mode: 0644]
lwm [new file with mode: 0755]
lwm.c [new file with mode: 0644]
lwm.h [new file with mode: 0644]
lwm.man [new file with mode: 0644]
manage.c [new file with mode: 0644]
no_xmkmf_makefile [new file with mode: 0644]
shape.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..b26d002
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+enh    Elliott Hughes  ehughes@bluearc.com
+jfc    James Carter    james@jfc.org.uk
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..1e31dd9
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,33 @@
+we're possibly being a bit overzealous about maintaining the window stack,
+with lots of flickering windows as a result. it's particularly noticeable
+when you're running a desktop - but then that's a stupid thing to do anyway.
+not sure how to fix it without maintaining a lot more data about the window
+stack.
+
+reported by corey holcomb-hockin: lwm producues messages like this when popups appear:
+lwm: protocol request X_ConfigureWindow on resource 0xa0000e failed: 
+BadWindow (invalid Window parameter)
+lwm: protocol request X_SendEvent on resource 0xa0000e failed: BadWindow 
+(invalid Window parameter)
+lwm: protocol request X_ConfigureWindow on resource 0xa0000e failed: 
+BadWindow (invalid Window parameter)
+lwm: protocol request X_SendEvent on resource 0xa0000e failed: BadWindow 
+(invalid Window parameter)
+lwm: protocol request X_SetInputFocus on resource 0xa0000e failed: 
+BadWindow (invalid Window parameter)
+lwm: protocol request X_SetInputFocus on resource 0x8000de failed: 
+BadMatch (invalid parameter attributes)
+
+martin and elliott still have window problems with edit, although it's
+better than it was. apparently the window is now placed correctly,
+but gets larger between successive runs. i can't reproduce this,
+unfortunately.
+
+chris reports that positioning a window on the bottom or right edge of a screen
+and then resizing the edge opposite to that touching the edge of the screen
+causes the window to jump in 1 unit (pixel or character depending on the
+application). also, and possibly related, holding button 1 to resize any edge
+apart from the right, and then moving the mouse along that edge (ie.
+perpendicular to the normal movement for resizing) causes the window to shrink
+one unit.
+
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..dcfa4c2
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..0bc30a0
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,486 @@
+Change Log for "lwm"
+
+2016-02-12     jfc     York
+       Patches from Greg Kenneky and other fixes for fullscreen windows.
+
+       Released lvm-1.2.4.
+
+2013-07-09     jfc     York
+       Applied a couple of minor patches suggested by Jari Aalto, the
+       Debian package maintainer.
+
+       Released lwm-1.2.3.
+
+
+2009-11-24     jfc     York
+       Released lwm-1.2.2.
+
+2009-11-20     jfc     York
+       Improved performance by only checking for pending X events when the
+       socket it ready for reading.
+
+       Fixed applyGravity() bug that caused frameless windows to be
+       mis-positioned.
+
+       Applied a workaround in destroy() to avoid error reports when closing
+       windows.
+
+2005-01-28     jfc     York
+
+       Applied a patch from Chris Reece that ensures that the popup
+       menu does not disappear off the bottom of the screen.
+
+2004-09-30     jfc     York
+
+       Fixed an issue with IRIX 6.5 and lwm, where the root menu could
+       not be used because motion events had coordinates with respect to
+       the popup, not the root. Fixed by explicitly using the root window
+       coordinates.
+
+       Released lwm-1.2.1.
+
+2004-09-28     jfc     York
+
+       Added missing -lSM to no_xmkmf_makefile.
+
+2003-12-09     jfc     York
+
+       Fixed bug (reported by Matthew Wilcox) where windows with extremely
+       long names could cause the pop menu to be unusable. Fixed by
+       maintaining a separate, shortened name for the menu, if
+       necessary ("this is a very very [...] ry long window name"). This
+       takes no account of UTF-8 names as yet.
+
+2003-12-08     jfc     York
+
+       Fixed bug (reported by Eugene Wong) where resizing the top of
+       a window would cause it to jump up several pixels. The height of
+       the titlebar was not being considered when calculating mouse
+       motion in reshaping_motionnotify().
+
+       Modified manage() to avoid autoplacing windows during
+       initialisation.
+
+       Released lwm-1.2.0.
+
+2003-12-03     jfc     York
+
+       Applied patch from Elliott that gives focus to new windows in
+       click-to-focus mode.
+
+       Changed the buttonpress code in disp.c to ignore scroll wheel
+       "clicks".
+
+       Modified Client_Remove so that, in click-to-focus mode, it
+       refocuses on the most sensible window (either the top window,
+       or the window that the closing window was a transient for).
+
+       Attempted to fix the edit placement bug by adding titleHeight()
+       to the supplied X coordinate during a ConfigureRequest event,
+       and not attempting to fix clients that don't supply a border
+       width during a configure request.
+
+       Fixed fullscreen-mode bug where galeon windows appeared to jump
+       up and to the left after the first click.
+
+       Released lwm-1.1.7.
+
+2003-11-28     jfc     York
+
+       Changed the behaviour when unhiding a window in click-to-focus mode.
+       An unhidden window now automatically gets focus in this mode.
+
+       Fixed a small bug in the session management code that could
+       cause a crash when lwm quit.
+
+       Moved a call to ewmh_set_client_list make before ewmh was
+       initialised.
+
+       Changed lwm's behaviour when minimising windows. Button three must
+       now be pressed and released before the window is hidden (or
+       moved to the bottom of the stack). This ensures that lwm swallows
+       all the events generated during the operation, and allows the user
+       to back out of the operation by moving the mouse out of the window
+       before releasing the button.
+
+       Removed include of Xm/MwmUtil.h in manage.c, and the HAVE_MOTIF
+       kludge from the Imakefile, in favour of copying the few lines
+       that are required from Xm/MwmUtil.h (LessTif, so hopefully no
+       licensing issues).
+
+       Fixed bug that caused the last cursor displayed in a frame
+       to be incorrectly used when moving into the frame when the
+       root menu was on screen. This is done by brute force -
+       see Client_ResetAllCursors().
+
+       Released lwm-1.1.6.
+
+2003-11-26     jfc     York
+
+       Fixed bug in Client_MakeSane that caused occasional crashes
+       during window moves/resizing. Should investigate why it occasionally
+       gets called with a NULL client.
+
+2003-11-03     jfc     York
+
+       Added an entry for LeaveNotify in the dispatch table (disp.c).
+
+2003-08-13     jfc     York
+
+       Removed "error" message when lwm fails to connect to a session
+       manager. This isn't actually an error and the message is confusing.
+
+2003-08-01     jfc     York
+
+       Fixed bug that allowed clients to grab the focus and confuse lwm.
+
+       Cleaned up the code for raising and lowering clients, and added
+       code to prevent a client from being raised above its transients.
+
+       Retired disp.old, and CLOSE_PATCH.txt.
+
+       Added an edge resistance to the workarea, so that window may
+       be moved to the edge of the workarea without precise mousing,
+       as requested by MAD. EDGE_RESIST in lwm.h defines the number of
+       pixels of resistance and may be safely set to zero.
+
+       Released lwm-1.1.5.
+
+2003-07-31     jfc     York
+
+       In click-to-focus mode, always draw the box in the frame.
+
+2003-07-29     jfc     York
+
+       Added a click-to-focus mode. The default remains (sloppy)
+       enter-to-focus.
+
+       Released lwm-1.1.4.
+
+
+2003-07-28     jfc     York
+
+       Updated no_xmkmf_makefile to reflect the changes made since 1.01.
+
+2003-07-10     jfc     York
+
+       Fixed a bug in manage.c than prevented lwm compiling on systems
+       with no variety of Motif installed. If this means you, remove
+       _DHAVE_MOTIF from Imakefile.
+
+       Released lwm-1.1.3.
+
+2003-07-08     jfc     York
+
+       Added support for NET_MOVERESIZE, but I cannot find any
+       applications the want to use it, apart from the keyboard
+       variants. I don't know what to do about the keyboard move/resize.
+
+2003-07-03     jfc     York
+
+       Fixed a few buglets thrown up by running lwm through the compiler
+       with all warnings on.
+
+2003-07-02     jfc     York
+
+       In Client_MakeSane(), added a check to prevent windows being
+       moved into a position where they might be completely obscured
+       by panels/docks.
+
+       Changed ewmh_set_strut() to run Client_MakeSane() across all
+       clients when the work area changes. This avoids clients getting
+       lost behind panels/docks.
+
+       Added support for _NET_WM_STATE_ABOVE and
+       _NET_WM_STATE_BELOW. Added fix_stack() to maintain the window
+       stack as dictated by the EWMH spec.
+
+2003-07-01     jfc     York
+
+       Added support for _NET_WM_STRUT. lwm now maintains _NET_WORKAREA
+       correctly, and takes the reserved space into account in its
+       window placing algorithm.
+
+       Released lwm-1.1.2.
+
+2003-06-30     jfc     York
+
+       Fixed bug that caused tk menus to be badly placed placed by
+       sending a configure notify where appropriate in setactive().
+
+       Removed compile time option of prepending window title's with
+       the client machines's name (PREPEND_CLIENT_MACHINE).
+
+       Added i18n support for window titles, using UTF8 names from
+       _NET_WM_NAME where available and supported (ie XFree86).
+
+       Added code in disp.c to change the pointer in some areas of the
+       frame to indicate the action taken by button1. I didn't allow the
+       "move" pointer in the titlebar because it looked nasty. Added
+       the xkill pointer for the the box. This was a TODO item.
+
+2003-06-28     jfc     York
+
+       Added GPL headers to all the source files.
+
+       Released lwm-1.1.1.
+
+2003-06-27     jfc     York
+
+       Fixed the bug where each GTK window generated an extra
+       window when lwm shut down by unmapping all the clients in
+       Client_FreeAll(). Elliott thinks this is bad magic, and that
+       the X server should lose the windows, but this doesn't happen
+       with XFree86.
+
+       Fixed bug, reported by Ed Porter, that caused moving the mouse
+       wheel to generate xterms. Wheel mice generate button press events
+       on buttons 4 and 5 and shell() wasn't taking this into account.
+
+       Fixed silly bug in motifWouldDecorate(): windows should have a
+       frame is MWM_DECOR_ALL is set.
+
+2003-06-26     jfc     York
+
+       Shaped windows now work again. I'm not sure what I changed
+       to break it, but the fix was to process shaped windows in
+       scanWindowTree (they were previously ignored). They had to be
+       clients anyway, if they were to appear in _NET_CLIENT_LIST.
+
+2003-06-25     jfc     York
+
+       Fixed bug that caused frameless windows to be immoveable.
+
+       In manage.c, allowed lwm to fall back on Motif hints when
+       deciding if a window should have a frame, if _NET_WM_WINDOW_TYPE
+       is not set. This breaks the EWMH spec, in that a window
+       without _NET_WM_WINDOW_TYPE should be assumed to have
+       _NET_WM_WINDOW_TYPE_NORMAL, but it's the only way for older
+       apps to indicate that they don't want decorating, and in the
+       absence of Motif hints the default state is
+       _NET_WM_WINDOW_TYPE_NORMAL.
+
+2003-06-24     jfc     York
+
+       Fixed the following TODO item:
+               allow users to back out of closing a window if
+               they leave the box before letting go of the button.
+       Implemented by adding an extra wm_closing_window mode rather
+       than adding to the Client structure, as per AMidthune's
+       patch. Not sure which is the better solution, though.
+
+       Added initial support for _NET_WM_STATE, but only for
+       _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER and
+       _NET_WM_HIDDEN.
+
+       Added simple hardwired _NET_WM_ALLOWED_ACTIONS support, and
+       support for the _NET_CLOSE_WINDOW client message.
+
+       First attempt an _WM_STATE_FULLSCREEN and a full-screen mode.
+       It's not quite right yet, but useable.
+
+
+2003-06-23     jfc     York
+
+       Fixed some silly bugs in the session management code.
+
+       Added initial EWMH code using the 1.2 spec:
+       http://www.freedesktop.org/standards/wm-spec/1.2/html/
+       Initial support covers the mechanisms for announcing support
+       for EWMH (_NET_SUPPORTED, _NET_SUPPORTING_WM_CHECK), the
+       client list and active client (_NET_CLIENT_LIST and
+       _NET_ACTIVE_WINDOW), and the window type (_NET_WM_WINDOW_TYPE).
+       Windows may now be frameless if their window type indicates.
+
+2003-06-21     jfc     York
+
+       Added session management so that GNOME2's gnome-session does
+       not wait a long timeout when starting the window manager.
+
+2000-02-08     enh     Basel
+       
+       Tried out a patch from Robert Bauer so that it's possible to move
+       windows with button 1, if you're in the ``titlebar'' (i.e. not touching
+       the top border). This makes it easier for Windows users to cope
+       with lwm, and easier for those with two-button mice (or laptops)
+       too. At the moment, "mv disp.old disp.c" will give back the old
+       behaviour.
+
+1999-11-11     enh     Basel
+
+       Fixed a cut-and-paste bug in client.c that made the check for
+       a window being too large or too small wrong. This bug was found
+       by Mike Meyer.
+
+1999-09-22     enh     Basel
+
+       Altered the button-press code so that it's now easier for unhappy
+       users to alter which button performs which function. Simply edit
+       lwm.h and modify the three relevant #define statements.
+
+1999-07-19     enh     Basel
+
+       Added a handler for circulation events so that other programs
+       can offer "Alt-Tab" functionality.
+
+1999-07-08     enh     Basel
+
+       Fixed the cosmetic problem with titlebars of dialogue boxes. If
+       this looks to be OK, I can think about another lwm release.
+
+1999-06-10     enh     Basel
+
+       Incorporated bug fix by Adrian Colley regarding the attempt in
+       manage.c to call XSetWindowBorderWidth on an InputOnly window,
+       and moved the #include of <signal.h> after <sys/types.h> so that
+       lwm can compile on Solaris 2.6. Cosmetic change to move the close
+       box to line up with the client window. The effect is spoilt if the
+       child insists on drawing a black border around itself, though.
+
+1999-02-07     enh     Basel
+       
+       Title-bars no longer pop up and down. An inactive window has a
+       grey title instead. This means less load on the server, no annoying
+       "I want to type the information from one window's title-bar into
+       the current window but can't" syndrome, and a final solution to
+       the race condition that's been with us since the very beginning.
+       
+       The size feedback no longer pops up as soon as you grab a window,
+       because that made it almost impossible to grab a window without
+       resizing it.
+
+1998-11-03     enh     Basel
+
+       The size feedback now pops up as soon as you grab a window,
+       rather than waiting for you to actually move.
+
+1998-10-06     enh     Basel
+
+       Al pointed out that my Sun actually has two framebuffers. One
+       monitor-lugging later, and I suddenly have a need for a window
+       manager that can cope with multiple screens. And here it is!
+
+1998-05-29     enh     Basel
+
+       Fixed window minimum/maximum height code so that it no longer
+       includes the title decoration. Menu now pops down if a window
+       disappears while the menu is up.
+
+1998-03-23     enh     Basel
+
+       Removed unused constant. A little tidying up, renaming.  Some
+       debugging code removed. The width of the size-feedback window
+       is now calculated at run-time depending on the size of the screen.
+
+1998-02-05     enh     Basel
+
+       Fixed bug found by Marty Olevitch: lwm's automatic window
+       placement heuristics broke down when either the right or bottom
+       of the display were reached.
+       Changed menu placement to ensure that the menu is fully
+       on-screen.
+
+1998-01-06     enh     Basel
+
+       Fixed bug found by J. Han whereby lwm dumped core if a window
+       disappeared while being reshaped.
+
+1997-09-01     enh     Basel
+
+       "Push to back" functionality moved from button 3 click in box
+       to button 3 click anywhere in frame with Shift held down.
+
+1997-08-29     enh     Basel
+
+       Simple version numbering introduced.
+
+1997-08-25     enh     Basel
+
+       Fixed stupid mistake introduced with the last change, with regard
+       to setting the input focus.
+
+1997-08-22     enh     Basel
+
+       Xt applications (strictly, applications whose window title is
+       the same as their class hint resource name) no longer have a
+       title bar. This means it's more awkward to kill them, but that
+       they don't have pointless decoration.
+
+1997-08-07     enh     Basel
+
+       Bug related to hiding windows fixed.
+
+1997-08-06     enh     Basel
+
+       The size indictor now has the correct GC settings. Whoops!
+       Improved handling of WM_NORMAL_HINTS. Amongst other things,
+       this means that size reporting of xterm et al is more reliable.
+
+1997-07-31     enh     Basel
+
+       Reshaping now uses the popup to display the current width and
+       height of the window being reshaped (in whatever units it uses).
+
+1997-07-04     enh     Swanwick
+
+       Clicking button 3 on the "box" pushes the window to the bottom.
+       Changing image in xv no longer causes the window to gravitate to
+       the southeast. There's an ICCCM convention that clients should
+       set the border width with each ConfigureWindow request. As usual,
+       many clients fail to follow this convention. I get the distinct
+       impression that the very reason for the existance of the Xt
+       library is because the X11 protocol and ICCCM are so messy and
+       involved that the only way to make X11 bearable was to write
+       this code once and for all. The menu code has been rewritten,
+       changing as a side-effect the order in which hidden windows
+       appear on the menu. The rewrite now means that the order is very
+       easy to change for experiments like alphabetical ordering etc. I
+       like it as it is: a stack.
+
+1997-06-24     enh     York
+
+       Now handles NoExpose events. Better protocol error reporting.
+       Default minimum size calculation improved.
+
+1997-06-23     enh     York
+
+       Both button 1 and 2 can now have commands associated with them.
+       See the documentation for details. Windows whose minimum and
+       maximum sizes are identical can no longer be resized. The
+       oscillation race condition is now less likely to occur. Some
+       dead code removed.
+
+1997-05-25     enh     York
+
+       lwm now does the right thing with respect to hidden windows on
+       exit and startup. a hidden window is now re-hidden if lwm exits
+       and is then restarted.
+
+1997-05-21     enh     York
+
+       Fixed a bug that meant a client could confuse lwm by remapping
+       a hidden window: the menu of hidden windows wasn't being updated.
+
+1997-05-16     enh     York
+
+       A bug relating to ConfigureRequests on the current window caused
+       the title-bar to be redrawn incorrectly. Once again, this came
+       to light with xv.
+
+       The "New Shell" command has gone from the button 3 menu, and
+       button 2 now performs this function.
+
+1997-05-09     enh     York
+
+       This version fixes a bug relating to ConfigureRequests. Client
+       windows that were resized under program control were resized,
+       but the client was misinformed as to what change had actually
+       taken place. xv's optimised redraw, for example, missed out on
+       part of the window because of this.
+       The behaviour with regard to hidden windows on exit has also
+       changed. They're now remapped, but lowered in the window stack.
+       This means that you don't lose them, but that they don't
+       obliterate the more important windows on your screen if you kill
+       the window manager.
+
+- Initial announcement on comp.windows.x.announce -
diff --git a/INSTALL b/INSTALL
new file mode 100755 (executable)
index 0000000..32c8967
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,11 @@
+#!/bin/cat
+
+Installing lwm
+
+1. Compile lwm:
+
+       xmkmf ; make ; strip lwm
+
+2. Copy the binary to where you want it.
+
+3. Edit your Xsession or whatever to call lwm.
diff --git a/Imakefile b/Imakefile
new file mode 100644 (file)
index 0000000..da76f1c
--- /dev/null
+++ b/Imakefile
@@ -0,0 +1,29 @@
+XCOMM lwm, a window manager for X11
+XCOMM Copyright (C) 1997-2016 Elliott Hughes, James Carter
+XCOMM 
+XCOMM This program is free software; you can redistribute it and/or
+XCOMM modify it under the terms of the GNU General Public License
+XCOMM as published by the Free Software Foundation; either version 2
+XCOMM of the License, or (at your option) any later version.
+XCOMM 
+XCOMM This program is distributed in the hope that it will be useful,
+XCOMM but WITHOUT ANY WARRANTY; without even the implied warranty of
+XCOMM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+XCOMM GNU General Public License for more details.
+XCOMM 
+XCOMM You should have received a copy of the GNU General Public License
+XCOMM along with this program; if not, write to the Free Software
+XCOMM Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+INCLUDES = -I$(TOP)
+DEPLIBS = $(DEPXLIB) $(DEPSMLIB)
+LOCAL_LIBRARIES = $(XLIB) $(SMLIB) -lICE
+DEFINES = -DSHAPE
+
+HEADERS = lwm.h ewmh.h
+SRCS = lwm.c manage.c mouse.c client.c cursor.c error.c disp.c shape.c resource.c session.c ewmh.c
+OBJS = ${SRCS:.c=.o}
+
+ComplexProgramTarget(lwm)
+
+${OBJS}: ${HEADERS}
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..6904297
--- /dev/null
+++ b/README
@@ -0,0 +1,4 @@
+This is lwm, a window manager for X.
+
+See INSTALL for install instructions and COPYRIGHT for license details.
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..d7d3cae
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+lwm
+===
+
+*lwm* is a window manager for X that tries to keep out of your face. There are no icons, no button bars, no icon docks, no root menus, no nothing: if you want all that, then other programs can provide it. There's no configurability either: if you want that, you want a different window manager; one that helps your operating system in its evil conquest of your disc space and its annexation of your physical memory. 
+
+[http://www.jfc.org.uk/software/lwm.html](http://www.jfc.org.uk/software/lwm.html)
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..ef7bb86
--- /dev/null
+++ b/TODO
@@ -0,0 +1,2 @@
+use XFT extensions where available?
+
diff --git a/client.c b/client.c
new file mode 100644 (file)
index 0000000..e7bf09e
--- /dev/null
+++ b/client.c
@@ -0,0 +1,783 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <unistd.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include "lwm.h"
+#include "ewmh.h"
+
+Client *current;
+Client *last_focus = NULL;
+static Client *clients;
+
+static int popup_width;        /* The width of the size-feedback window. */
+
+Edge interacting_edge;
+
+static void sendClientMessage(Window, Atom, long, long);
+
+Client *
+client_head(void) {
+       return clients;
+}
+
+void
+setactive(Client *c, int on, long timestamp) {
+       int inhibit;
+
+       if (c == 0 || hidden(c))
+               return;
+
+       inhibit = !c->framed;
+
+       if (!inhibit) {
+               XMoveResizeWindow(dpy, c->parent,
+                       c->size.x, c->size.y - titleHeight(),
+                       c->size.width, c->size.height + titleHeight());
+               XMoveWindow(dpy, c->window, border, border + titleHeight());
+               sendConfigureNotify(c);
+       }
+
+       if (on && c->accepts_focus) {
+               XSetInputFocus(dpy, c->window, RevertToPointerRoot, CurrentTime);
+               if (c->proto & Ptakefocus)
+                       sendClientMessage(c->window, wm_protocols,
+                               wm_take_focus, timestamp);
+               if (focus_mode == focus_click) {
+                       XUngrabButton(dpy, AnyButton, AnyModifier, c->window);
+               }
+               cmapfocus(c);
+       }
+
+       /* FIXME: is this sensible? */
+       if (on && !c->accepts_focus) {
+               XSetInputFocus(dpy, None, RevertToPointerRoot, CurrentTime);
+       }
+
+       if (!on && focus_mode == focus_click)
+               XGrabButton(dpy, AnyButton, AnyModifier, c->window, False,
+                       ButtonPressMask | ButtonReleaseMask, GrabModeAsync,
+                       GrabModeSync, None, None);
+
+       if (!inhibit)
+               Client_DrawBorder(c, on);
+}
+
+
+void
+Client_DrawBorder(Client *c, int active) {
+       int quarter = (border + titleHeight()) / 4;
+
+       if (c->parent == c->screen->root || c->parent == 0 ||
+               c->framed == False || c->wstate.fullscreen == True)
+               return;
+
+       XSetWindowBackground(dpy, c->parent,
+               active ? c->screen->black : c->screen->gray);
+       XClearWindow(dpy, c->parent);
+
+       /* Draw the ``box''. */
+       if (active || focus_mode == focus_click) {
+               XDrawRectangle(dpy, c->parent, c->screen->gc,
+                       quarter + 2, quarter, 2 * quarter, 2 * quarter);
+       }
+
+       /* Draw window title. */
+       if (c->name != 0) {
+#ifdef X_HAVE_UTF8_STRING
+               if (c->name_utf8 == True)
+                       Xutf8DrawString(dpy, c->parent, font_set,
+                               c->screen->gc, border + 2 + (3 * quarter),
+                               2 + ascent(font_set_ext),
+                               c->name, c->namelen);
+               else
+#endif
+                       XmbDrawString(dpy, c->parent, font_set,
+                               c->screen->gc, border + 2 + (3 * quarter),
+                               2 + ascent(font_set_ext),
+                               c->name, c->namelen);
+       }
+}
+
+
+Client *
+Client_Get(Window w) {
+       Client * c;
+
+       if (w == 0 || (getScreenFromRoot(w) != 0))
+               return 0;
+
+       /* Search for the client corresponding to this window. */
+       for (c = clients; c; c = c->next)
+               if (c->window == w || c->parent == w)
+                       return c;
+
+       /* Not found. */
+       return 0;
+}
+
+
+Client *
+Client_Add(Window w, Window root) {
+       Client * c;
+
+       if (w == 0 || w == root)
+               return 0;
+
+       /* Search for the client corresponding to this window. */
+       for (c = clients; c != 0; c = c->next)
+               if (c->window == w || c->parent == w)
+                       return c;
+
+       c = calloc(1, sizeof *c);
+       c->window = w;
+       c->parent = root;
+       c->framed = False;
+       c->hidden = False;
+       c->state = WithdrawnState;
+       c->internal_state = INormal;
+       c->cmap = None;
+       c->name =  0;
+       c->menu_name =  0;
+       c->cursor = ENone;
+       c->wtype = WTypeNone;
+       c->wstate.skip_taskbar = False;
+       c->wstate.skip_pager = False;
+       c->wstate.fullscreen = False;
+       c->wstate.above = False;
+       c->wstate.below = False;
+       c->strut.left = 0;
+       c->strut.right = 0;
+       c->strut.top = 0;
+       c->strut.bottom = 0;
+       c->ncmapwins = 0;
+       c->cmapwins = 0;
+       c->wmcmaps = 0;
+       c->accepts_focus = 1;
+       c->next = clients;
+
+       /* Add to head of list of clients. */
+       clients = c;
+       return clients;
+}
+
+
+void
+Client_Remove(Client *c) {
+       Client * cc;
+       ScreenInfo *screen = c->screen;
+
+       if (c == 0)
+               return;
+
+       /* Remove the client from our client list. */
+       if (c == clients)
+               clients = c->next;
+       for (cc = clients; cc && cc->next; cc = cc->next) {
+               if (cc->next == c)
+                       cc->next = cc->next->next;
+       }
+
+       /* Remove it from the hidden list if it's hidden. */
+       if (hidden(c)) {
+               /* Al Smith points out that you also want to get rid of the menu
+                * so you can be sure that if you let go on an item, you always
+                * get the corresponding window. */
+               if (mode == wm_menu_up) {
+                       XUnmapWindow(dpy, current_screen->popup);
+                       mode = wm_idle;
+               }
+       }
+
+       /* A deleted window can no longer be the current window. */
+       if (c == current || (current == NULL && c == last_focus)) {
+               Client *focus = NULL;
+
+               /* As pointed out by J. Han, if a window disappears while it's
+                * being reshaped you need to get rid of the size indicator. */
+               if (c == current && mode == wm_reshaping) {
+                       XUnmapWindow(dpy, current_screen->popup);
+                       mode = wm_idle;
+               }
+               if (focus_mode == focus_click) {
+                       /* Try and find the window that this was a transient
+                        * for, else focus on the top client. */
+                       if (c->trans != None) {
+                               focus = Client_Get(c->trans);
+                       }
+                       if (!focus) {
+                               Window dw1;
+                               Window dw2;
+                               Window *wins;
+                               unsigned int nwins;
+
+                               XQueryTree(dpy, c->screen->root, &dw1,
+                                       &dw2, &wins, &nwins);
+                               while (nwins) {
+                                       focus = Client_Get(wins[nwins -1]);
+                                       if (focus) break;
+                                       nwins--;
+                               }
+                               if (wins) XFree(wins);
+                       }
+               }
+               Client_Focus(focus, CurrentTime);
+       }
+
+       if (getScreenFromRoot(c->parent) == 0)
+               XDestroyWindow(dpy, c->parent);
+
+       if (c->ncmapwins != 0) {
+               XFree(c->cmapwins);
+               free(c->wmcmaps);
+       }
+
+       if (c->name != 0)
+               free(c->name);
+       if (c->menu_name != 0)
+               free(c->name);
+
+       free(c);
+
+       ewmh_set_client_list(screen);
+       ewmh_set_strut(screen);
+}
+
+
+void
+Client_MakeSane(Client *c, Edge edge, int *x, int *y, int *dx, int *dy) {
+       Bool    horizontal_ok = True;
+       Bool    vertical_ok = True;
+
+       if (edge != ENone) {
+               /*
+                *      Make sure we're not making the window too small.
+                */
+               if (*dx < c->size.min_width)
+                       horizontal_ok = False;
+               if (*dy < c->size.min_height)
+                       vertical_ok = False;
+
+               /*
+                * Make sure we're not making the window too large.
+                */
+               if (c->size.flags & PMaxSize) {
+                       if (*dx > c->size.max_width)
+                               horizontal_ok = False;
+                       if (*dy > c->size.max_height)
+                               vertical_ok = False;
+               }
+
+               /*
+                * Make sure the window's width & height are multiples of
+                * the width & height increments (not including the base size).
+                */
+
+               if (c->size.width_inc > 1) {
+                       int apparent_dx = *dx - 2 * border - c->size.base_width;
+                       int x_fix = apparent_dx % c->size.width_inc;
+
+                       switch (edge) {
+                       case ELeft:
+                       case ETopLeft:
+                       case EBottomLeft:
+                               *x += x_fix;
+                               /*FALLTHROUGH*/
+                       case ERight:
+                       case ETopRight:
+                       case EBottomRight:
+                               *dx -= x_fix;
+                               break;
+                       default: break;
+                       }
+               }
+
+               if (c->size.height_inc > 1) {
+                       int apparent_dy = *dy - 2 * border - c->size.base_height;
+                       int y_fix = apparent_dy % c->size.height_inc;
+
+                       switch (edge) {
+                       case ETop:
+                       case ETopLeft:
+                       case ETopRight:
+                               *y += y_fix;
+                               /*FALLTHROUGH*/
+                       case EBottom:
+                       case EBottomLeft:
+                       case EBottomRight:
+                               *dy -= y_fix;
+                               break;
+                       default: break;
+                       }
+               }
+
+               /*
+                * Check that we may change the client horizontally and vertically.
+                */
+
+               if (c->size.width_inc == 0)
+                       horizontal_ok = False;
+               if (c->size.height_inc == 0)
+                       vertical_ok = False;
+       }
+
+       /* Ensure that at least one border is not entirely within the
+        * reserved areas. Keeping clients completely within the
+        * the workarea is too restrictive, but this measure means they
+        * should always be accessible.
+        * Of course all of this is only applicable if the client doesn't
+        * set a strut itself.                                  jfc
+        */
+       if (c->strut.left == 0 && c->strut.right == 0 &&
+               c->strut.top == 0 && c->strut.bottom == 0) {
+               if ((int)(*y + border) >=
+                       (int)(c->screen->display_height -
+                       c->screen->strut.bottom)) {
+                       *y = c->screen->display_height -
+                               c->screen->strut.bottom -border;
+               }
+               if ((int)(*y + c->size.height - border) <=
+                       (int)c->screen->strut.top) {
+                       *y = c->screen->strut.top + border - c->size.height;
+               }
+               if ((int)(*x + border) >=
+                       (int)(c->screen->display_width -
+                       c->screen->strut.right)) {
+                       *x = c->screen->display_width -
+                               c->screen->strut.right -border;
+               }
+               if ((int)(*x + c->size.width - border) <=
+                       (int)c->screen->strut.left) {
+                       *x = c->screen->strut.left + border - c->size.width;
+               }
+       }
+
+       /*
+        * Introduce a resistance to the workarea edge, so that windows may
+        * be "thrown" to the edge of the workarea without precise mousing,
+        * as requested by MAD.
+        */
+       if (*x < (int)c->screen->strut.left &&
+               *x > ((int)c->screen->strut.left - EDGE_RESIST)) {
+               *x =  (int)c->screen->strut.left;
+       }
+       if ((*x + c->size.width) >
+               (int)(c->screen->display_width - c->screen->strut.right) &&
+               (*x + c->size.width) <
+               (int)(c->screen->display_width - c->screen->strut.right +                       EDGE_RESIST)) {
+               *x = (int)(c->screen->display_width - c->screen->strut.right -
+                       c->size.width);
+       }
+       if ((*y - titleHeight()) < (int)c->screen->strut.top &&
+               (*y - titleHeight()) >
+               ((int)c->screen->strut.top - EDGE_RESIST)) {
+               *y =  (int)c->screen->strut.top + titleHeight();
+       }
+       if ((*y + c->size.height) >
+               (int)(c->screen->display_height - c->screen->strut.bottom) &&
+               (*y + c->size.height) <
+               (int)(c->screen->display_height - c->screen->strut.bottom +                     EDGE_RESIST)) {
+               *y = (int)(c->screen->display_height - c->screen->strut.bottom -
+                       c->size.height);
+       }
+
+       /*
+        * Update that part of the client information that we're happy with.
+        */
+       if (interacting_edge != ENone) {
+               if (horizontal_ok) {
+                       c->size.x = *x;
+                       c->size.width  = *dx;
+               }
+               if (vertical_ok) {
+                       c->size.y = *y;
+                       c->size.height = *dy;
+               }
+       } else {
+               if (horizontal_ok)
+                       c->size.x = *x;
+               if (vertical_ok)
+                       c->size.y = *y;
+       }
+}
+
+void
+size_expose(void) {
+       int width, height;
+       char buf[4*2 + 3 + 1];
+
+       width = current->size.width - 2*border;
+       height = current->size.height - 2*border;
+
+       /* This dance ensures that we report 80x24 for an xterm even when
+        * it has a scrollbar. */
+       if (current->size.flags & (PMinSize|PBaseSize) && current->size.flags & PResizeInc) {
+               if (current->size.flags & PBaseSize) {
+                       width -= current->size.base_width;
+                       height -= current->size.base_height;
+               } else {
+                       width -= current->size.min_width;
+                       height -= current->size.min_height;
+               }
+       }
+
+       if (current->size.width_inc != 0)
+               width /= current->size.width_inc;
+       if (current->size.height_inc != 0)
+               height /= current->size.height_inc;
+
+       snprintf(buf, sizeof(buf), "%i x %i", width, height);
+       XmbDrawString(dpy, current_screen->popup, popup_font_set,
+               current_screen->size_gc,
+               (popup_width - popupWidth(buf, strlen(buf))) / 2,
+               ascent(popup_font_set_ext) + 1, buf, strlen(buf));
+}
+
+static void
+Client_OpaquePrimitive(Client *c, Edge edge) {
+       Cursor cursor;
+       int ox, oy;
+
+       if (c == 0 /*|| c != current*/)
+               return;
+
+       /* Find out where we've got hold of the window. */
+       getMousePosition(&ox, &oy);
+       ox = c->size.x - ox;
+       oy = c->size.y - oy;
+
+       cursor = getEdgeCursor(edge);
+       XChangeActivePointerGrab(dpy, ButtonMask | PointerMotionHintMask |
+               ButtonMotionMask | OwnerGrabButtonMask, cursor, CurrentTime);
+
+       /*
+        * Store some state so that we can get back into the main event
+        * dispatching thing.
+        */
+       interacting_edge = edge;
+       start_x = ox;
+       start_y = oy;
+       mode = wm_reshaping;
+       ewmh_set_client_list(c->screen);
+}
+
+void
+Client_Lower(Client *c)
+{
+       if (c == 0) return;
+
+       XLowerWindow(dpy, c->window);
+       if (c->framed) XLowerWindow(dpy, c->parent);
+       ewmh_set_client_list(c->screen);
+}
+
+void
+Client_Raise(Client *c)
+{
+       Client * trans;
+
+       if (c == 0) return;
+
+       if (c->framed) XRaiseWindow(dpy, c->parent);
+       XRaiseWindow(dpy, c->window);
+
+       for (trans = clients; trans != NULL; trans = trans->next) {
+               if (trans->trans != c->window &&
+                       !(c->framed == True && trans->trans == c->parent))
+                       continue;
+               if (trans->framed) XRaiseWindow(dpy, trans->parent);
+               XRaiseWindow(dpy, trans->window);
+       }
+
+       ewmh_set_client_list(c->screen);
+}
+
+void
+Client_Close(Client *c) {
+       if (c == 0)
+               return;
+
+       /*
+        *      Terminate the client nicely if possible. Be brutal otherwise.
+        */
+       if (c->proto & Pdelete) {
+               sendClientMessage(c->window, wm_protocols, wm_delete, CurrentTime);
+       } else {
+               XKillClient(dpy, c->window);
+       }
+}
+
+void
+Client_SetState(Client *c, int state) {
+       long    data[2];
+
+       data[0] = (long) state;
+       data[1] = (long) None;
+
+       c->state = state;
+       XChangeProperty(dpy, c->window, wm_state, wm_state, 32,
+               PropModeReplace, (unsigned char *) data, 2);
+       ewmh_set_state(c);
+}
+
+static void
+sendClientMessage(Window w, Atom a, long data0, long data1) {
+       XEvent  ev;
+       long    mask;
+
+       memset(&ev, 0, sizeof(ev));
+       ev.xclient.type = ClientMessage;
+       ev.xclient.window = w;
+       ev.xclient.message_type = a;
+       ev.xclient.format = 32;
+       ev.xclient.data.l[0] = data0;
+       ev.xclient.data.l[1] = data1;
+       mask = (getScreenFromRoot(w) != 0) ? SubstructureRedirectMask : 0L;
+
+       XSendEvent(dpy, w, False, mask, &ev);
+}
+
+extern void
+Client_ResetAllCursors(void) {
+       Client *c;
+       XSetWindowAttributes attr;
+
+       for (c = clients; c; c = c->next) {
+               if (c->framed != True) continue;
+               attr.cursor = c->screen->root_cursor;
+               XChangeWindowAttributes(dpy, c->parent,
+                       CWCursor, &attr);
+               c->cursor = ENone;
+       }
+}
+
+extern void
+Client_FreeAll(void) {
+       Client *c;
+       XWindowChanges wc;
+
+       for (c = clients; c; c = c->next) {
+               int not_mapped = !normal(c);
+
+               /* elliott thinks leaving window unmapped causes the x server
+                * to lose them when the window manager quits. it doesn't
+                * happen to me with XFree86's Xnest, but unmapping the
+                * windows stops gtk window generating an extra window when
+                * the window manager quits.
+                * who is right? only time will tell....
+                */
+               XUnmapWindow(dpy, c->parent);
+               XUnmapWindow(dpy, c->window);
+               /* Remap the window if it's hidden.
+               if (not_mapped) {
+                       XMapWindow(dpy, c->parent);
+                       XMapWindow(dpy, c->window);
+               } */
+
+               /* Reparent it, and then push it to the bottom if it was hidden. */
+               XReparentWindow(dpy, c->window, c->screen->root, c->size.x, c->size.y);
+               if (not_mapped)
+                       XLowerWindow(dpy, c->window);
+
+               /* Give it back its initial border width. */
+               wc.border_width = c->border;
+               XConfigureWindow(dpy, c->window, CWBorderWidth, &wc);
+       }
+}
+
+extern void
+Client_ColourMap(XEvent *e) {
+       int i;
+       Client * c;
+
+       for (c = clients; c; c = c->next) {
+               for (i = 0; i < c->ncmapwins; i++) {
+                       if (c->cmapwins[i] == e->xcolormap.window) {
+                               c->wmcmaps[i] = e->xcolormap.colormap;
+                               if (c == current)
+                                       cmapfocus(c);
+                               return;
+                       }
+               }
+       }
+}
+
+extern void
+Client_ReshapeEdge(Client *c, Edge e) {
+       Client_OpaquePrimitive(c, e);
+}
+
+extern void
+Client_Move(Client *c) {
+       Client_OpaquePrimitive(c, ENone);
+}
+
+extern int
+hidden(Client *c) {
+       return c->state == IconicState;
+}
+
+extern int
+withdrawn(Client *c) {
+       return c->state == WithdrawnState;
+}
+
+extern int
+normal(Client *c) {
+       return c->state == NormalState;
+}
+
+extern void
+Client_EnterFullScreen(Client *c) {
+       XWindowChanges fs;
+
+       memcpy(&c->return_size, &c->size, sizeof(XSizeHints));
+       if (c->framed) {
+               c->size.x = fs.x = -border;
+               c->size.y = fs.y = -border;
+               c->size.width = fs.width =
+                       c->screen->display_width + 2 * border;
+               c->size.height = fs.height =
+                       c->screen->display_height + 2 * border;
+               XConfigureWindow(dpy, c->parent,
+                       CWX | CWY | CWWidth | CWHeight, &fs);
+
+               fs.x = border;
+               fs.y = border;
+               fs.width = c->screen->display_width;
+               fs.height = c->screen->display_height;
+               XConfigureWindow(dpy, c->window,
+                       CWX | CWY | CWWidth | CWHeight, &fs);
+               XRaiseWindow(dpy,c->parent);
+       } else {
+               c->size.x = c->size.y = fs.x = fs.y = 0;
+               c->size.width = fs.width = c->screen->display_width;
+               c->size.height = fs.height = c->screen->display_height;
+               XConfigureWindow(dpy, c->window,
+                       CWX | CWY | CWWidth | CWHeight, &fs);
+               XRaiseWindow(dpy,c->window);
+       }
+       sendConfigureNotify(c);
+}
+
+extern void
+Client_ExitFullScreen(Client *c) {
+       XWindowChanges fs;
+
+       memcpy(&c->size, &c->return_size, sizeof(XSizeHints));
+       if (c->framed == True) {
+               fs.x = c->size.x;
+               fs.y = c->size.y - titleHeight();
+               fs.width = c->size.width;
+               fs.height = c->size.height + titleHeight();
+               XConfigureWindow(dpy, c->parent,
+                       CWX | CWY | CWWidth | CWHeight, &fs);
+
+               fs.x = border;
+               fs.y = border + titleHeight();
+               fs.width = c->size.width -(2 * border);
+               fs.height = c->size.height -(2 * border);
+               XConfigureWindow(dpy, c->window,
+                       CWX | CWY | CWWidth | CWHeight, &fs);
+       } else {
+               fs.x = c->size.x;
+               fs.y = c->size.y;
+               fs.width = c->size.width;
+               fs.height = c->size.height;
+               XConfigureWindow(dpy, c->window,
+                       CWX | CWY | CWWidth | CWHeight, &fs);
+       }
+       sendConfigureNotify(c);
+}
+
+extern void
+Client_Focus(Client *c, Time time) {
+       if (current) {
+               setactive(current, 0, 0L);
+               XDeleteProperty(dpy, current->screen->root,
+                       ewmh_atom[_NET_ACTIVE_WINDOW]);
+       }
+
+       if (!c && current) {
+               last_focus = current;
+       } else {
+               last_focus = NULL;
+       }
+       current = c;
+       if (c) {
+               setactive(current, 1, time);
+               XChangeProperty(dpy, current->screen->root,
+                       ewmh_atom[_NET_ACTIVE_WINDOW],
+                       XA_WINDOW, 32, PropModeReplace,
+                       (unsigned char *)&current->window, 1);
+       }
+
+       if (focus_mode == focus_click)
+               Client_Raise(c);
+}
+
+extern void
+Client_Name(Client *c, const char *name, Bool is_utf8) {
+       int tx;
+       static const char dots[] = " [...] ";
+       int cut;
+
+       if (c->name) free(c->name);
+       c->name = strdup((char *) name);
+       c->namelen = strlen(c->name);
+       c->name_utf8 = is_utf8;
+
+       if (c->menu_name) free(c->menu_name);
+       c->menu_name = 0;
+       tx = titleWidth(popup_font_set, c);
+       if (tx <= (c->screen->display_width - (c->screen->display_width / 10)))
+               return;
+
+       /* the menu entry for this client will not fit on the display
+        * (minus 10% for saftey), so produced a truncated version...
+        */
+       cut = 5;
+       do {
+               if (c->menu_name) {
+                       free(c->menu_name);
+                       c->menu_name = 0;
+               }
+               if (cut >= (strlen(c->name) / 2)) break;
+               c->menu_name = strdup(c->name);
+               /* FIXME: this is not UTF-8 safe! */
+               sprintf(&c->menu_name[(strlen(c->name) / 2) - cut], dots);
+               strcat(c->menu_name,
+                       &c->name[(strlen(c->name) / 2) + cut]);
+               c->menu_namelen = strlen(c->menu_name);
+               cut++;
+               tx = titleWidth(popup_font_set, c);
+               if (!tx) break;
+       } while (tx >
+               (c->screen->display_width - (c->screen->display_width / 10)));
+}
diff --git a/cursor.c b/cursor.c
new file mode 100644 (file)
index 0000000..f528095
--- /dev/null
+++ b/cursor.c
@@ -0,0 +1,77 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+
+#include "lwm.h"
+
+typedef struct CursorMapping CursorMapping;
+struct CursorMapping {
+       Edge edge;
+       int font_char;
+};
+
+static CursorMapping cursor_map[] = {
+       {ETopLeft,     XC_top_left_corner},
+       {ETop,         XC_top_side},
+       {ETopRight,    XC_top_right_corner},
+       {ERight,       XC_right_side},
+       {ENone,        XC_fleur},
+       {ELeft,        XC_left_side},
+       {EBottomLeft,  XC_bottom_left_corner},
+       {EBottom,      XC_bottom_side},
+       {EBottomRight, XC_bottom_right_corner},
+       {ENone,        0},
+};
+
+extern void
+initialiseCursors(int screen) {
+       XColor red, white, exact;
+       Colormap cmp;
+       int i;
+
+       cmp = DefaultColormap(dpy, screen);
+
+       XAllocNamedColor(dpy, cmp, "red", &red, &exact);
+       XAllocNamedColor(dpy, cmp, "white", &white, &exact);
+
+       screens[screen].root_cursor = XCreateFontCursor(dpy, XC_left_ptr);
+       XRecolorCursor(dpy, screens[screen].root_cursor, &red, &white);
+
+       screens[screen].box_cursor = XCreateFontCursor(dpy, XC_draped_box);
+       XRecolorCursor(dpy, screens[screen].box_cursor, &red, &white);
+
+       for (i = 0; cursor_map[i].font_char != 0; i++) {
+               Edge e = cursor_map[i].edge;
+               screens[screen].cursor_map[e] =
+                       XCreateFontCursor(dpy, cursor_map[i].font_char);
+               XRecolorCursor(dpy, screens[screen].cursor_map[e],
+                       &red, &white);
+       }
+}
+
+extern Cursor
+getEdgeCursor(Edge edge) {
+       return screens[0].cursor_map[edge];
+}
diff --git a/disp.c b/disp.c
new file mode 100644 (file)
index 0000000..bdf1d9d
--- /dev/null
+++ b/disp.c
@@ -0,0 +1,884 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/Xresource.h>
+#include <X11/Xatom.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lwm.h"
+#include "ewmh.h"
+
+/*
+ *     Dispatcher for main event loop.
+ */
+typedef struct Disp Disp;
+struct Disp {
+       int     type;
+       void    (*handler)(XEvent *);
+};
+
+static void expose(XEvent *);
+static void buttonpress(XEvent *);
+static void buttonrelease(XEvent *);
+static void focuschange(XEvent *);
+static void maprequest(XEvent *);
+static void configurereq(XEvent *);
+static void unmap(XEvent *);
+static void destroy(XEvent *);
+static void clientmessage(XEvent *);
+static void colormap(XEvent *);
+static void property(XEvent *);
+static void reparent(XEvent *);
+static void enter(XEvent *);
+static void circulaterequest(XEvent *);
+static void motionnotify(XEvent *);
+
+void reshaping_motionnotify(XEvent *);
+
+static Disp disps[] =
+{
+       {Expose, expose},
+       {MotionNotify, motionnotify},
+       {ButtonPress, buttonpress},
+       {ButtonRelease, buttonrelease},
+       {FocusIn, focuschange},
+       {FocusOut, focuschange},
+       {MapRequest, maprequest},
+       {ConfigureRequest, configurereq},
+       {UnmapNotify, unmap},
+       {DestroyNotify, destroy},
+       {ClientMessage, clientmessage},
+       {ColormapNotify, colormap},
+       {PropertyNotify, property},
+       {ReparentNotify, reparent},
+       {EnterNotify, enter},
+       {CirculateRequest, circulaterequest},
+       {LeaveNotify, 0},
+       {ConfigureNotify, 0},
+       {CreateNotify, 0},
+       {GravityNotify, 0},
+       {MapNotify, 0},
+       {MappingNotify, 0},
+       {SelectionClear, 0},
+       {SelectionNotify, 0},
+       {SelectionRequest, 0},
+       {NoExpose, 0},
+};
+
+/**
+ * pending it the client in which an action has been started by a mouse press
+ * and we are waiting for the button to be released before performing the action
+ */
+static Client *pending=NULL;
+
+extern void
+dispatch(XEvent * ev) {
+       Disp * p;
+
+       for (p = disps; p < disps + sizeof(disps)/sizeof(disps[0]); p++) {
+               if (p->type == ev->type) {
+                       if (p->handler != 0)
+                               p->handler(ev);
+                       return;
+               }
+       }
+
+       if (!shapeEvent(ev))
+               fprintf(stderr, "%s: unknown event %d\n", argv0, ev->type);
+}
+
+static void
+expose(XEvent * ev) {
+       Client * c;
+       Window w;       /* Window the expose event is for. */
+
+       /* Only handle the last in a group of Expose events. */
+       if (ev->xexpose.count != 0) return;
+
+       w = ev->xexpose.window;
+
+       /*
+       * We don't draw on the root window so that people can have
+       * their favourite Spice Girls backdrop...
+       */
+       if (getScreenFromRoot(w) != 0)
+               return;
+
+       /* Decide what needs redrawing: window frame or menu? */
+       if (current_screen && w == current_screen->popup) {
+               if (mode == wm_reshaping && current != 0)
+                       size_expose();
+       } else {
+               c = Client_Get(w);
+               if (c != 0) {
+                       Client_DrawBorder(c, c == current);
+               }
+       }
+}
+
+static void
+buttonpress(XEvent *ev) {
+       Client *c;
+       XButtonEvent *e = &ev->xbutton;
+       int quarter;
+
+       /* If we're getting it already, we're not in the market for more. */
+       if (mode != wm_idle) {
+               /* but allow a button press to cancel a move/resize,
+                * to satify the EWMH advisory to allow a second mechanism
+                * of completing move/resize operations, due to a race.
+                * (section 4.3) sucky!
+                */
+               if (mode == wm_reshaping) {
+                       mode = wm_idle;
+               }
+               return;
+       }
+
+       c = Client_Get(e->window);
+
+       if (c && c != current && focus_mode == focus_click) {
+               /* Click is not on current window,
+                * and in click-to-focus mode, so change focus
+                */
+               Client_Focus(c, e->time);
+       }
+
+       /*move this test up to disable scroll to focus*/
+       if (e->button >= 4 && e->button <= 7) {
+               return;
+       }
+
+       if (c && c == current && (e->window == c->parent)) {
+               /* Click went to our frame around a client. */
+
+               /* The ``box''. */
+               quarter = (border + titleHeight()) / 4;
+               if (e->x > (quarter + 2) && e->x < (3 + 3*quarter) && e->y > quarter && e->y <= 3*quarter) {
+                       /*Client_Close(c);*/
+                       pending = c;
+                       mode = wm_closing_window;
+                       return;
+               }
+
+               /* Somewhere in the rest of the frame. */
+               if (e->button == HIDE_BUTTON) {
+                       pending = c;
+                       mode = wm_hiding_window;
+                       return;
+               }
+               if (e->button == MOVE_BUTTON) {
+                       Client_Move(c);
+                       return;
+               }
+               if (e->button == RESHAPE_BUTTON) {
+                       XMapWindow(dpy, c->parent);
+                       Client_Raise(c);
+
+                       /* Lasciate ogni speranza voi ch'entrate...  */
+
+                       if (e->x <= border && e->y <= border) {
+                               Client_ReshapeEdge(c, ETopLeft);
+                       } else if (e->x >= (c->size.width - border) && e->y <= border) {
+                               Client_ReshapeEdge(c, ETopRight);
+                       } else if (e->x >= (c->size.width - border) && e->y >= (c->size.height + titleHeight() - border)) {
+                               Client_ReshapeEdge(c, EBottomRight);
+                       } else if (e->x <= border && e->y >= (c->size.height + titleHeight() - border)) {
+                               Client_ReshapeEdge(c, EBottomLeft);
+                       } else if (e->x > border && e->x < (c->size.width - border) && e->y < border) {
+                               Client_ReshapeEdge(c, ETop);
+                       } else if (e->x > border && e->x < (c->size.width - border) && e->y >= border && e->y < (titleHeight() + border)) {
+                               Client_Move(c);
+                       } else if (e->x > (c->size.width - border) && e->y > border && e->y < (c->size.height + titleHeight() - border)) {
+                               Client_ReshapeEdge(c, ERight);
+                       } else if (e->x > border && e->x < (c->size.width - border) && e->y > (c->size.height - border)) {
+                               Client_ReshapeEdge(c, EBottom);
+                       } else if (e->x < border && e->y > border && e->y < (c->size.height + titleHeight() - border)) {
+                               Client_ReshapeEdge(c, ELeft);
+                       }
+                       return;
+               }
+               return;
+       }
+
+       /* Deal with root window button presses. */
+       if (e->window == e->root) {
+               if (e->button == Button3) {
+                       cmapfocus(0);
+               } else {
+                       shell(getScreenFromRoot(e->root), e->button, e->x, e->y);
+               }
+       }
+}
+
+static void
+buttonrelease(XEvent *ev) {
+       XButtonEvent *e = &ev->xbutton;
+       int quarter;
+
+       if (mode == wm_reshaping)
+               XUnmapWindow(dpy, current_screen->popup);
+       else if (mode == wm_closing_window) {
+               /* was the button released within the window's box?*/
+               quarter = (border + titleHeight()) / 4;
+               if (pending != NULL &&
+                       (e->window == pending->parent) &&
+                       (e->x > (quarter + 2) &&
+                               e->x < (3 + 3*quarter) &&
+                               e->y > quarter && e->y <= 3*quarter))
+                                       Client_Close(pending);
+               pending = NULL;
+       } else if (mode == wm_hiding_window) {
+               /* was the button release within the window's frame? */
+               if (pending != NULL &&
+                       (e->window == pending->parent) &&
+                       (e->x >= 0) && (e->y >= 0) &&
+                       (e->x <= pending->size.width) &&
+                       (e->y <= (pending->size.height + titleHeight()))) {
+                       if (e->state & ShiftMask) {
+                               Client_Lower(pending);
+                       }
+               }
+               pending = NULL;
+       }
+
+       mode = wm_idle;
+}
+
+static void circulaterequest(XEvent *ev) {
+       XCirculateRequestEvent * e = &ev->xcirculaterequest;
+       Client * c;
+
+       c = Client_Get(e->window);
+
+       if (c == 0) {
+               if (e->place == PlaceOnTop) {
+                       XRaiseWindow(e->display, e->window);
+               } else {
+                       XLowerWindow(e->display, e->window);
+               }
+       } else {
+               if (e->place == PlaceOnTop) {
+                       Client_Raise(c);
+               } else {
+                       Client_Lower(c);
+               }
+       }
+}
+
+static void
+maprequest(XEvent *ev) {
+       Client * c;
+       XMapRequestEvent * e = &ev->xmaprequest;
+
+       c = Client_Get(e->window);
+
+       if (c == 0 || c->window != e->window) {
+               int screen;
+               for (screen = 0; screen < screen_count; screen++)
+                       scanWindowTree(screen);
+               c = Client_Get(e->window);
+               if (c == 0 || c->window != e->window) {
+                       fprintf(stderr, "MapRequest for non-existent window!\n");
+                       return;
+               }
+       }
+
+       switch (c->state) {
+       case WithdrawnState:
+               if (getScreenFromRoot(c->parent) != 0) {
+                       manage(c, 0);
+                       break;
+               }
+               if (c->framed == True) {
+                       XReparentWindow(dpy, c->window, c->parent, border,
+                               border + titleHeight());
+               } else {
+                       XReparentWindow(dpy, c->window, c->parent,
+                               c->size.x, c->size.y);
+               }
+               XAddToSaveSet(dpy, c->window);
+               /*FALLTHROUGH*/
+       case NormalState:
+               XMapWindow(dpy, c->parent);
+               XMapWindow(dpy, c->window);
+               Client_Raise(c);
+               Client_SetState(c, NormalState);
+               break;
+       }
+       ewmh_set_client_list(c->screen);
+}
+
+static void
+unmap(XEvent *ev) {
+       Client *c;
+       XUnmapEvent *e = &ev->xunmap;
+
+       c = Client_Get(e->window);
+       if (c == 0) return;
+
+       /*
+        * In the description of the ReparentWindow request we read: "If the window
+        * is mapped, an UnmapWindow request is performed automatically first". This
+        * might seem stupid, but it's the way it is. While a reparenting is pending
+        * we ignore UnmapWindow requests.
+        */
+       if (c->internal_state == IPendingReparenting) {
+               c->internal_state = INormal;
+               return;
+       }
+
+       /* "This time it's the real thing." */
+
+       if (c->state == IconicState) {
+               /*
+                * Is this a hidden window disappearing? If not, then we
+                * aren't interested because it's an unmap request caused
+                * by our hiding a window.
+                */
+       } else {
+               /* This is a plain unmap, so withdraw the window. */
+               withdraw(c);
+       }
+
+       c->internal_state = INormal;
+}
+
+static void
+configurereq(XEvent *ev) {
+       XWindowChanges wc;
+       Client *c;
+       XConfigureRequestEvent *e = &ev->xconfigurerequest;
+
+       c = Client_Get(e->window);
+
+
+       if (c && c->window == e->window) {
+               /*
+               * ICCCM section 4.1.5 says that the x and y coordinates here
+               * will have been "adjusted for the border width".
+               * NOTE: this may not be the only place to bear this in mind.
+               */
+               if (e->value_mask & CWBorderWidth) {
+                       e->x -= e->border_width;
+                       e->y -= e->border_width;
+               } else {
+                       /*
+                       * The ICCCM also says that clients should always set the
+                       * border width in a configure request. As usual, many don't.
+                       */
+                       /* adding one seems a bit arbitrary and makes edit
+                               drift by one pixel*/
+                       /*e->x--;*/
+                       /*e->y--;*/
+               }
+
+               if (e->value_mask & CWX) {
+                       c->size.x = e->x;
+               }
+               if (e->value_mask & CWY) {
+                       c->size.y = e->y;
+                       if (c->framed == True)
+                               c->size.y += titleHeight();
+               }
+               if (e->value_mask & CWWidth) {
+                       c->size.width = e->width;
+                       if (c->framed == True)
+                               c->size.width += 2 * border;
+               }
+               if (e->value_mask & CWHeight) {
+                       c->size.height = e->height;
+                       if (c->framed == True)
+                               c->size.height += 2 * border;
+               }
+               if (e->value_mask & CWBorderWidth)
+                       c->border = e->border_width;
+
+               if (getScreenFromRoot(c->parent) == 0) {
+                       wc.x = c->size.x;
+                       wc.y = c->size.y;
+                       if (c->framed == True)
+                               wc.y -= titleHeight();
+                       wc.width = c->size.width;
+                       wc.height = c->size.height;
+                       if (c->framed == True)
+                               wc.height += titleHeight();
+                       wc.border_width = 1;
+                       wc.sibling = e->above;
+                       wc.stack_mode = e->detail;
+
+                       XConfigureWindow(dpy, e->parent, e->value_mask, &wc);
+                       sendConfigureNotify(c);
+               }
+       }
+       if (c && (c->internal_state == INormal) && (c->framed == True)) {
+               wc.x = border;
+               wc.y = border;
+       } else {
+               wc.x = e->x;
+               wc.y = e->y;
+       }
+
+       wc.width = e->width;
+       wc.height = e->height;
+       wc.border_width = 0;
+       wc.sibling = e->above;
+       wc.stack_mode = e->detail;
+       e->value_mask |= CWBorderWidth;
+
+       XConfigureWindow(dpy, e->window, e->value_mask, &wc);
+
+       if (c) {
+               if (c->framed == True)  {
+                       XMoveResizeWindow(dpy, c->parent,
+                               c->size.x, c->size.y - titleHeight(),
+                               c->size.width, c->size.height + titleHeight());
+                       XMoveWindow(dpy, c->window,
+                               border, border + titleHeight());
+               } else {
+                       XMoveResizeWindow(dpy, c->window,
+                               c->size.x, c->size.y,
+                               c->size.width, c->size.height);
+               }
+       }
+}
+
+static void
+destroy(XEvent *ev) {
+       Client * c;
+       Window w = ev->xdestroywindow.window;
+
+       c = Client_Get(w);
+       if (c == 0)
+               return;
+
+       ignore_badwindow = 1;
+       Client_Remove(c);
+       ignore_badwindow = 0;
+}
+
+static void
+clientmessage(XEvent *ev) {
+       Client * c;
+       XClientMessageEvent * e = &ev->xclient;
+
+       c = Client_Get(e->window);
+       if (c == 0) return;
+       if (e->message_type == wm_change_state) {
+               return;
+       }
+       if (e->message_type == ewmh_atom[_NET_WM_STATE] &&
+               e->format == 32) {
+               ewmh_change_state(c, e->data.l[0], e->data.l[1]);
+               ewmh_change_state(c, e->data.l[0], e->data.l[2]);
+               return;
+       }
+       if (e->message_type == ewmh_atom[_NET_ACTIVE_WINDOW] &&
+               e->format == 32) {
+               /* An EWMH enabled application has asked for this client
+                * to be made the active window. The window is raised, and
+                * focus given if the focus mode is click (focusing on a
+                * window other than the one the pointer is in makes no
+                * sense when the focus mode is enter).
+                */
+               XMapWindow(dpy, c->parent);
+               Client_Raise(c);
+               if (c != current && focus_mode == focus_click)
+                       Client_Focus(c, CurrentTime);
+               return;
+       }
+       if (e->message_type == ewmh_atom[_NET_CLOSE_WINDOW] &&
+               e->format == 32) {
+               Client_Close(c);
+               return;
+       }
+       if (e->message_type == ewmh_atom[_NET_MOVERESIZE_WINDOW] &&
+               e->format == 32) {
+               XEvent ev;
+
+               /* FIXME: ok, so this is a bit of a hack */
+               ev.xconfigurerequest.window = e->window;
+               ev.xconfigurerequest.x = e->data.l[1];
+               ev.xconfigurerequest.y = e->data.l[2];
+               ev.xconfigurerequest.width = e->data.l[3];
+               ev.xconfigurerequest.height = e->data.l[4];
+               ev.xconfigurerequest.value_mask = 0;
+               if (e->data.l[0] & (1 << 8))
+                       ev.xconfigurerequest.value_mask |= CWX;
+               if (e->data.l[0] & (1 << 9))
+                       ev.xconfigurerequest.value_mask |= CWY;
+               if (e->data.l[0] & (1 << 10))
+                       ev.xconfigurerequest.value_mask |= CWWidth;
+               if (e->data.l[0] & (1 << 11))
+                       ev.xconfigurerequest.value_mask |= CWHeight;
+               configurereq(&ev);
+               return;
+       }
+       if (e->message_type == ewmh_atom[_NET_WM_MOVERESIZE] &&
+               e->format == 32) {
+               Edge edge = E_LAST;
+               EWMHDirection direction = e->data.l[2];
+/*
+               int x_root = e->data.l[0];
+               int y_root = e->data.l[1];
+*/
+
+
+               /* before we can do any resizing, make the window visible */
+               XMapWindow(dpy, c->parent);
+               Client_Raise(c);
+               /* FIXME: we're ignorning x_root, y_root and button! */
+               switch (direction) {
+               case DSizeTopLeft:
+                       edge = ETopLeft;
+                       break;
+               case DSizeTop:
+                       edge = ETop;
+                       break;
+               case DSizeTopRight:
+                       edge = ETopRight;
+                       break;
+               case DSizeRight:
+                       edge = ERight;
+                       break;
+               case DSizeBottomRight:
+                       edge = EBottomRight;
+                       break;
+               case DSizeBottom:
+                       edge = EBottom;
+                       break;
+               case DSizeBottomLeft:
+                       edge = EBottomLeft;
+                       break;
+               case DSizeLeft:
+                       edge = ELeft;
+                       break;
+               case DMove:
+                       edge = ENone;
+                       break;
+               case DSizeKeyboard:
+                       /* FIXME: don't know how to deal with this */
+                       edge = E_LAST;
+                       break;
+               case DMoveKeyboard:
+#if 0
+/* need to do a release and this is too broken for that */
+                       /* don't believe i'm doing this. mouse warping
+                        * sucks!
+                        */
+                       XWarpPointer(dpy, c->screen->root, c->window,
+                               x_root, y_root,
+                               c->screen->display_width,
+                               c->screen->display_height,
+                               c->size.width / 2, c->size.height / 2);
+                       edge = ENone;
+#endif
+                       edge = E_LAST;
+                       break;
+               default:
+                       edge = E_LAST;
+                       fprintf(stderr, "%s: received _NET_WM_MOVERESIZE"
+                               " with bad direction", argv0);
+                       break;
+               }
+               switch (edge) {
+               case E_LAST:
+                       break;
+               case ENone:
+                       Client_Move(c);
+                       break;
+               default:
+                       Client_ReshapeEdge(c, edge);
+                       break;
+               }
+       }
+}
+
+static void
+colormap(XEvent *ev) {
+       Client * c;
+       XColormapEvent * e = &ev->xcolormap;
+
+       if (e->new) {
+               c = Client_Get(e->window);
+               if (c) {
+                       c->cmap = e->colormap;
+                       if (c == current)
+                               cmapfocus(c);
+               } else {
+                       Client_ColourMap(ev);
+               }
+       }
+}
+
+static void
+property(XEvent * ev) {
+       Client * c;
+       XPropertyEvent * e = &ev->xproperty;
+
+       c = Client_Get(e->window);
+       if (c == 0)
+               return;
+
+       if (e->atom == _mozilla_url || e->atom == XA_WM_NAME) {
+               getWindowName(c);
+               setactive(c, c == current, 0L);
+       } else if (e->atom == XA_WM_TRANSIENT_FOR) {
+               getTransientFor(c);
+       } else if (e->atom == XA_WM_NORMAL_HINTS) {
+               getNormalHints(c);
+       } else if (e->atom == wm_colormaps) {
+               getColourmaps(c);
+               if (c == current)
+                       cmapfocus(c);
+       } else if (e->atom == ewmh_atom[_NET_WM_STRUT]) {
+               ewmh_get_strut(c);
+       } else if (e->atom == ewmh_atom[_NET_WM_STATE]) {
+               // Received notice that client wants to change its state
+               //  update internal wstate tracking
+               Bool wasFullscreen = c->wstate.fullscreen;
+               ewmh_get_state(c);
+               // make any changes requested
+               if (c->wstate.fullscreen == True && wasFullscreen == False) Client_EnterFullScreen(c);
+               else if (c->wstate.fullscreen == False && wasFullscreen == True) Client_ExitFullScreen(c);
+       }
+}
+
+static void
+reparent(XEvent *ev) {
+       Client * c;
+       XReparentEvent * e = &ev->xreparent;
+
+       if (getScreenFromRoot(e->event) == 0 || e->override_redirect || getScreenFromRoot(e->parent) != 0)
+               return;
+
+       c = Client_Get(e->window);
+       if (c != 0 && (getScreenFromRoot(c->parent) != 0 || withdrawn(c)))
+               Client_Remove(c);
+}
+
+static void
+focuschange(XEvent *ev) {
+       Client *c;
+       Window focus_window;
+       int revert_to;
+
+       if (ev->type == FocusOut) return;
+
+       XGetInputFocus(dpy, &focus_window, &revert_to);
+       if (focus_window == PointerRoot || focus_window == None) {
+               if (current) Client_Focus(NULL, CurrentTime);
+               return;
+       }
+       c = Client_Get(focus_window);
+       if (c && c != current) {
+               Client_Focus(c, CurrentTime);
+       }
+       return;
+}
+
+static void
+enter(XEvent *ev) {
+       Client *c;
+
+       c = Client_Get(ev->xcrossing.window);
+       if (c == 0 || mode != wm_idle)
+               return;
+
+       if (c->framed == True) {
+               XSetWindowAttributes attr;
+
+               attr.cursor = c->screen->root_cursor;
+               XChangeWindowAttributes(dpy, c->parent,
+                       CWCursor, &attr);
+               c->cursor = ENone;
+       }
+       if (c != current && !c->hidden && focus_mode == focus_enter) {
+               /* Entering a new window in enter focus mode, so take focus */
+               Client_Focus(c, ev->xcrossing.time);
+       }
+}
+
+static void
+motionnotify(XEvent *ev) {
+       if (mode == wm_reshaping)
+               reshaping_motionnotify(ev);
+       else if (mode == wm_idle) {
+               XMotionEvent *e = &ev->xmotion;
+               Client *c = Client_Get(e->window);
+               Edge edge = ENone;
+               int quarter = (border + titleHeight()) / 4;
+
+               if (c && (e->window == c->parent) &&
+                       (e->subwindow != c->window) &&
+                       mode == wm_idle) {
+                       /* mouse moved in a frame we manage - check cursor */
+                       if (e->x > (quarter + 2)
+                               && e->x < (3 + 3*quarter)
+                               && e->y > quarter && e->y <= 3*quarter) {
+                               edge = E_LAST;
+                       } else if (e->x <= border && e->y <= border) {
+                               edge = ETopLeft;
+                       } else if (e->x >= (c->size.width - border)
+                               && e->y <= border) {
+                               edge = ETopRight;
+                       } else if (e->x >= (c->size.width - border)
+                               && e->y >=
+                               (c->size.height + titleHeight() - border)) {
+                               edge = EBottomRight;
+                       } else if (e->x <= border &&
+                               e->y >=
+                               (c->size.height + titleHeight() - border)) {
+                               edge = EBottomLeft;
+                       } else if (e->x > border &&
+                               e->x < (c->size.width - border)
+                               && e->y < border) {
+                               edge = ETop;
+                       } else if (e->x > border &&
+                               e->x < (c->size.width - border)
+                               && e->y >= border
+                               && e->y < (titleHeight() + border)) {
+                               edge = ENone;
+                       } else if (e->x > (c->size.width - border)
+                               && e->y > border
+                               && e->y <
+                               (c->size.height + titleHeight() - border)) {
+                               edge = ERight;
+                       } else if (e->x > border
+                               && e->x < (c->size.width - border)
+                               && e->y > (c->size.height - border)) {
+                               edge = EBottom;
+                       } else if (e->x < border
+                               && e->y > border
+                               && e->y <
+                               (c->size.height + titleHeight() - border)) {
+                               edge = ELeft;
+                       }
+                       if (c->cursor != edge) {
+                               XSetWindowAttributes attr;
+
+                               if (edge == ENone) {
+                                       attr.cursor =
+                                               c->screen->root_cursor;
+                               } else if (edge == E_LAST) {
+                                       attr.cursor =
+                                               c->screen->box_cursor;
+                               } else {
+                                       attr.cursor =
+                                               c->screen->cursor_map[edge];
+                               }
+                               XChangeWindowAttributes(dpy, c->parent,
+                                       CWCursor, &attr);
+                               c->cursor = edge;
+                       }
+               }
+       }
+}
+
+/*ARGSUSED*/
+void
+reshaping_motionnotify(XEvent* ev) {
+       int     nx;     /* New x. */
+       int     ny;     /* New y. */
+       int     ox;     /* Original x. */
+       int     oy;     /* Original y. */
+       int     ndx;    /* New width. */
+       int     ndy;    /* New height. */
+       int     odx;    /* Original width. */
+       int     ody;    /* Original height. */
+       int     pointer_x;
+       int     pointer_y;
+
+       if (mode != wm_reshaping || !current) return;
+
+       getMousePosition(&pointer_x, &pointer_y);
+
+       if (interacting_edge != ENone) {
+               nx = ox = current->size.x;
+               ny = oy = current->size.y;
+               ndx = odx = current->size.width;
+               ndy = ody = current->size.height;
+
+               /* Vertical. */
+               switch (interacting_edge) {
+               case ETop:
+               case ETopLeft:
+               case ETopRight:
+                       pointer_y += titleHeight();
+                       ndy += (current->size.y - pointer_y);
+                       ny = pointer_y;
+                       break;
+               case EBottom:
+               case EBottomLeft:
+               case EBottomRight:
+                       ndy = pointer_y - current->size.y;
+                       break;
+               default:        break;
+               }
+
+               /* Horizontal. */
+               switch (interacting_edge) {
+               case ERight:
+               case ETopRight:
+               case EBottomRight:
+                       ndx = pointer_x - current->size.x;
+                       break;
+               case ELeft:
+               case ETopLeft:
+               case EBottomLeft:
+                       ndx += (current->size.x - pointer_x);
+                       nx = pointer_x;
+                       break;
+               default: break;
+               }
+
+               Client_MakeSane(current, interacting_edge, &nx, &ny, &ndx, &ndy);
+               XMoveResizeWindow(dpy, current->parent,
+                       current->size.x, current->size.y - titleHeight(),
+                       current->size.width, current->size.height + titleHeight());
+               if (current->size.width == odx && current->size.height == ody) {
+                       if (current->size.x != ox || current->size.y != oy)
+                               sendConfigureNotify(current);
+               } else
+                       XMoveResizeWindow(dpy, current->window,
+                               border, border + titleHeight(),
+                               current->size.width - 2 * border,
+                               current->size.height - 2 * border);
+       } else {
+               nx = pointer_x + start_x;
+               ny = pointer_y + start_y;
+
+               Client_MakeSane(current, interacting_edge, &nx, &ny, 0, 0);
+               if (current->framed == True) {
+                       XMoveWindow(dpy, current->parent,
+                               current->size.x,
+                               current->size.y - titleHeight());
+               } else {
+                       XMoveWindow(dpy, current->parent,
+                               current->size.x, current->size.y);
+               }
+               sendConfigureNotify(current);
+       }
+}
diff --git a/error.c b/error.c
new file mode 100644 (file)
index 0000000..9c7e4cd
--- /dev/null
+++ b/error.c
@@ -0,0 +1,64 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xproto.h>
+
+#include       "lwm.h"
+
+int ignore_badwindow;
+
+void
+panic(char *s) {
+       fprintf(stderr, "%s: %s\n", argv0, s);
+       exit(EXIT_FAILURE);
+}
+
+int
+errorHandler(Display *d, XErrorEvent *e) {
+       char    msg[80];
+       char    req[80];
+       char    number[80];
+
+       if (mode == wm_initialising &&
+                e->request_code == X_ChangeWindowAttributes &&
+                e->error_code == BadAccess)
+                       panic("another window manager is already running.");
+
+       if (ignore_badwindow &&
+           (e->error_code == BadWindow || e->error_code == BadColor))
+                       return 0;
+
+       XGetErrorText(d, e->error_code, msg, sizeof(msg));
+       sprintf(number, "%d", e->request_code);
+       XGetErrorDatabaseText(d, "XRequest", number, number, req, sizeof(req));
+
+       fprintf(stderr, "%s: protocol request %s on resource %#x failed: %s\n",
+               argv0, req, (unsigned int) e->resourceid, msg);
+
+       if (mode == wm_initialising)
+               panic("can't initialise.");
+
+       return 0;
+}
diff --git a/ewmh.c b/ewmh.c
new file mode 100644 (file)
index 0000000..e6bb1b3
--- /dev/null
+++ b/ewmh.c
@@ -0,0 +1,643 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lwm.h"
+#include "ewmh.h"
+
+Atom ewmh_atom[EWMH_ATOM_LAST];
+Atom utf8_string;
+
+void
+ewmh_init(void) {
+       /* build half a million EWMH atoms */
+       ewmh_atom[_NET_SUPPORTED] =
+               XInternAtom(dpy, "_NET_SUPPORTED", False);
+       ewmh_atom[_NET_CLIENT_LIST] =
+               XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+       ewmh_atom[_NET_CLIENT_LIST_STACKING] =
+               XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False);
+       ewmh_atom[_NET_NUMBER_OF_DESKTOPS] =
+               XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False);
+       ewmh_atom[_NET_DESKTOP_GEOMETRY] =
+               XInternAtom(dpy, "_NET_DESKTOP_GEOMETRY", False);
+       ewmh_atom[_NET_DESKTOP_VIEWPORT] =
+               XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False);
+       ewmh_atom[_NET_CURRENT_DESKTOP] =
+               XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False);
+       ewmh_atom[_NET_ACTIVE_WINDOW] =
+               XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+       ewmh_atom[_NET_WORKAREA] =
+               XInternAtom(dpy, "_NET_WORKAREA", False);
+       ewmh_atom[_NET_SUPPORTING_WM_CHECK] =
+               XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+       ewmh_atom[_NET_CLOSE_WINDOW] =
+               XInternAtom(dpy, "_NET_CLOSE_WINDOW", False);
+       ewmh_atom[_NET_MOVERESIZE_WINDOW] =
+               XInternAtom(dpy, "_NET_MOVERESIZE_WINDOW", False);
+       ewmh_atom[_NET_WM_MOVERESIZE] =
+               XInternAtom(dpy, "_NET_WM_MOVERESIZE", False);
+       ewmh_atom[_NET_WM_NAME] =
+               XInternAtom(dpy, "_NET_WM_NAME", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+       ewmh_atom[_NET_WM_STATE] =
+               XInternAtom(dpy, "_NET_WM_STATE", False);
+       ewmh_atom[_NET_WM_ALLOWED_ACTIONS] =
+               XInternAtom(dpy, "_NET_WM_ALLOWED_ACTIONS", False);
+       ewmh_atom[_NET_WM_STRUT] =
+               XInternAtom(dpy, "_NET_WM_STRUT", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_DESKTOP] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_DOCK] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_TOOLBAR] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_MENU] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_UTILITY] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_SPLASH] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_DIALOG] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+       ewmh_atom[_NET_WM_WINDOW_TYPE_NORMAL] =
+               XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+       ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR] =
+               XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False);
+       ewmh_atom[_NET_WM_STATE_SKIP_PAGER] =
+               XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False);
+       ewmh_atom[_NET_WM_STATE_HIDDEN] =
+               XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
+       ewmh_atom[_NET_WM_STATE_FULLSCREEN] =
+               XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+       ewmh_atom[_NET_WM_ACTION_MOVE] =
+               XInternAtom(dpy, "_NET_WM_ACTION_MOVE", False);
+       ewmh_atom[_NET_WM_ACTION_RESIZE] =
+               XInternAtom(dpy, "_NET_WM_ACTION_RESIZE", False);
+       ewmh_atom[_NET_WM_ACTION_FULLSCREEN] =
+               XInternAtom(dpy, "_NET_WM_ACTION_FULLSCREEN", False);
+       ewmh_atom[_NET_WM_ACTION_CLOSE] =
+               XInternAtom(dpy, "_NET_WM_ACTION_CLOSE", False);
+       utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
+}
+
+void
+ewmh_init_screens(void) {
+       int i;
+       unsigned long data[4];
+
+       /* announce EWMH compatibility on all acreens */
+       for (i = 0; i < screen_count; i++) {
+               screens[i].ewmh_set_client_list = False;
+               screens[i].ewmh_compat = XCreateSimpleWindow(dpy,
+                       screens[i].root,
+                       -200, -200, 1, 1, 
+                       0, 0, 0);
+               XChangeProperty(dpy, screens[i].ewmh_compat,
+                       ewmh_atom[_NET_WM_NAME],
+                       utf8_string, 8, PropModeReplace,
+                       "lwm", 3);
+
+               /* set root window properties */
+               XChangeProperty(dpy, screens[i].root,
+                       ewmh_atom[_NET_SUPPORTED],
+                       XA_ATOM, 32, PropModeReplace,
+                       (unsigned char *)ewmh_atom, EWMH_ATOM_LAST);
+
+               XChangeProperty(dpy, screens[i].root,
+                       ewmh_atom[_NET_SUPPORTING_WM_CHECK],
+                       XA_WINDOW, 32, PropModeReplace,
+                       (unsigned char *)&screens[i].ewmh_compat, 1);
+
+               data[0] = 1;
+               XChangeProperty(dpy, screens[i].root,
+                       ewmh_atom[_NET_NUMBER_OF_DESKTOPS],
+                       XA_CARDINAL, 32, PropModeReplace,
+                       (unsigned char*)data, 1);
+
+               data[0] = screens[i].display_width;
+               data[1] = screens[i].display_height;
+               XChangeProperty(dpy, screens[i].root,
+                       ewmh_atom[_NET_DESKTOP_GEOMETRY],
+                       XA_CARDINAL, 32, PropModeReplace,
+                       (unsigned char*)data, 2);
+
+               data[0] = 0;
+               data[1] = 0;
+               XChangeProperty(dpy, screens[i].root,
+                       ewmh_atom[_NET_DESKTOP_VIEWPORT],
+                       XA_CARDINAL, 32, PropModeReplace,
+                       (unsigned char*)data, 2);
+
+               data[0] = 0;
+               XChangeProperty(dpy, screens[i].root,
+                       ewmh_atom[_NET_CURRENT_DESKTOP],
+                       XA_CARDINAL, 32, PropModeReplace,
+                       (unsigned char*)data, 1);
+
+               ewmh_set_strut(&screens[i]);
+               ewmh_set_client_list(&screens[i]);
+       }
+}
+
+EWMHWindowType
+ewmh_get_window_type(Window w) {
+       Atom rt;
+       Atom *type;
+       int fmt;
+       unsigned long n;
+       unsigned long extra;
+       int i;
+       EWMHWindowType ret;
+
+       i = XGetWindowProperty(dpy, w,
+               ewmh_atom[_NET_WM_WINDOW_TYPE],
+               0, 100, False, XA_ATOM, &rt, &fmt, &n, &extra,
+               (unsigned char **)&type);
+       if (i != Success || type == NULL)
+               return WTypeNone;
+       ret = WTypeNone;
+       for (; n; n--) {
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_DESKTOP]) {
+                       ret = WTypeDesktop;
+                       break;
+               }
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_DOCK]) {
+                       ret = WTypeDock;
+                       break;
+               }
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_TOOLBAR]) {
+                       ret = WTypeToolbar;
+                       break;
+               }
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_MENU]) {
+                       ret = WTypeMenu;
+                       break;
+               }
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_UTILITY]) {
+                       ret = WTypeUtility;
+                       break;
+               }
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_SPLASH]) {
+                       ret = WTypeSplash;
+                       break;
+               }
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_DIALOG]) {
+                       ret = WTypeDialog;
+                       break;
+               }
+               if (type[n - 1] ==
+                       ewmh_atom[_NET_WM_WINDOW_TYPE_NORMAL]) {
+                       ret = WTypeNormal;
+                       break;
+               }
+       }
+       XFree(type);
+       return ret;
+}
+
+
+Bool ewmh_get_window_name(Client *c) {
+#ifdef X_HAVE_UTF8_STRING
+       Atom rt;
+       char *name;
+       int fmt;
+       unsigned long n;
+       unsigned long extra;
+       int i;
+
+       i = XGetWindowProperty(dpy, c->window,
+               ewmh_atom[_NET_WM_NAME],
+               0, 100, False, utf8_string, &rt, &fmt, &n, &extra,
+               (unsigned char **)&name);
+       if (i != Success || name == NULL)
+               return False;
+       Client_Name(c, name, True);
+       XFree(name);
+       return True;
+#else
+       return False;
+#endif
+}
+
+Bool
+ewmh_hasframe(Client *c) {
+       switch (c->wtype) {
+       case WTypeDesktop:
+       case WTypeDock:
+       case WTypeMenu:
+       case WTypeSplash:
+               return False;
+       default:
+               return True;
+       }
+}
+
+void
+ewmh_get_state(Client *c) {
+       Atom rt;
+       Atom *state;
+       int fmt;
+       unsigned long n;
+       unsigned long extra;
+       int i;
+
+       if (c == NULL) return;
+       i = XGetWindowProperty(dpy, c->window,
+               ewmh_atom[_NET_WM_STATE],
+               0, 100, False, XA_ATOM, &rt, &fmt, &n, &extra,
+               (unsigned char **)&state);
+       if (i != Success || state == NULL) return;
+       c->wstate.skip_taskbar = False;
+       c->wstate.skip_pager = False;
+       c->wstate.fullscreen = False;
+       c->wstate.above = False;
+       c->wstate.below = False;
+       for (; n; n--) {
+               if (state[n - 1] ==
+                       ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR])
+                       c->wstate.skip_taskbar = True;
+               if (state[n - 1] ==
+                       ewmh_atom[_NET_WM_STATE_SKIP_PAGER])
+                       c->wstate.skip_pager = True;
+               if (state[n - 1] ==
+                       ewmh_atom[_NET_WM_STATE_FULLSCREEN])
+                       c->wstate.fullscreen = True;
+               if (state[n - 1] ==
+                       ewmh_atom[_NET_WM_STATE_ABOVE])
+                       c->wstate.above = True;
+               if (state[n - 1] ==
+                       ewmh_atom[_NET_WM_STATE_BELOW])
+                       c->wstate.below = True;
+       }
+       XFree(state);
+}
+
+static Bool
+new_state(unsigned long action, Bool current)
+{
+       enum Action {remove, add, toggle};
+
+       switch (action) {
+       case remove:
+               return False;
+       case add:
+               return True;
+       case toggle:
+               if (current == True) return False; else return True;
+       }
+       fprintf(stderr,"%s: bad action in _NET_WM_STATE (%d)\n",
+               argv0, (int) action);
+       return current;
+}
+
+void
+ewmh_change_state(Client *c, unsigned long action,
+       unsigned long atom) {
+       Atom *a = (Atom *)&atom;
+       
+       if (atom == 0) return;
+       if (*a == ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR])
+               c->wstate.skip_taskbar =
+                       new_state(action, c->wstate.skip_taskbar);
+       if (*a == ewmh_atom[_NET_WM_STATE_SKIP_PAGER])
+               c->wstate.skip_pager =
+                       new_state(action, c->wstate.skip_pager);
+       if (*a == ewmh_atom[_NET_WM_STATE_FULLSCREEN]) {
+               Bool was_fullscreen = c->wstate.fullscreen;
+
+               c->wstate.fullscreen =
+                       new_state(action, c->wstate.fullscreen);
+               if (was_fullscreen == False &&
+                       c->wstate.fullscreen == True) Client_EnterFullScreen(c);
+               if (was_fullscreen == True &&
+                       c->wstate.fullscreen == False) Client_ExitFullScreen(c);
+       }
+       if (*a == ewmh_atom[_NET_WM_STATE_ABOVE])
+               c->wstate.above =
+                       new_state(action, c->wstate.above);
+       if (*a == ewmh_atom[_NET_WM_STATE_BELOW])
+               c->wstate.below =
+                       new_state(action, c->wstate.below);
+       ewmh_set_state(c);
+
+       /* may have to shuffle windows in the stack after a change of state */
+       ewmh_set_client_list(c->screen);
+}
+
+void
+ewmh_set_state(Client *c) {
+       int atoms = 0;
+       Atom *a = NULL;
+       int i = 0;
+
+       if (c == NULL) return;
+
+       if (c->state != WithdrawnState) {
+               if (c->hidden == True) atoms++;
+               if (c->wstate.skip_taskbar == True) atoms++;
+               if (c->wstate.skip_pager == True) atoms++;
+               if (c->wstate.fullscreen == True) atoms++;
+               if (c->wstate.above == True) atoms++;
+               if (c->wstate.below == True) atoms++;
+               if (atoms > 0) a = malloc(sizeof(Atom) * atoms);
+
+               if (c->hidden == True) {
+                       a[i] = ewmh_atom[_NET_WM_STATE_HIDDEN];
+                       i++;
+               }
+               if (c->wstate.skip_taskbar == True) {
+                       a[i] = ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR];
+                       i++;
+               }
+               if (c->wstate.skip_pager == True) {
+                       a[i] = ewmh_atom[_NET_WM_STATE_SKIP_PAGER];
+                       i++;
+               }
+               if (c->wstate.fullscreen == True) {
+                       a[i] = ewmh_atom[_NET_WM_STATE_FULLSCREEN];
+                       i++;
+               }
+               if (c->wstate.above == True) {
+                       a[i] = ewmh_atom[_NET_WM_STATE_ABOVE];
+                       i++;
+               }
+               if (c->wstate.below == True) {
+                       a[i] = ewmh_atom[_NET_WM_STATE_BELOW];
+                       i++;
+               }
+       }
+
+       XChangeProperty(dpy, c->window, ewmh_atom[_NET_WM_STATE],
+               XA_ATOM, 32, PropModeReplace, (unsigned char *)a, atoms);
+       if (a != NULL) free(a);
+       
+}
+
+void
+ewmh_set_allowed(Client *c)
+{
+/* FIXME: this is dumb - the allowed actions should be calculuated
+ * but for now, anything goes.
+ */
+       Atom action[4];
+
+       action[0] = ewmh_atom[_NET_WM_ACTION_MOVE];
+       action[1] = ewmh_atom[_NET_WM_ACTION_RESIZE];
+       action[2] = ewmh_atom[_NET_WM_ACTION_FULLSCREEN];
+       action[3] = ewmh_atom[_NET_WM_ACTION_CLOSE];
+       XChangeProperty(dpy, c->window, ewmh_atom[_NET_WM_ALLOWED_ACTIONS],
+               XA_ATOM, 32, PropModeReplace, (unsigned char *)action, 4);
+}
+
+void
+ewmh_set_strut(ScreenInfo *screen) {
+       Client *c;
+       EWMHStrut strut;
+       unsigned long data[4];
+       /* FIXME: add parameter to MakeSane rather than this hack */
+       Edge backup;
+
+       /* find largest reserved areas */
+       strut.left = 0;
+       strut.right = 0;
+       strut.top = 0;
+       strut.bottom = 0;
+       for (c = client_head(); c; c = c->next) {
+               if (c->screen != screen) continue;
+               if (c->strut.left > strut.left) strut.left = c->strut.left;
+               if (c->strut.right > strut.right) strut.right = c->strut.right;
+               if (c->strut.top > strut.top) strut.top = c->strut.top;
+               if (c->strut.bottom > strut.bottom)
+                       strut.bottom = c->strut.bottom;
+       }
+
+       /* if the reservered aread have not changed then we're done */
+       if ( screen->strut.left == strut.left &&
+               screen->strut.right == strut.right &&
+               screen->strut.top == strut.top &&
+               screen->strut.bottom == strut.bottom) return;
+
+       /* apply the new strut */
+       screen->strut.left = strut.left;
+       screen->strut.right = strut.right;
+       screen->strut.top = strut.top;
+       screen->strut.bottom = strut.bottom;
+
+       /* set the new workarea */
+       data[0] = strut.left;
+       data[1] = strut.top;
+       data[2] = screen->display_width - (strut.left + strut.right);
+       data[3] = screen->display_height - (strut.top + strut.bottom);
+       XChangeProperty(dpy, screen->root,
+               ewmh_atom[_NET_WORKAREA],
+               XA_CARDINAL, 32, PropModeReplace,
+               (unsigned char*)data, 4);
+
+       /* ensure no window fully occupy reserved areas */
+       for (c = client_head(); c; c = c->next) {
+               int x = c->size.x;
+               int y = c->size.y;
+
+               if (c->wstate.fullscreen == True) continue;
+               backup = interacting_edge;
+               interacting_edge = ENone;
+               Client_MakeSane(c, ENone, &x, &y, 0, 0);
+               interacting_edge = backup;
+               if (c->framed == True) {
+                       XMoveWindow(dpy, c->parent,
+                               c->size.x,
+                               c->size.y - titleHeight());
+               } else {
+                       XMoveWindow(dpy, c->parent,
+                               c->size.x, c->size.y);
+               }
+               sendConfigureNotify(c);
+       }
+       
+}
+
+/*
+ * get _NET_WM_STRUT and if it is available recalulate the screens
+ * reserved areas. the EWMH spec isn't clear about what we should do
+ * about hidden windows. It seems silly to reserve space for an invisible
+ * window, but the spec allows it. Ho Hum...           jfc
+ */
+void
+ewmh_get_strut(Client *c) {
+       Atom rt;
+       unsigned long *strut;
+       int fmt;
+       unsigned long n;
+       unsigned long extra;
+       int i;
+
+       if (c == NULL) return;
+       i = XGetWindowProperty(dpy, c->window,
+               ewmh_atom[_NET_WM_STRUT],
+               0, 5, False, XA_CARDINAL, &rt, &fmt, &n, &extra,
+               (unsigned char **)&strut);
+       if (i != Success || strut == NULL || n < 4) return;
+       c->strut.left = (unsigned int) strut[0];
+       c->strut.right = (unsigned int) strut[1];
+       c->strut.top = (unsigned int) strut[2];
+       c->strut.bottom = (unsigned int) strut[3];
+       ewmh_set_strut(c->screen);
+}
+
+/* fix stack forces each window on the screen to be in the right place in
+ * the window stack as indicated in the EWMH spec version 1.2 (section 7.10).
+ */
+static void
+fix_stack(ScreenInfo *screen) {
+       Client *c;
+
+       /* this is pretty dumb. we should query the tree and only move
+        * those windows that require it. doing it regardless liek this
+        * causes the desktop to flicker
+        */
+
+       /* first lower clients with _NET_WM_STATE_BELOW */
+       for (c = client_head(); c; c = c->next) {
+               if (c->wstate.below == False) continue;
+               Client_Lower(c);
+       }
+
+       /* lower desktops - they are always the lowest */
+       for (c = client_head(); c; c = c->next) {
+               if (c->wtype != WTypeDesktop) continue;
+               Client_Lower(c);
+               break; /* only one desktop, surely */
+       }
+
+       /* raise clients with _NET_WM_STATE_ABOVE and docks
+        * (unless marked with _NET_WM_STATE_BELOW)
+        */
+       for (c = client_head(); c; c = c->next) {
+               if (!(c->wstate.above == True ||
+                       (c->wtype == WTypeDock &&
+                       c->wstate.below == False)))
+                       continue;
+               Client_Raise(c);
+       }
+
+       /* raise fullscreens - they're always on top */
+       /* Misam Saki reports problems with this and believes fullscreens
+        * should not be automatically raised.
+        *
+        * However if the code below is removed then the panel is raised above
+        * fullscreens, which is not desirable.
+        */
+       for (c = client_head(); c; c = c->next) {
+               if (c->wstate.fullscreen == False) continue;
+               Client_Raise(c);
+       }
+}
+
+
+static Bool
+valid_for_client_list(ScreenInfo *screen, Client *c) {
+       if (c->screen != screen) return False;
+       if (c->state == WithdrawnState) return False;
+       return True;
+}
+
+/*
+* update_client_list updates the properties on the root window used by
+* task lists and pagers.
+*
+* it should be called whenever the window stack is modified, or when clients
+* are hidden or unhidden.
+*/
+void
+ewmh_set_client_list(ScreenInfo *screen) {
+       int no_clients=0;
+       Window *client_list=NULL;
+       Window *stacked_client_list=NULL;
+       Client *c;
+
+       if (screen == NULL || screen->ewmh_set_client_list == True) return;
+       screen->ewmh_set_client_list = True;
+       fix_stack(screen);
+       for (c = client_head(); c; c = c->next) {
+               if (valid_for_client_list(screen, c) == True) no_clients++;
+       }
+       if (no_clients > 0) {
+               int i;
+               Window dw1;
+               Window dw2;
+               Window *wins;
+               unsigned int win;
+               unsigned int nwins;
+
+               client_list = malloc(sizeof(Window) * no_clients);
+               i = no_clients - 1; /* array starts with oldest */
+               for (c = client_head(); c; c = c->next) {
+                       if (valid_for_client_list(screen, c) == True) {
+                               client_list[i] = c->window;
+                               i--;
+                               if (i < 0) break;
+                       }
+               }
+
+               stacked_client_list = malloc(sizeof(Window) * no_clients);
+               i = 0;
+               XQueryTree(dpy, screen->root, &dw1, &dw2, &wins, &nwins);
+               for (win = 0; win < nwins; win++) {
+                       c = Client_Get(wins[win]);
+                       if (!c) continue;
+                       if (valid_for_client_list(screen, c) == True) {
+                               stacked_client_list[i] = c->window;
+                               i++;
+                               if (i >= no_clients) break;
+                       }
+               }
+               if ( nwins > 0 ) XFree(wins);
+
+       }
+       XChangeProperty(dpy, screen->root,
+               ewmh_atom[_NET_CLIENT_LIST],
+               XA_WINDOW, 32, PropModeReplace,
+               (unsigned char*)client_list, no_clients);
+       XChangeProperty(dpy, screen->root,
+               ewmh_atom[_NET_CLIENT_LIST_STACKING],
+               XA_WINDOW, 32, PropModeReplace,
+               (unsigned char*)stacked_client_list, no_clients);
+       if (no_clients > 0 ) {
+               free(client_list);
+               free(stacked_client_list);
+       }
+       screen->ewmh_set_client_list = False;
+}
diff --git a/ewmh.h b/ewmh.h
new file mode 100644 (file)
index 0000000..a9fced1
--- /dev/null
+++ b/ewmh.h
@@ -0,0 +1,94 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/**
+ * These are indexes into the ewmh_atom array. Only atoms actually supported
+ * by lwm should be included here because _NET_SUPPORTED is built from the
+ * ewmh_atom array.
+ */
+
+typedef enum {
+/* root window properties */
+       _NET_SUPPORTED,
+       _NET_CLIENT_LIST,
+       _NET_CLIENT_LIST_STACKING,
+       _NET_NUMBER_OF_DESKTOPS,
+       _NET_DESKTOP_GEOMETRY,
+       _NET_DESKTOP_VIEWPORT,
+       _NET_CURRENT_DESKTOP,
+       /*_NET_DESKTOP_NAMES,*/
+       _NET_ACTIVE_WINDOW,
+       _NET_WORKAREA,
+       _NET_SUPPORTING_WM_CHECK,
+       /*_NET_VIRTUAL_ROOTS,*/
+       /*_NET_DESKTOP_LAYOUT,*/
+       /*_NET_SHOWING_DESKTOP,*/
+/* other root window messages */
+       _NET_CLOSE_WINDOW,
+       _NET_MOVERESIZE_WINDOW,
+       _NET_WM_MOVERESIZE,
+/* application window properties */
+       _NET_WM_NAME,
+       /*_NET_WM_VISIBLE_NAME,*/
+       /*_NET_WM_ICON_NAME,*/
+       /*_NET_WM_VISIBLE_ICON_NAME,*/
+       /*_NET_WM_DESKTOP,*/
+       _NET_WM_WINDOW_TYPE,
+       _NET_WM_STATE,
+       _NET_WM_ALLOWED_ACTIONS,
+       _NET_WM_STRUT,
+       /*_NET_WM_ICON_GEOMETRY,*/
+       /*_NET_WM_ICON,*/
+       /*_NET_WM_PID,*/
+       /*_NET_WM_HANDLED_ICONS,*/
+/* window types for _NET_WM_WINDOW_TYPE */
+       _NET_WM_WINDOW_TYPE_DESKTOP,
+       _NET_WM_WINDOW_TYPE_DOCK,
+       _NET_WM_WINDOW_TYPE_TOOLBAR,
+       _NET_WM_WINDOW_TYPE_MENU,
+       _NET_WM_WINDOW_TYPE_UTILITY,
+       _NET_WM_WINDOW_TYPE_SPLASH,
+       _NET_WM_WINDOW_TYPE_DIALOG,
+       _NET_WM_WINDOW_TYPE_NORMAL,
+/* window states for _NET_WM_STATE */
+       /*_NET_WM_STATE_MODAL,*/
+       /*_NET_WM_STATE_STICKY,*/
+       /*_NET_WM_STATE_MAXIMISED_VERT,*/
+       /*_NET_WM_STATE_MAXIMISED_HORZ,*/
+       /*_NET_WM_STATE_SHADED,*/
+       _NET_WM_STATE_SKIP_TASKBAR,
+       _NET_WM_STATE_SKIP_PAGER,
+       _NET_WM_STATE_HIDDEN,
+       _NET_WM_STATE_FULLSCREEN,
+       _NET_WM_STATE_ABOVE,
+       _NET_WM_STATE_BELOW,
+/* allowed actions for _NET_WM_ALLOWED_ACTIONS */
+       _NET_WM_ACTION_MOVE,
+       _NET_WM_ACTION_RESIZE,
+       /*_NET_WM_ACTION_MINIMIZE,*/
+       /*_NET_WM_ACTION_SHADE,*/
+       /*_NET_WM_ACTION_STICK,*/
+       /*_NET_WM_ACTION_MAXIMIZE_HORIZ,*/
+       /*_NET_WM_ACTION_MAXIMIZE_VERT,*/
+       _NET_WM_ACTION_FULLSCREEN,
+       /*_NET_WM_ACTION_CHANGE_DESKTOP,*/
+       _NET_WM_ACTION_CLOSE,
+       EWMH_ATOM_LAST
+} EWMHAtom;
+
diff --git a/lwm b/lwm
new file mode 100755 (executable)
index 0000000..12e9c03
Binary files /dev/null and b/lwm differ
diff --git a/lwm.c b/lwm.c
new file mode 100644 (file)
index 0000000..662dfcd
--- /dev/null
+++ b/lwm.c
@@ -0,0 +1,495 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include "lwm.h"
+
+Mode mode;                     /* The window manager's mode. (See "lwm.h".) */
+int start_x;                   /* The X position where the mode changed. */
+int start_y;                   /* The Y position where the mode changed. */
+
+Display * dpy;                 /* The connection to the X server. */
+int screen_count;              /* The number of screens. */
+ScreenInfo * screens;          /* Information about these screens. */
+ScreenInfo * current_screen;
+
+XFontSet font_set = NULL;      /* Font set for title var */
+XFontSetExtents *font_set_ext = NULL;
+XFontSet popup_font_set = NULL;        /* Font set for popups */
+XFontSetExtents *popup_font_set_ext = NULL;
+
+Bool shape;                    /* Does server have Shape Window extension? */
+int shape_event;               /* ShapeEvent event type. */
+
+/* Atoms we're interested in. See the ICCCM for more information. */
+Atom wm_state;
+Atom wm_change_state;
+Atom wm_protocols;
+Atom wm_delete;
+Atom wm_take_focus;
+Atom wm_colormaps;
+Atom compound_text;
+
+/** Netscape uses this to give information about the URL it's displaying. */
+Atom _mozilla_url;
+
+/*
+ * if we're really short of a clue we might look at motif hints, and
+ * we're not going to link with motif, so we'll have to do it by hand
+ */
+Atom motif_wm_hints;
+
+char *argv0;
+
+static void initScreens(void);
+static void initScreen(int);
+
+char   *font_name;             /* User's choice of titlebar font. */
+char   *popup_font_name;       /* User's choice of menu font. */
+char   *btn1_command;          /* User's choice of button 1 command. */
+char   *btn2_command;          /* User's choice of button 2 command. */
+int    border;                 /* User's choice of border size. */
+FocusMode focus_mode;          /* User's choice of focus mode (default enter) */
+
+void getMousePosition(int * x, int * y) {
+       Window root, child;
+       int t1, t2;
+       unsigned int b;
+
+       /* It doesn't matter which root window we give this call. */
+       XQueryPointer(dpy, screens[0].root, &root, &child, x, y, &t1, &t2, &b);
+       current_screen = getScreenFromRoot(root);
+}
+
+void parseResources(void) {
+       /* Set our fall-back defaults. */
+       font_name = DEFAULT_TITLE_FONT;
+       popup_font_name = DEFAULT_POPUP_FONT;
+       border = DEFAULT_BORDER;
+       btn1_command = 0;
+       btn2_command = DEFAULT_TERMINAL;
+       focus_mode = focus_enter;
+}
+
+int main(int argc, char *argv[]) {
+       XEvent ev;
+       struct sigaction sa;
+       int dpy_fd, max_fd;
+
+       argv0 = argv[0];
+
+       mode = wm_initialising;
+
+       /* Open a connection to the X server. */
+       dpy = XOpenDisplay(NULL);
+       if (dpy == 0)
+               panic("can't open display.");
+
+       parseResources();
+
+       /* Set up an error handler. */
+       XSetErrorHandler(errorHandler);
+
+       /* Set up signal handlers. */
+       signal(SIGTERM, Terminate);
+       signal(SIGINT, Terminate);
+       signal(SIGHUP, Terminate);
+
+       /* Ignore SIGCHLD. */
+       sa.sa_handler = SIG_IGN;
+#ifdef SA_NOCLDWAIT
+       sa.sa_flags = SA_NOCLDWAIT;
+#else
+       sa.sa_flags = 0;
+#endif
+       sigemptyset(&sa.sa_mask);
+       sigaction(SIGCHLD, &sa, 0);
+
+       /* Internalize useful atoms. */
+       wm_state = XInternAtom(dpy, "WM_STATE", False);
+       wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);
+       wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
+       wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+       wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+       wm_colormaps = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
+       compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False);
+
+       _mozilla_url = XInternAtom(dpy, "_MOZILLA_URL", False);
+
+       motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
+
+       ewmh_init();
+
+       /*
+        * Get fonts for our titlebar and our popup window. We try to
+        * get Lucida, but if we can't we make do with fixed because everyone
+        * has that.
+        */
+       {
+               /* FIXME: do these need to be freed? */
+               char **missing;
+               char *def;
+               int missing_count;
+
+               font_set = XCreateFontSet(dpy, font_name,
+                       &missing, &missing_count, &def);
+               if (font_set == NULL)
+                       font_set = XCreateFontSet(dpy, "fixed",
+                               &missing, &missing_count, &def);
+               if (font_set == NULL)
+                       panic("unable to create font set for title font");
+               if (missing_count > 0)
+                       fprintf(stderr,"%s: warning: missing %d charset"
+                               "%s for title font\n", argv0, missing_count,
+                               (missing_count == 1)?"":"s");
+               font_set_ext = XExtentsOfFontSet(font_set);
+
+               popup_font_set = XCreateFontSet(dpy, popup_font_name,
+                       &missing, &missing_count, &def);
+               if (popup_font_set == NULL)
+                       popup_font_set = XCreateFontSet(dpy, "fixed",
+                               &missing, &missing_count, &def);
+               if (popup_font_set == NULL)
+                       panic("unable to create font set for popup font");
+               if (missing_count > 0)
+                       fprintf(stderr,"%s: warning: missing %d charset"
+                               "%s for popup font\n", argv0, missing_count,
+                               (missing_count == 1)?"":"s");
+               popup_font_set_ext = XExtentsOfFontSet(popup_font_set);
+       }
+
+       initScreens();
+       ewmh_init_screens();
+
+       /* See if the server has the Shape Window extension. */
+       shape = serverSupportsShapes();
+
+       /*
+        * Initialisation is finished, but we start off not interacting with the
+        * user.
+        */
+       mode = wm_idle;
+
+       /*
+        * The main event loop.
+        */
+       dpy_fd = ConnectionNumber(dpy);
+       max_fd = dpy_fd + 1;
+       for (;;) {
+               fd_set readfds;
+
+               FD_ZERO(&readfds);
+               FD_SET(dpy_fd, &readfds);
+               if (select(max_fd, &readfds, NULL, NULL, NULL) > -1) {
+                   if (FD_ISSET(dpy_fd, &readfds)) {
+                           while (XPending(dpy)) {
+                               XNextEvent(dpy, &ev);
+                               dispatch(&ev);
+                           }
+                   }
+               }
+       }
+}
+
+void
+sendConfigureNotify(Client *c) {
+       XConfigureEvent ce;
+
+       ce.type = ConfigureNotify;
+       ce.event = c->window;
+       ce.window = c->window;
+       if (c->framed == True) {
+               ce.x = c->size.x + border;
+               ce.y = c->size.y + border;
+               ce.width = c->size.width - 2 * border;
+               ce.height = c->size.height - 2 * border;
+               ce.border_width = c->border;
+       } else {
+               ce.x = c->size.x;
+               ce.y = c->size.y;
+               ce.width = c->size.width;
+               ce.height = c->size.height;
+               ce.border_width = c->border;
+       }
+       ce.above = None;
+       ce.override_redirect = 0;
+       XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent *) &ce);
+}
+
+extern void
+scanWindowTree(int screen) {
+       unsigned int i;
+       unsigned int nwins;
+       Client * c;
+       Window dw1;
+       Window dw2;
+       Window * wins;
+       XWindowAttributes attr;
+
+       XQueryTree(dpy, screens[screen].root, &dw1, &dw2, &wins, &nwins);
+       for (i = 0; i < nwins; i++) {
+               XGetWindowAttributes(dpy, wins[i], &attr);
+               if (attr.override_redirect /*|| isShaped(wins[i])*/ || wins[i] == screens[screen].popup)
+                       continue;
+               c = Client_Add(wins[i], screens[screen].root);
+               if (c != 0 && c->window == wins[i]) {
+                       c->screen = &screens[screen];
+                       c->size.x = attr.x;
+                       c->size.y = attr.y;
+/* we'll leave it until it's managed
+                       if (c->framed == True) {
+                               c->size.x -= border;
+                               c->size.y -= border;
+                       }
+*/
+                       c->size.width  = attr.width;
+                       c->size.height = attr.height;
+/* we'll leave it until it's managed
+                       if (c->framed == True) {
+                               c->size.width  += 2 * border;
+                               c->size.height += 2 * border;
+                       }
+*/
+                       c->border = attr.border_width;
+                       if (attr.map_state == IsViewable) {
+                               c->internal_state = IPendingReparenting;
+                               manage(c, 1);
+                       }
+               }
+       }
+       XFree(wins);
+}
+
+/*ARGSUSED*/
+extern void
+shell(ScreenInfo * screen, int button, int x, int y) {
+       char * command = NULL;
+       char * sh;
+
+       /* Get the command we're to execute. Give up if there isn't one. */
+       if (button == Button1)
+               command = btn1_command;
+       if (button == Button2)
+               command = btn2_command;
+       if (command == NULL)
+               return;
+
+       sh = getenv("SHELL");
+       if (sh == 0)
+               sh = "/bin/sh";
+
+       switch (fork()) {
+       case 0:         /* Child. */
+               close(ConnectionNumber(dpy));
+               if (screen && screen->display_spec != 0)
+                       putenv(screen->display_spec);
+               execl(sh, sh, "-c", command, NULL);
+               fprintf(stderr, "%s: can't exec \"%s -c %s\"\n", argv0, sh,
+                       command);
+               execlp("xterm", "xterm", NULL);
+               exit(EXIT_FAILURE);
+       case -1:        /* Error. */
+               fprintf(stderr, "%s: couldn't fork\n", argv0);
+               break;
+       }
+}
+
+extern int
+titleHeight(void) {
+       return font_set_ext->max_logical_extent.height;
+}
+
+extern int
+ascent(XFontSetExtents *font_set_ext) {
+       return abs(font_set_ext->max_logical_extent.y);
+}
+
+extern int
+popupHeight(void) {
+       return popup_font_set_ext->max_logical_extent.height;
+}
+
+extern int
+titleWidth(XFontSet font_set, Client *c) {
+       XRectangle ink;
+       XRectangle logical;
+       char *name;
+       int namelen;
+
+       if (c == NULL) return 0;
+       if (c->menu_name == NULL) {
+               name = c->name;
+               namelen = c->namelen;
+       } else {
+               name = c->menu_name;
+               namelen = c->menu_namelen;
+       }
+       if (name == NULL) return 0;
+#ifdef X_HAVE_UTF8_STRING
+       if (c->name_utf8 == True)
+               Xutf8TextExtents(font_set, name, namelen,
+                       &ink, &logical);
+       else
+#endif
+               XmbTextExtents(font_set, name, namelen,
+                       &ink, &logical);
+
+       return logical.width;
+}
+
+extern int
+popupWidth(char *string, int string_length) {
+       XRectangle ink;
+       XRectangle logical;
+
+       XmbTextExtents(popup_font_set, string, string_length,
+               &ink, &logical);
+
+       return logical.width;
+}
+
+static void
+initScreens(void) {
+       int screen;
+
+       /* Find out how many screens we've got, and allocate space for their info. */
+       screen_count = ScreenCount(dpy);
+       screens = (ScreenInfo *) malloc(screen_count * sizeof(ScreenInfo));
+
+       /* Go through the screens one-by-one, initialising them. */
+       for (screen = 0; screen < screen_count; screen++) {
+               initialiseCursors(screen);
+               initScreen(screen);
+               scanWindowTree(screen);
+       }
+}
+
+static void
+initScreen(int screen) {
+       XGCValues gv;
+       XSetWindowAttributes attr;
+       XColor colour, exact;
+       int len;
+       char * display_string = DisplayString(dpy);
+       char * colon = strrchr(display_string, ':');
+       char * dot = NULL;
+
+       /* Set the DISPLAY specification. */
+       if (colon) {
+               dot = strrchr(colon, '.');
+               len = 9 + strlen(display_string) + ((dot == 0) ? 2 : 0) + 10;
+               screens[screen].display_spec = (char *) malloc(len);
+               sprintf(screens[screen].display_spec, "DISPLAY=%s", display_string);
+               if (dot == 0) dot = screens[screen].display_spec + len - 3;
+               else dot = strrchr(screens[screen].display_spec, '.');
+               sprintf(dot, ".%i", screen);
+       } else {
+               screens[screen].display_spec = 0;
+       }
+
+       /* Find the root window. */
+       screens[screen].root = RootWindow(dpy, screen);
+       screens[screen].display_width = DisplayWidth(dpy, screen);
+       screens[screen].display_height = DisplayHeight(dpy, screen);
+       screens[screen].strut.left = 0;
+       screens[screen].strut.right = 0;
+       screens[screen].strut.top = 0;
+       screens[screen].strut.bottom = 0;
+
+       /* Get the pixel values of the only two colours we use. */
+       screens[screen].black = BlackPixel(dpy, screen);
+       screens[screen].white = WhitePixel(dpy, screen);
+       XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "DimGray", &colour, &exact);
+       screens[screen].gray = colour.pixel;
+
+       /* Set up root (frame) GC's. */
+       gv.foreground = screens[screen].black ^ screens[screen].white;
+       gv.background = screens[screen].white;
+       gv.function = GXxor;
+       gv.line_width = 1;
+       gv.subwindow_mode = IncludeInferiors;
+       screens[screen].gc_thin = XCreateGC(dpy, screens[screen].root,
+               GCForeground | GCBackground | GCFunction |
+               GCLineWidth | GCSubwindowMode, &gv);
+
+       gv.line_width = 2;
+       screens[screen].gc = XCreateGC(dpy, screens[screen].root,
+               GCForeground | GCBackground | GCFunction |
+               GCLineWidth | GCSubwindowMode, &gv);
+
+       /* Create a window for our popup. */
+       screens[screen].popup = XCreateSimpleWindow(dpy, screens[screen].root,
+               0, 0, 1, 1, 1, screens[screen].black, screens[screen].white);
+       attr.event_mask = ButtonMask | ButtonMotionMask | ExposureMask;
+       XChangeWindowAttributes(dpy, screens[screen].popup, CWEventMask, &attr);
+
+       /* Create menu GC. */
+       gv.line_width = 1;
+       screens[screen].menu_gc = XCreateGC(dpy, screens[screen].popup,
+               GCForeground | GCBackground | GCFunction |
+               GCLineWidth | GCSubwindowMode, &gv);
+
+       /* Create size indicator GC. */
+       gv.foreground = screens[screen].black;
+       gv.function = GXcopy;
+       screens[screen].size_gc = XCreateGC(dpy, screens[screen].popup,
+               GCForeground | GCBackground | GCFunction |
+               GCLineWidth | GCSubwindowMode, &gv);
+
+       /* Announce our interest in the root window. */
+       attr.cursor = screens[screen].root_cursor;
+       attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
+               ColormapChangeMask | ButtonPressMask | PropertyChangeMask |
+               EnterWindowMask;
+       XChangeWindowAttributes(dpy, screens[screen].root, CWCursor |
+               CWEventMask, &attr);
+
+       /* Make sure all our communication to the server got through. */
+       XSync(dpy, False);
+}
+
+/**
+Find the screen for which root is the root window.
+*/
+ScreenInfo *
+getScreenFromRoot(Window root) {
+       int screen;
+
+       for (screen = 0; screen < screen_count; screen++)
+               if (screens[screen].root == root)
+                       return &screens[screen];
+
+       return 0;
+}
diff --git a/lwm.h b/lwm.h
new file mode 100644 (file)
index 0000000..136350b
--- /dev/null
+++ b/lwm.h
@@ -0,0 +1,347 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/* --- Administrator-configurable defaults. --- */
+
+#define DEFAULT_TITLE_FONT "-*-lucida-bold-r-normal-sans-14-*-*-*-p-*-iso10646-1"
+#define DEFAULT_POPUP_FONT "-*-lucida-medium-r-normal-sans-12-*-*-*-p-*-iso10646-1"
+#define DEFAULT_TERMINAL "xterm"
+#define DEFAULT_BORDER 6
+
+#define HIDE_BUTTON Button3
+#define MOVE_BUTTON Button2
+#define RESHAPE_BUTTON Button1
+
+#define EDGE_RESIST 32
+
+/* --- End of administrator-configurable defaults. --- */
+
+/*
+ * Window manager mode. wm is in one of five modes: it's either getting
+ * user input to move/reshape a window, getting user input to make a
+ * selection from the menu, waiting for user input to confirm a window close
+ * (by releasing a mouse button after prssing it in a window's box),
+ * waiting for user input to confirm a window hide (by releasing a mouse
+ * button after prssing it in a window's frame),
+ * or it's `idle' --- responding to events arriving
+ * from the server, but not directly interacting with the user.
+ * OK, so I lied: there's a sixth mode so that we can tell when wm's still
+ * initialising.
+ */
+typedef enum {
+       wm_initialising,
+       wm_idle,
+       wm_reshaping,
+       wm_menu_up,
+       wm_closing_window,
+       wm_hiding_window
+} Mode;
+
+/** Window internal state. Yuck. */
+typedef enum {
+       IPendingReparenting, INormal
+} IState ;
+
+
+/**
+* Focus mode, may me (and defaults to) enter where entering a window gives
+* that window input focus, or click where a window must be explicitly clicked
+* to give it the focus.
+*/
+typedef enum {
+       focus_enter,    focus_click
+} FocusMode;
+
+/**
+* Window edge, used in resizing. The `edge' ENone is used to signify a
+* window move rather than a resize. The code is sufficiently similar that
+* this isn't a special case to be treated separately.
+*/
+typedef enum {
+       ETopLeft,       ETop,           ETopRight,
+       ERight,         ENone,          ELeft,
+       EBottomLeft,    EBottom,        EBottomRight,
+       E_LAST
+} Edge ;
+
+/**
+* EWMH direction for _NET_WM_MOVERESIZE
+*/
+typedef enum {
+       DSizeTopLeft,           DSizeTop,               DSizeTopRight,
+       DSizeRight,             DSizeBottomRight,       DSizeBottom,
+       DSizeBottomLeft,        DSizeLeft,              DMove,
+       DSizeKeyboard,          DMoveKeyboard
+} EWMHDirection;
+
+/**
+* EWMH window type. See section 5.6 of the EWMH specification (1.2).
+* WTypeNone indicates that no EWMH window type as been set and MOTIF
+* hints should be used instead.
+*/
+typedef enum {
+       WTypeDesktop,   WTypeDock,      WTypeToolbar,
+       WTypeMenu,      WTypeUtility,   WTypeSplash,
+       WTypeDialog,    WTypeNormal,    WTypeNone
+} EWMHWindowType;
+
+/**
+* EWMH window state, See section 5.7 of the EWMH specification (1.2).
+* lwm does not support all states. _NET_WM_STATE_HIDDEN is taken from
+* Client.hidden.
+*/
+typedef struct {
+       Bool skip_taskbar;
+       Bool skip_pager;
+       Bool fullscreen;
+       Bool above;
+       Bool below;
+} EWMHWindowState;
+
+/**
+* EWMH "strut", or area on each edge of the screen reserved for docking
+* bars/panels.
+*/
+typedef struct {
+       unsigned int left;
+       unsigned int right;
+       unsigned int top;
+       unsigned int bottom;
+} EWMHStrut;
+
+/**
+* Screen information.
+*/
+typedef struct ScreenInfo ScreenInfo;
+struct ScreenInfo {
+       Window root;
+       Window popup;
+       Window ewmh_compat;
+
+       int display_width;              /* The width of the screen. */
+       int display_height;             /* The height of the screen. */
+       EWMHStrut strut;                /* reserved areas */
+
+       GC gc;          /* The default GC. */
+       GC gc_thin;     /* The default GC but with thinner lines. */
+       GC menu_gc;     /* The GC for the popup window (menu). */
+       GC size_gc;     /* The GC for the popup window (sizing). */
+
+       unsigned long black;            /* Black pixel value. */
+       unsigned long white;    /* White pixel value. */
+       unsigned long gray;             /* Gray pixel value. */
+
+       Cursor root_cursor;
+       Cursor box_cursor;
+
+       Cursor cursor_map[E_LAST];
+
+       Bool ewmh_set_client_list;      /* hack to prevent recursion */
+
+       char * display_spec;
+};
+
+typedef struct Client Client;
+struct Client {
+       Window window;          /* Client's window. */
+       Window parent;          /* Window manager frame. */
+       Window trans;           /* Window that client is a transient for. */
+
+       Bool framed;            /* True is lwm is maintaining a frame */
+
+       Client * next;          /* Next window in client list. */
+
+       int border;             /* Client's original border width. */
+
+       XSizeHints size;        /* Client's current geometry information. */
+       XSizeHints return_size; /* Client's old geometry information. */
+       int state;              /* Window state. See ICCCM and <X11/Xutil.h> */
+
+       Bool hidden;            /* True if this client is hidden. */
+       IState internal_state;
+       int proto;
+
+       int accepts_focus;      /* Does this window want keyboard events? */
+
+       char * name;            /* Name used for title in frame. */
+       int namelen;
+       char * menu_name;       /* Name used in root popup */
+       int menu_namelen;
+       Bool name_utf8;
+
+       ScreenInfo * screen;
+
+       Edge cursor;            /* indicates which cursor is being used for parent window */
+
+       EWMHWindowType wtype;
+       EWMHWindowState wstate;
+       EWMHStrut strut;                /* reserved areas */
+
+       /* Colourmap scum. */
+       Colormap cmap;
+       int ncmapwins;
+       Window * cmapwins;
+       Colormap * wmcmaps;
+};
+
+
+/*
+ *     c->proto is a bitarray of these
+ */
+enum {
+       Pdelete = 1,
+       Ptakefocus = 2
+};
+
+/*
+ * This should really have been in X.h --- if you select both ButtonPress
+ * and ButtonRelease events, the server makes an automatic grab on the
+ * pressed button for you. This is almost always exactly what you want.
+ */
+#define ButtonMask (ButtonPressMask | ButtonReleaseMask)
+
+/*     lwm.c */
+extern Mode mode;
+extern int start_x;
+extern int start_y;
+extern Display * dpy;
+extern int screen_count;
+extern ScreenInfo * screens;
+extern ScreenInfo * current_screen;
+extern XFontSet font_set;
+extern XFontSetExtents *font_set_ext;
+extern XFontSet popup_font_set;
+extern XFontSetExtents *popup_font_set_ext;
+extern Atom _mozilla_url;
+extern Atom motif_wm_hints;
+extern Atom wm_state;
+extern Atom wm_change_state;
+extern Atom wm_protocols;
+extern Atom wm_delete;
+extern Atom wm_take_focus;
+extern Atom wm_colormaps;
+extern Atom compound_text;
+extern Bool shape;
+extern int shape_event;
+extern char *argv0;
+extern void shell(ScreenInfo *, int, int, int);
+extern void sendConfigureNotify(Client *);
+extern int titleHeight(void);
+extern int titleWidth(XFontSet font_set, Client *c);
+extern int popupHeight(void);
+extern int popupWidth(char *string, int string_length);
+extern int ascent(XFontSetExtents *font_set_ext);
+extern ScreenInfo * getScreenFromRoot(Window);
+extern void scanWindowTree(int);
+
+/*     client.c */
+extern Client *client_head(void);
+extern Edge interacting_edge;
+extern Client *Client_Get(Window);
+extern Client *Client_Add(Window, Window);
+extern void Client_MakeSane(Client *, Edge, int *, int *, int *, int *);
+extern void Client_DrawBorder(Client *, int);
+extern void setactive(Client *, int, long);
+extern void size_expose(void);
+extern void Client_ReshapeEdge(Client *, Edge);
+extern void Client_Move(Client*);
+extern void Client_SetState(Client *, int);
+extern void Client_Raise(Client *);
+extern void Client_Lower(Client *);
+extern void Client_Close(Client *);
+extern void Client_Remove(Client *);
+extern void Client_FreeAll(void);
+extern void Client_ColourMap(XEvent*);
+extern void Client_EnterFullScreen(Client *c);
+extern void Client_ExitFullScreen(Client *c);
+extern void Client_Focus(Client *c, Time time);
+extern void Client_ResetAllCursors(void);
+extern void Client_Name(Client *c, const char *name, Bool is_utf8);
+extern int hidden(Client *);
+extern int withdrawn(Client *);
+extern int normal(Client *);
+extern void update_client_list(ScreenInfo *screen);
+extern Client *current;
+
+/*     cursor.c */
+extern Cursor getEdgeCursor(Edge edge);
+extern void initialiseCursors(int);
+
+/*     disp.c */
+extern void dispatch(XEvent *);
+extern void reshaping_motionnotify(XEvent *);
+
+/*     error.c */
+extern int ignore_badwindow;
+extern int errorHandler(Display *, XErrorEvent *);
+extern void panic(char*);
+
+/*     manage.c */
+extern void getWindowName(Client *);
+extern void getNormalHints(Client *);
+extern Bool motifWouldDecorate(Client *);
+extern void manage(Client *, int);
+extern void withdraw(Client *);
+extern void cmapfocus(Client *);
+extern void getColourmaps(Client *);
+extern void getTransientFor(Client *);
+extern void Terminate(int);
+
+/*     mouse.c */
+extern void getMousePosition(int *, int *);
+extern void hide(Client *);
+extern void unhidec(Client *, int);
+extern int menu_whichitem(int, int);
+extern void menuhit(XButtonEvent *);
+extern void unhide(int, int);
+extern void menu_expose(void);
+extern void menu_motionnotify(XEvent *);
+extern void menu_buttonrelease(XEvent *);
+
+/*     shape.c */
+extern int shapeEvent(XEvent *);
+extern int serverSupportsShapes(void);
+extern int isShaped(Window);
+extern void setShape(Client *);
+
+/*     resource.c */
+extern char *font_name;
+extern char *popup_font_name;
+extern char *btn1_command;
+extern char *btn2_command;
+extern int border;
+extern FocusMode focus_mode;
+extern char * sdup(char *);
+extern void parseResources(void);
+
+/*     ewmh.c */
+extern Atom ewmh_atom[];
+extern void ewmh_init(void);
+extern void ewmh_init_screens(void);
+extern EWMHWindowType ewmh_get_window_type(Window w);
+extern Bool ewmh_get_window_name(Client *c);
+extern Bool ewmh_hasframe(Client *c);
+extern void ewmh_set_state(Client *c);
+extern void ewmh_get_state(Client *c);
+extern void ewmh_change_state(Client *c, unsigned long action,
+       unsigned long atom);
+extern void ewmh_set_allowed(Client *c);
+extern void ewmh_set_client_list(ScreenInfo *screen);
+extern void ewmh_get_strut(Client *c);
+extern void ewmh_set_strut(ScreenInfo *screen);
diff --git a/lwm.man b/lwm.man
new file mode 100644 (file)
index 0000000..a527498
--- /dev/null
+++ b/lwm.man
@@ -0,0 +1,96 @@
+.\" lwm, a window manager for X11
+.\" Copyright (C) 1997-2016 Elliott Hughes, James Carter
+.\" 
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public License
+.\" as published by the Free Software Foundation; either version 2
+.\" of the License, or (at your option) any later version.
+.\" 
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\" 
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+.\" 
+.TH LWM 1
+.SH NAME
+lwm \- Lightweight Window Manager for the X Window System
+.SH SYNTAX
+\fBlwm \fP[ \fB\-s\fP \fIsession-id\fP ] 
+.SH DESCRIPTION
+\fILwm\fP is a window manager for the X Window System. It provides enough
+features to allow the user to manage their windows, and no more.
+.PP
+Windows are surrounded by a frame with a 
+titlebar at the top next to a small box. The frame is a grey colour for
+all windows except that which has the input focus, where it is black.
+.PP
+In the default configuration, \fIlwm\fP uses the enter-to-focus scheme, where
+moving the pointer into a window gives that window the input focus.
+\fILwm\fP may also be configured to use the click-to-focus scheme, where a
+window must be clicked on (with any button) to receive the input focus. Clicking
+on a window in this mode causes the window to be raised. Note that a click
+used to focus a window is always swallowed by \fIlwm\fP, so clicking a
+button in a new window requires two clicks.
+.PP
+A button 1 click on a window frame brings that window to the top. Dragging
+button 1 on the frame of a resizable window repositions that edge of
+the window. If a corner rather than an edge is dragged, then both edges
+forming the corner are repositioned. While you're reshaping a window,
+a little window pops up to show you the window's current size.
+.PP
+In the default configuration, button 1 on the root window does nothing.
+.PP
+Button 2 is used to drag a window by its frame, repositioning the window
+but maintaining its position in the window stack.
+.PP
+In the default configuration, button 2 on the root window brings up a
+new shell.
+.PP
+A button 3 click on a window frame hides that window.  Pressing
+button 3 on the root window brings up a menu of all the hidden windows.
+Releasing the button while over an item will unhide the named window.
+.PP
+A button 3 click in the frame while Shift is held down pushes the window
+to the back, under any other windows. (Users with 4-button mice are
+encouraged to use their fourth button for this function.)
+.PP
+A click with any button inside the little white box in a window's frame
+can be used to close the window.
+.SH OPTIONS
+\fILwm\fP accepts the following command line options:
+.PP
+.TP 8
+.B \-s
+specifies a client ID for the X Session Management system, and is used
+exclusively by session managers.
+.SH RESOURCES
+\fILwm\fP understands the following X resources:
+.TP 12
+.B titlefont
+font used in window titles
+.TP 12
+.B popupFont
+font used in popup window (menu/size indicator)
+.TP 12
+.B border
+width in pixels of window borders
+.TP 12
+.B button1
+program spawned when button 1 is clicked on the root window
+.TP 12
+.B button2
+program spawned when button 2 is clicked on the root window
+.TP 12
+.B focus
+focus mode, one of "enter" for enter-to-focus (or sloppy focus), or
+"click" for click-to-focus
+.SH "SEE ALSO"
+.PP
+X(7)
+.SH AUTHORS
+Elliott Hughes <ehughes@bluearc.com>,
+James Carter <james@jfc.org.uk>
diff --git a/manage.c b/manage.c
new file mode 100644 (file)
index 0000000..9f8e4f9
--- /dev/null
+++ b/manage.c
@@ -0,0 +1,609 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+/* These are Motif definitions from Xm/MwmUtil.h, but Motif isn't available
+   everywhere. */
+#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)
+#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)
+
+#include "lwm.h"
+
+static int getProperty(Window, Atom, Atom, long, unsigned char **);
+static int getWindowState(Window, int *);
+static void applyGravity(Client *);
+
+/*ARGSUSED*/
+void
+manage(Client * c, int mapped)
+{
+       int state;
+       XWMHints * hints;
+       XWindowAttributes current_attr;
+       XSetWindowAttributes attr;
+
+       /* For WM_PROTOCOLS handling. */
+       Atom * protocols;
+       int n;
+       int p;
+
+       /* Where auto-placement is going to put the next window. */
+       static int      auto_x = 100;
+       static int      auto_y = 100;
+
+       /* get the EWMH window type, as this might overrule some hints */
+       c->wtype = ewmh_get_window_type(c->window);
+       /* get in the initial EWMH state */
+       ewmh_get_state(c);
+       /* set EWMH allowable actions, now we intend to manage this window */
+       ewmh_set_allowed(c);
+       /* is this window to have a frame? */
+       if (c->wtype == WTypeNone) {
+               /* this breaks the ewmh spec (section 5.6) because in the
+                * absence of a _NET_WM_WINDOW_TYPE, _WM_WINDOW_TYPE_NORMAL
+                * must be taken. bummer.
+                */
+               c->framed = motifWouldDecorate(c);
+       } else {
+               c->framed = ewmh_hasframe(c);
+       }
+       if (isShaped(c->window)) c->framed = False;
+
+       /* get the EWMH strut - if there is one */
+       ewmh_get_strut(c);
+
+       /*
+        * Get the hints, window name, and normal hints (see ICCCM
+        * section 4.1.2.3).
+        */
+       hints = XGetWMHints(dpy, c->window);
+
+       getWindowName(c);
+       getNormalHints(c);
+
+       /*
+        * Get the colourmaps associated with this window. Get the window
+        * attribute colourmap first, then look to see if the
+        * WM_COLORMAP_WINDOWS property has been used to specify
+        * windows needing colourmaps that differ from the top-level
+        * colourmap. (See ICCCM section 4.1.8.)
+        */
+       XGetWindowAttributes(dpy, c->window, &current_attr);
+       c->cmap = current_attr.colormap;
+
+       getColourmaps(c);
+
+       /*
+        * Scan the list of atoms on WM_PROTOCOLS to see which of the
+        * protocols that we understand the client is prepared to
+        * participate in. (See ICCCM section 4.1.2.7.)
+        */
+       if (XGetWMProtocols(dpy, c->window, &protocols, &n) != 0) {
+               for (p = 0; p < n; p++) {
+                       if (protocols[p] == wm_delete) {
+                               c->proto |= Pdelete;
+                       } else if (protocols[p] == wm_take_focus) {
+                               c->proto |= Ptakefocus;
+                       }
+               }
+
+               XFree(protocols);
+       }
+
+       /* Get the WM_TRANSIENT_FOR property (see ICCCM section 4.1.2.6). */
+       getTransientFor(c);
+
+       /* Work out details for the Client structure from the hints. */
+       if (hints && (hints->flags & InputHint))
+               c->accepts_focus = hints->input;
+       if (c->proto | Ptakefocus)
+               /* WM_TAKE_FOCUS overrides normal hints */
+               c->accepts_focus = True;
+
+       if (!getWindowState(c->window, &state))
+               state = hints ? hints->initial_state : NormalState;
+
+       /*
+        *      Sort out the window's position.
+        */
+       {
+               Window root_window;
+               int x, y;
+               unsigned int w, h;
+               unsigned int border_width, depth;
+
+               XGetGeometry(dpy, c->window, &root_window, &x, &y, &w, &h,
+                       &border_width, &depth);
+
+               /*
+                * Do the size first.
+                *
+                * "The size specifiers refer to the width and height of the
+                * client excluding borders" -- ICCCM 4.1.2.3.
+                */
+               c->size.width  = w;
+               c->size.height = h;
+               if (c->framed == True) {
+                       c->size.width  += 2 * border;
+                       c->size.height += 2 * border;
+               }
+
+               /*
+                * THIS IS A HACK!
+                *
+                * OpenGL programs have a habit of appearing smaller than their
+                * minimum sizes, which they don't like.
+                */
+               if (c->size.width < c->size.min_width)
+                       c->size.width = c->size.min_width;
+               if (c->size.height < c->size.min_height)
+                       c->size.height = c->size.min_height;
+
+               /* Do the position next. */
+
+               /*
+                * If we have a user-specified position for a top-level window,
+                * or a program-specified position for a dialogue box, we'll
+                * take it. We'll also just take it during initialisation,
+                * since the previous manage probably placed its windows
+                * sensibly.
+                */
+               if (c->trans != None && c->size.flags & PPosition) {
+                       /* It's a "dialogue box". Trust it. */
+                       c->size.x = x;
+                       c->size.y = y;
+               } else if ((c->size.flags & USPosition) ||
+                       c->framed == False || mode == wm_initialising ) {
+                       /* Use the specified window position. */
+                       c->size.x = x;
+                       c->size.y = y;
+
+                       /*
+                        * We need to be careful of the right-hand edge and
+                        * bottom. We can use the window gravity (if specified)
+                        * to handle this. (See section 4.1.2.3 of the ICCCM.)
+                        */
+                       applyGravity(c);
+               } else {
+                       /* No position was specified: use the auto-placement
+                        * heuristics. */
+
+                       /* firstly, make sure auto_x and auto_y are outside
+                        * strut */
+                       if (auto_x < c->screen->strut.left)
+                               auto_x = c->screen->strut.left;
+                       if (auto_y < c->screen->strut.top)
+                               auto_y = c->screen->strut.top;
+
+                       if ((auto_x + c->size.width) >
+                               (c->screen->display_width -
+                               c->screen->strut.right) &&
+                               (c->size.width <=
+                               (c->screen->display_width -
+                               c->screen->strut.left -
+                               c->screen->strut.right))) {
+                               /*
+                                * If the window wouldn't fit using normal
+                                * auto-placement but is small enough to fit
+                                * horizontally, then centre the window
+                                * horizontally.
+                                */
+                               c->size.x = (c->screen->display_width
+                                       - c->size.width) / 2;
+                               auto_x = c->screen->strut.left + 20;
+                       } else {
+                               c->size.x = auto_x;
+                               auto_x += 10;
+                               if (auto_x > (c->screen->display_width / 2))
+                                       auto_x = c->screen->strut.left + 20;
+                       }
+
+                       if (((auto_y + c->size.height) >
+                               (c->screen->display_height -
+                               c->screen->strut.bottom)) &&
+                               (c->size.height <=
+                               (c->screen->display_height -
+                               c->screen->strut.top -
+                               c->screen->strut.bottom))) {
+                               /*
+                                * If the window wouldn't fit using normal
+                                * auto-placement but is small enough to fit
+                                * vertically, then centre the window
+                                * vertically.
+                                */
+                                c->size.y = (c->screen->display_height
+                                       - c->size.height) / 2;
+                                auto_y = c->screen->strut.top + 20;
+                       } else {
+                               c->size.y = auto_y;
+                               auto_y += 10;
+                               if (auto_y > (c->screen->display_height / 2))
+                                       auto_y = c->screen->strut.top + 20;
+                       }
+               }
+       }
+
+       if (hints)
+               XFree(hints);
+
+       /*
+        * Do all the reparenting and stuff.
+        */
+
+       if (c->framed == True) {
+               c->parent = XCreateSimpleWindow(dpy, c->screen->root,
+                       c->size.x, c->size.y - titleHeight(),
+                       c->size.width, c->size.height + titleHeight(),
+                       1, c->screen->black, c->screen->white);
+
+               attr.event_mask = ExposureMask | EnterWindowMask | ButtonMask |
+                       SubstructureRedirectMask | SubstructureNotifyMask |
+                       PointerMotionMask;
+               XChangeWindowAttributes(dpy, c->parent, CWEventMask, &attr);
+
+               XResizeWindow(dpy, c->window, c->size.width - 2 * border,
+                       c->size.height - 2 * border);
+       }
+
+       /*
+        * Stupid X11 doesn't let us change border width in the above
+        * call. It's a window attribute, but it's somehow second-class.
+        *
+        * As pointed out by Adrian Colley, we can't change the window
+        * border width at all for InputOnly windows.
+        */
+       if (current_attr.class != InputOnly)
+               XSetWindowBorderWidth(dpy, c->window, 0);
+
+       attr.event_mask = ColormapChangeMask | EnterWindowMask |
+               PropertyChangeMask | FocusChangeMask;
+       attr.win_gravity = StaticGravity;
+       attr.do_not_propagate_mask = ButtonMask;
+       XChangeWindowAttributes(dpy, c->window,
+               CWEventMask | CWWinGravity | CWDontPropagate, &attr);
+
+       if (c->framed == True) {
+               XReparentWindow(dpy, c->window, c->parent,
+                       border, border + titleHeight());
+       } else {
+               XReparentWindow(dpy, c->window, c->parent,
+                       c->size.x, c->size.y);
+       }
+
+       setShape(c);
+
+       XAddToSaveSet(dpy, c->window);
+       if (state == IconicState) {
+       } else {
+               /* Map the new window in the relevant state. */
+               c->hidden = False;
+               XMapWindow(dpy, c->parent);
+               XMapWindow(dpy, c->window);
+               setactive(c, (focus_mode == focus_click) ? 1 : 0, 0L);
+               Client_SetState(c, NormalState);
+       }
+
+       if (c->wstate.fullscreen == True) Client_EnterFullScreen(c);
+
+       if (current != c)
+               cmapfocus(current);
+}
+
+static void
+applyGravity(Client *c) {
+       if (c->framed == False) return; /* only required for framed windows*/
+       if (c->size.flags & PWinGravity) {
+               switch (c->size.win_gravity) {
+                       case NorthEastGravity:
+                               c->size.x -= 2 * border;
+                               break;
+                       case SouthWestGravity:
+                               c->size.y -= 2 * border;
+                               break;
+                       case SouthEastGravity:
+                               c->size.x -= 2 * border;
+                               c->size.y -= 2 * border;
+                               break;
+               }
+       }
+}
+
+void
+getTransientFor(Client *c) {
+       Window  trans = None;
+
+       XGetTransientForHint(dpy, c->window, &trans);
+       c->trans = trans;
+}
+
+void
+withdraw(Client *c) {
+       if (c->parent != c->screen->root) {
+               XUnmapWindow(dpy, c->parent);
+               XReparentWindow(dpy, c->parent, c->screen->root, c->size.x, c->size.y);
+       }
+
+       XRemoveFromSaveSet(dpy, c->window);
+       Client_SetState(c, WithdrawnState);
+
+       /*
+        * Flush and ignore any errors. X11 sends us an UnmapNotify before it
+        * sends us a DestroyNotify. That means we can get here without knowing
+        * whether the relevant window still exists.
+        */
+       ignore_badwindow = 1;
+       XSync(dpy, False);
+       ignore_badwindow = 0;
+}
+
+static void
+installColourmap(Colormap cmap) {
+       if (cmap == None)
+               cmap = DefaultColormap(dpy, DefaultScreen(dpy));
+       XInstallColormap(dpy, cmap);
+}
+
+void
+cmapfocus(Client * c) {
+       int     i;
+       int     found;
+       Client  *cc;
+
+       if (c == 0)
+               installColourmap(None);
+       else if (c->ncmapwins != 0) {
+               found = 0;
+               for (i = c->ncmapwins - 1; i >= 0; i--) {
+                       installColourmap(c->wmcmaps[i]);
+                       if (c->cmapwins[i] == c->window)
+                               found++;
+               }
+               if (!found)
+                       installColourmap(c->cmap);
+       } else if (c->trans != None && (cc = Client_Get(c->trans)) != 0 &&
+           cc->ncmapwins != 0)
+               cmapfocus(cc);
+       else
+               installColourmap(c->cmap);
+}
+
+void
+getColourmaps(Client *c) {
+       int     n;
+       int     i;
+       Window  *cw;
+       XWindowAttributes       attr;
+
+       if (c == 0)
+               return;
+
+       n = getProperty(c->window, wm_colormaps, XA_WINDOW, 100L, (unsigned char **) &cw);
+       if (c->ncmapwins != 0) {
+               XFree(c->cmapwins);
+               free(c->wmcmaps);
+       }
+       if (n <= 0) {
+               c->ncmapwins = 0;
+               return;
+       }
+       c->ncmapwins = n;
+       c->cmapwins = cw;
+
+       c->wmcmaps = (Colormap *) malloc(n * sizeof(Colormap));
+       for (i = 0; i < n; i++) {
+               if (cw[i] == c->window) {
+                       c->wmcmaps[i] = c->cmap;
+               } else {
+                       XSelectInput(dpy, cw[i], ColormapChangeMask);
+                       XGetWindowAttributes(dpy, cw[i], &attr);
+                       c->wmcmaps[i] = attr.colormap;
+               }
+       }
+}
+
+/*ARGSUSED*/
+void
+Terminate(int signal) {
+       /* Set all clients free. */
+       Client_FreeAll();
+
+       /* Give up the input focus and the colourmap. */
+       XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+       installColourmap(None);
+
+       XCloseDisplay(dpy);
+       exit(signal ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+static int
+getProperty(Window w, Atom a, Atom type, long len, unsigned char **p) {
+       Atom    real_type;
+       int     format;
+       unsigned long   n;
+       unsigned long   extra;
+       int     status;
+
+       /*
+        *      len is in 32-bit multiples.
+        */
+       status = XGetWindowProperty(dpy, w, a, 0L, len, False, type, &real_type, &format, &n, &extra, p);
+       if (status != Success || *p == 0)
+               return -1;
+       if (n == 0)
+               XFree(*p);
+       /*
+        *      could check real_type, format, extra here...
+        */
+       return n;
+}
+
+void
+getWindowName(Client *c) {
+       char * name;
+       Atom actual_type;
+       int format;
+       unsigned long n;
+       unsigned long extra;
+       int was_nameless;
+
+       if (c == 0)
+               return;
+
+       was_nameless = (c->name == 0);
+
+       if (ewmh_get_window_name(c) == False &&
+               XGetWindowProperty(dpy, c->window, _mozilla_url, 0L, 100L, False, AnyPropertyType, &actual_type, &format, &n, &extra, (unsigned char **) &name) == Success && name && *name != '\0' && n != 0) {
+               Client_Name(c, name, False);
+               XFree(name);
+       } else if (XGetWindowProperty(dpy, c->window, XA_WM_NAME, 0L, 100L, False, AnyPropertyType, &actual_type, &format, &n, &extra, (unsigned char **) &name) == Success && name && *name != '\0' && n != 0) {
+               /* That rather unpleasant condition is necessary because xwsh uses
+               * COMPOUND_TEXT rather than STRING for its WM_NAME property,
+               * and anonymous xwsh windows are annoying.
+               */
+               if (actual_type == compound_text && memcmp(name, "\x1b\x28\x42", 3) == 0) {
+                       Client_Name(c, name + 3, False);
+               } else {
+                       Client_Name(c, name, False);
+               }
+               XFree(name);
+       }
+
+       if (!was_nameless)
+               Client_DrawBorder(c, c == current);
+}
+
+void
+getNormalHints(Client *c) {
+       int x, y, w, h;
+       long msize;
+
+       /* We have to be a little careful here. The ICCCM says that the x, y
+        * and width, height components aren't used. So we use them. That means
+        * that we need to save and restore them whenever we fill the size
+        * struct. */
+       x = c->size.x;
+       y = c->size.y;
+       w = c->size.width;
+       h = c->size.height;
+
+       /* Do the get. */
+       if (XGetWMNormalHints(dpy, c->window, &c->size, &msize) == 0)
+               c->size.flags = 0;
+
+       if (c->framed == True) {
+               /*
+               * Correct the minimum allowable size of this client to take
+               * account of the window border.
+               */
+               if (c->size.flags & PMinSize) {
+                       c->size.min_width  += 2 * border;
+                       c->size.min_height += 2 * border;
+               } else {
+                       c->size.flags |= PMinSize;
+                       c->size.min_width  = 2 * (2 * border);
+                       if (c->accepts_focus)
+                               c->size.min_height = 2 * (2*border);
+                       else
+                               c->size.min_height = 2 * (2*border);
+               }
+
+               /*
+               * Correct the maximum allowable size of this client to take
+               * account of the window border.
+               */
+               if (c->size.flags & PMaxSize) {
+                       c->size.max_width  += 2 * border;
+                       c->size.max_height += 2 * border;
+               }
+       }
+
+       /*
+        * Ensure that the base width & height and the width & height increments
+        * are set correctly so that we don't have to do this in MakeSane.
+        */
+       if (!(c->size.flags & PBaseSize))
+               c->size.base_width = c->size.base_height = 0;
+
+       if (!(c->size.flags & PResizeInc))
+               c->size.width_inc = c->size.height_inc = 1;
+
+       /*
+        * If the client gives identical minimum and maximum sizes, we don't
+        * want the user to resize in that direction.
+        */
+       if (c->size.min_width == c->size.max_width)
+               c->size.width_inc = 0;
+
+       if (c->size.min_height == c->size.max_height)
+               c->size.height_inc = 0;
+
+       /* Restore the window-manager bits. */
+       c->size.x = x;
+       c->size.y = y;
+       c->size.width = w;
+       c->size.height = h;
+}
+
+static int
+getWindowState(Window w, int *state) {
+       long    *p = 0;
+
+       if (getProperty(w, wm_state, wm_state, 2L, (unsigned char **) &p) <= 0)
+               return 0;
+
+       *state = (int) *p;
+       XFree(p);
+       return 1;
+}
+
+
+extern Bool
+motifWouldDecorate(Client *c) {
+       unsigned long *p = 0;
+       Bool ret = True; /* if all else fails - decorate */
+
+       if (getProperty(c->window, motif_wm_hints, motif_wm_hints,
+               5L, (unsigned char **) &p) <= 0)
+               return ret;
+
+       if ((p[0] & MWM_HINTS_DECORATIONS) &&
+               !(p[2] & (MWM_DECOR_BORDER | MWM_DECOR_ALL)))
+               ret = False;
+
+       XFree(p);
+       return ret;
+}
diff --git a/no_xmkmf_makefile b/no_xmkmf_makefile
new file mode 100644 (file)
index 0000000..d184e5c
--- /dev/null
@@ -0,0 +1,48 @@
+#      Sample Makefile for lwm.
+
+#      You ought to be using the Imakefile (xmkmf;make) but
+#      if Imake isn't set up properly on your system, this might
+#      help you out. I used to use it on an SGI.
+
+# Uncomment these lines to use gcc.
+#CC = gcc
+#CFLAGS = -ansi -pedantic -Wall -DSHAPE
+
+# Uncomment these lines to use SGI cc.
+#CC = cc
+#CFLAGS = -fullwarn -g -DSHAPE
+
+# Uncomment these for Solaris Sun Studio, choose your architecture.
+#CC = cc
+#CFLAGS = -Xa -fast -xarch=v8a
+#CFLAGS = -Xa -fast -xarch=386
+
+DEFINES = 
+
+# Bennett Todd (bet@lehman.com) says this helped him compile on
+# Solaris 2.5.1, avoiding a problem with <sys/signal.h>.
+#DEFINES = -D_POSIX_C_SOURCE=2
+
+# Add any strange libraries your system needs here.
+LDFLAGS = -lXext -lX11 -lICE -lSM
+
+# -----------------------------------------------------------------------------
+
+OFILES = client.o cursor.o disp.o error.o ewmh.o lwm.o manage.o mouse.o \
+       resource.o session.o shape.o
+HFILES = lwm.h ewmh.h
+
+# -----------------------------------------------------------------------------
+
+all: lwm
+
+lwm: $(OFILES)
+       $(CC) $(CFLAGS) $(DEFINES) -o lwm $(OFILES) $(LDFLAGS)
+
+install: lwm
+       cp lwm /usr/local/bin
+
+$(OFILES): $(HFILES)
+
+clean:
+       rm -f lwm *.o core
diff --git a/shape.c b/shape.c
new file mode 100644 (file)
index 0000000..2b06189
--- /dev/null
+++ b/shape.c
@@ -0,0 +1,56 @@
+/*
+ * lwm, a window manager for X11
+ * Copyright (C) 1997-2016 Elliott Hughes, James Carter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <unistd.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#ifdef SHAPE
+#include <X11/extensions/shape.h>
+#endif
+
+#include "lwm.h"
+
+/*ARGSUSED*/
+extern void
+setShape(Client *c) {
+}
+
+/*ARGSUSED*/
+extern int
+shapeEvent(XEvent *ev) {
+       return 0;
+}
+
+/*ARGSUSED*/
+extern int
+isShaped(Window w) {
+       return 0;
+}
+
+extern int
+serverSupportsShapes(void) {
+       return 0;
+}