--- /dev/null
+enh Elliott Hughes ehughes@bluearc.com
+jfc James Carter james@jfc.org.uk
--- /dev/null
+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.
+
--- /dev/null
+ 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.
--- /dev/null
+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 -
--- /dev/null
+#!/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.
--- /dev/null
+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}
--- /dev/null
+This is lwm, a window manager for X.
+
+See INSTALL for install instructions and COPYRIGHT for license details.
+
--- /dev/null
+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)
+
--- /dev/null
+use XFT extensions where available?
+
--- /dev/null
+/*
+ * 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 *)¤t->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)));
+}
--- /dev/null
+/*
+ * 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];
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+.\" 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>
--- /dev/null
+/*
+ * 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, ¤t_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;
+}
--- /dev/null
+# 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
--- /dev/null
+/*
+ * 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;
+}