├── .gitignore ├── AUTHORS ├── BUGS ├── COPYING ├── ChangeLog ├── INSTALL ├── Imakefile ├── README ├── README.md ├── TODO ├── client.c ├── cursor.c ├── disp.c ├── error.c ├── ewmh.c ├── ewmh.h ├── lwm.c ├── lwm.h ├── lwm.man ├── manage.c ├── mouse.c ├── no_xmkmf_makefile ├── resource.c ├── session.c └── shape.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | 4 | # Libraries 5 | *.lib 6 | *.a 7 | 8 | # Shared objects (inc. Windows DLLs) 9 | *.dll 10 | *.so 11 | *.so.* 12 | *.dylib 13 | 14 | # Executables 15 | *.exe 16 | *.out 17 | *.app 18 | 19 | # Other generated Stuffs 20 | Makefile 21 | Makefile.bak 22 | lwm 23 | lwm._man 24 | 25 | mkdist.sh 26 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | enh Elliott Hughes ehughes@bluearc.com 2 | jfc James Carter james@jfc.org.uk 3 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | we're possibly being a bit overzealous about maintaining the window stack, 2 | with lots of flickering windows as a result. it's particularly noticeable 3 | when you're running a desktop - but then that's a stupid thing to do anyway. 4 | not sure how to fix it without maintaining a lot more data about the window 5 | stack. 6 | 7 | reported by corey holcomb-hockin: lwm producues messages like this when popups appear: 8 | lwm: protocol request X_ConfigureWindow on resource 0xa0000e failed: 9 | BadWindow (invalid Window parameter) 10 | lwm: protocol request X_SendEvent on resource 0xa0000e failed: BadWindow 11 | (invalid Window parameter) 12 | lwm: protocol request X_ConfigureWindow on resource 0xa0000e failed: 13 | BadWindow (invalid Window parameter) 14 | lwm: protocol request X_SendEvent on resource 0xa0000e failed: BadWindow 15 | (invalid Window parameter) 16 | lwm: protocol request X_SetInputFocus on resource 0xa0000e failed: 17 | BadWindow (invalid Window parameter) 18 | lwm: protocol request X_SetInputFocus on resource 0x8000de failed: 19 | BadMatch (invalid parameter attributes) 20 | 21 | martin and elliott still have window problems with edit, although it's 22 | better than it was. apparently the window is now placed correctly, 23 | but gets larger between successive runs. i can't reproduce this, 24 | unfortunately. 25 | 26 | chris reports that positioning a window on the bottom or right edge of a screen 27 | and then resizing the edge opposite to that touching the edge of the screen 28 | causes the window to jump in 1 unit (pixel or character depending on the 29 | application). also, and possibly related, holding button 1 to resize any edge 30 | apart from the right, and then moving the mouse along that edge (ie. 31 | perpendicular to the normal movement for resizing) causes the window to shrink 32 | one unit. 33 | 34 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | Change Log for "lwm" 2 | 3 | 2016-02-12 jfc York 4 | Patches from Greg Kenneky and other fixes for fullscreen windows. 5 | 6 | Released lvm-1.2.4. 7 | 8 | 2013-07-09 jfc York 9 | Applied a couple of minor patches suggested by Jari Aalto, the 10 | Debian package maintainer. 11 | 12 | Released lwm-1.2.3. 13 | 14 | 15 | 2009-11-24 jfc York 16 | Released lwm-1.2.2. 17 | 18 | 2009-11-20 jfc York 19 | Improved performance by only checking for pending X events when the 20 | socket it ready for reading. 21 | 22 | Fixed applyGravity() bug that caused frameless windows to be 23 | mis-positioned. 24 | 25 | Applied a workaround in destroy() to avoid error reports when closing 26 | windows. 27 | 28 | 2005-01-28 jfc York 29 | 30 | Applied a patch from Chris Reece that ensures that the popup 31 | menu does not disappear off the bottom of the screen. 32 | 33 | 2004-09-30 jfc York 34 | 35 | Fixed an issue with IRIX 6.5 and lwm, where the root menu could 36 | not be used because motion events had coordinates with respect to 37 | the popup, not the root. Fixed by explicitly using the root window 38 | coordinates. 39 | 40 | Released lwm-1.2.1. 41 | 42 | 2004-09-28 jfc York 43 | 44 | Added missing -lSM to no_xmkmf_makefile. 45 | 46 | 2003-12-09 jfc York 47 | 48 | Fixed bug (reported by Matthew Wilcox) where windows with extremely 49 | long names could cause the pop menu to be unusable. Fixed by 50 | maintaining a separate, shortened name for the menu, if 51 | necessary ("this is a very very [...] ry long window name"). This 52 | takes no account of UTF-8 names as yet. 53 | 54 | 2003-12-08 jfc York 55 | 56 | Fixed bug (reported by Eugene Wong) where resizing the top of 57 | a window would cause it to jump up several pixels. The height of 58 | the titlebar was not being considered when calculating mouse 59 | motion in reshaping_motionnotify(). 60 | 61 | Modified manage() to avoid autoplacing windows during 62 | initialisation. 63 | 64 | Released lwm-1.2.0. 65 | 66 | 2003-12-03 jfc York 67 | 68 | Applied patch from Elliott that gives focus to new windows in 69 | click-to-focus mode. 70 | 71 | Changed the buttonpress code in disp.c to ignore scroll wheel 72 | "clicks". 73 | 74 | Modified Client_Remove so that, in click-to-focus mode, it 75 | refocuses on the most sensible window (either the top window, 76 | or the window that the closing window was a transient for). 77 | 78 | Attempted to fix the edit placement bug by adding titleHeight() 79 | to the supplied X coordinate during a ConfigureRequest event, 80 | and not attempting to fix clients that don't supply a border 81 | width during a configure request. 82 | 83 | Fixed fullscreen-mode bug where galeon windows appeared to jump 84 | up and to the left after the first click. 85 | 86 | Released lwm-1.1.7. 87 | 88 | 2003-11-28 jfc York 89 | 90 | Changed the behaviour when unhiding a window in click-to-focus mode. 91 | An unhidden window now automatically gets focus in this mode. 92 | 93 | Fixed a small bug in the session management code that could 94 | cause a crash when lwm quit. 95 | 96 | Moved a call to ewmh_set_client_list make before ewmh was 97 | initialised. 98 | 99 | Changed lwm's behaviour when minimising windows. Button three must 100 | now be pressed and released before the window is hidden (or 101 | moved to the bottom of the stack). This ensures that lwm swallows 102 | all the events generated during the operation, and allows the user 103 | to back out of the operation by moving the mouse out of the window 104 | before releasing the button. 105 | 106 | Removed include of Xm/MwmUtil.h in manage.c, and the HAVE_MOTIF 107 | kludge from the Imakefile, in favour of copying the few lines 108 | that are required from Xm/MwmUtil.h (LessTif, so hopefully no 109 | licensing issues). 110 | 111 | Fixed bug that caused the last cursor displayed in a frame 112 | to be incorrectly used when moving into the frame when the 113 | root menu was on screen. This is done by brute force - 114 | see Client_ResetAllCursors(). 115 | 116 | Released lwm-1.1.6. 117 | 118 | 2003-11-26 jfc York 119 | 120 | Fixed bug in Client_MakeSane that caused occasional crashes 121 | during window moves/resizing. Should investigate why it occasionally 122 | gets called with a NULL client. 123 | 124 | 2003-11-03 jfc York 125 | 126 | Added an entry for LeaveNotify in the dispatch table (disp.c). 127 | 128 | 2003-08-13 jfc York 129 | 130 | Removed "error" message when lwm fails to connect to a session 131 | manager. This isn't actually an error and the message is confusing. 132 | 133 | 2003-08-01 jfc York 134 | 135 | Fixed bug that allowed clients to grab the focus and confuse lwm. 136 | 137 | Cleaned up the code for raising and lowering clients, and added 138 | code to prevent a client from being raised above its transients. 139 | 140 | Retired disp.old, and CLOSE_PATCH.txt. 141 | 142 | Added an edge resistance to the workarea, so that window may 143 | be moved to the edge of the workarea without precise mousing, 144 | as requested by MAD. EDGE_RESIST in lwm.h defines the number of 145 | pixels of resistance and may be safely set to zero. 146 | 147 | Released lwm-1.1.5. 148 | 149 | 2003-07-31 jfc York 150 | 151 | In click-to-focus mode, always draw the box in the frame. 152 | 153 | 2003-07-29 jfc York 154 | 155 | Added a click-to-focus mode. The default remains (sloppy) 156 | enter-to-focus. 157 | 158 | Released lwm-1.1.4. 159 | 160 | 161 | 2003-07-28 jfc York 162 | 163 | Updated no_xmkmf_makefile to reflect the changes made since 1.01. 164 | 165 | 2003-07-10 jfc York 166 | 167 | Fixed a bug in manage.c than prevented lwm compiling on systems 168 | with no variety of Motif installed. If this means you, remove 169 | _DHAVE_MOTIF from Imakefile. 170 | 171 | Released lwm-1.1.3. 172 | 173 | 2003-07-08 jfc York 174 | 175 | Added support for NET_MOVERESIZE, but I cannot find any 176 | applications the want to use it, apart from the keyboard 177 | variants. I don't know what to do about the keyboard move/resize. 178 | 179 | 2003-07-03 jfc York 180 | 181 | Fixed a few buglets thrown up by running lwm through the compiler 182 | with all warnings on. 183 | 184 | 2003-07-02 jfc York 185 | 186 | In Client_MakeSane(), added a check to prevent windows being 187 | moved into a position where they might be completely obscured 188 | by panels/docks. 189 | 190 | Changed ewmh_set_strut() to run Client_MakeSane() across all 191 | clients when the work area changes. This avoids clients getting 192 | lost behind panels/docks. 193 | 194 | Added support for _NET_WM_STATE_ABOVE and 195 | _NET_WM_STATE_BELOW. Added fix_stack() to maintain the window 196 | stack as dictated by the EWMH spec. 197 | 198 | 2003-07-01 jfc York 199 | 200 | Added support for _NET_WM_STRUT. lwm now maintains _NET_WORKAREA 201 | correctly, and takes the reserved space into account in its 202 | window placing algorithm. 203 | 204 | Released lwm-1.1.2. 205 | 206 | 2003-06-30 jfc York 207 | 208 | Fixed bug that caused tk menus to be badly placed placed by 209 | sending a configure notify where appropriate in setactive(). 210 | 211 | Removed compile time option of prepending window title's with 212 | the client machines's name (PREPEND_CLIENT_MACHINE). 213 | 214 | Added i18n support for window titles, using UTF8 names from 215 | _NET_WM_NAME where available and supported (ie XFree86). 216 | 217 | Added code in disp.c to change the pointer in some areas of the 218 | frame to indicate the action taken by button1. I didn't allow the 219 | "move" pointer in the titlebar because it looked nasty. Added 220 | the xkill pointer for the the box. This was a TODO item. 221 | 222 | 2003-06-28 jfc York 223 | 224 | Added GPL headers to all the source files. 225 | 226 | Released lwm-1.1.1. 227 | 228 | 2003-06-27 jfc York 229 | 230 | Fixed the bug where each GTK window generated an extra 231 | window when lwm shut down by unmapping all the clients in 232 | Client_FreeAll(). Elliott thinks this is bad magic, and that 233 | the X server should lose the windows, but this doesn't happen 234 | with XFree86. 235 | 236 | Fixed bug, reported by Ed Porter, that caused moving the mouse 237 | wheel to generate xterms. Wheel mice generate button press events 238 | on buttons 4 and 5 and shell() wasn't taking this into account. 239 | 240 | Fixed silly bug in motifWouldDecorate(): windows should have a 241 | frame is MWM_DECOR_ALL is set. 242 | 243 | 2003-06-26 jfc York 244 | 245 | Shaped windows now work again. I'm not sure what I changed 246 | to break it, but the fix was to process shaped windows in 247 | scanWindowTree (they were previously ignored). They had to be 248 | clients anyway, if they were to appear in _NET_CLIENT_LIST. 249 | 250 | 2003-06-25 jfc York 251 | 252 | Fixed bug that caused frameless windows to be immoveable. 253 | 254 | In manage.c, allowed lwm to fall back on Motif hints when 255 | deciding if a window should have a frame, if _NET_WM_WINDOW_TYPE 256 | is not set. This breaks the EWMH spec, in that a window 257 | without _NET_WM_WINDOW_TYPE should be assumed to have 258 | _NET_WM_WINDOW_TYPE_NORMAL, but it's the only way for older 259 | apps to indicate that they don't want decorating, and in the 260 | absence of Motif hints the default state is 261 | _NET_WM_WINDOW_TYPE_NORMAL. 262 | 263 | 2003-06-24 jfc York 264 | 265 | Fixed the following TODO item: 266 | allow users to back out of closing a window if 267 | they leave the box before letting go of the button. 268 | Implemented by adding an extra wm_closing_window mode rather 269 | than adding to the Client structure, as per AMidthune's 270 | patch. Not sure which is the better solution, though. 271 | 272 | Added initial support for _NET_WM_STATE, but only for 273 | _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER and 274 | _NET_WM_HIDDEN. 275 | 276 | Added simple hardwired _NET_WM_ALLOWED_ACTIONS support, and 277 | support for the _NET_CLOSE_WINDOW client message. 278 | 279 | First attempt an _WM_STATE_FULLSCREEN and a full-screen mode. 280 | It's not quite right yet, but useable. 281 | 282 | 283 | 2003-06-23 jfc York 284 | 285 | Fixed some silly bugs in the session management code. 286 | 287 | Added initial EWMH code using the 1.2 spec: 288 | http://www.freedesktop.org/standards/wm-spec/1.2/html/ 289 | Initial support covers the mechanisms for announcing support 290 | for EWMH (_NET_SUPPORTED, _NET_SUPPORTING_WM_CHECK), the 291 | client list and active client (_NET_CLIENT_LIST and 292 | _NET_ACTIVE_WINDOW), and the window type (_NET_WM_WINDOW_TYPE). 293 | Windows may now be frameless if their window type indicates. 294 | 295 | 2003-06-21 jfc York 296 | 297 | Added session management so that GNOME2's gnome-session does 298 | not wait a long timeout when starting the window manager. 299 | 300 | 2000-02-08 enh Basel 301 | 302 | Tried out a patch from Robert Bauer so that it's possible to move 303 | windows with button 1, if you're in the ``titlebar'' (i.e. not touching 304 | the top border). This makes it easier for Windows users to cope 305 | with lwm, and easier for those with two-button mice (or laptops) 306 | too. At the moment, "mv disp.old disp.c" will give back the old 307 | behaviour. 308 | 309 | 1999-11-11 enh Basel 310 | 311 | Fixed a cut-and-paste bug in client.c that made the check for 312 | a window being too large or too small wrong. This bug was found 313 | by Mike Meyer. 314 | 315 | 1999-09-22 enh Basel 316 | 317 | Altered the button-press code so that it's now easier for unhappy 318 | users to alter which button performs which function. Simply edit 319 | lwm.h and modify the three relevant #define statements. 320 | 321 | 1999-07-19 enh Basel 322 | 323 | Added a handler for circulation events so that other programs 324 | can offer "Alt-Tab" functionality. 325 | 326 | 1999-07-08 enh Basel 327 | 328 | Fixed the cosmetic problem with titlebars of dialogue boxes. If 329 | this looks to be OK, I can think about another lwm release. 330 | 331 | 1999-06-10 enh Basel 332 | 333 | Incorporated bug fix by Adrian Colley regarding the attempt in 334 | manage.c to call XSetWindowBorderWidth on an InputOnly window, 335 | and moved the #include of after so that 336 | lwm can compile on Solaris 2.6. Cosmetic change to move the close 337 | box to line up with the client window. The effect is spoilt if the 338 | child insists on drawing a black border around itself, though. 339 | 340 | 1999-02-07 enh Basel 341 | 342 | Title-bars no longer pop up and down. An inactive window has a 343 | grey title instead. This means less load on the server, no annoying 344 | "I want to type the information from one window's title-bar into 345 | the current window but can't" syndrome, and a final solution to 346 | the race condition that's been with us since the very beginning. 347 | 348 | The size feedback no longer pops up as soon as you grab a window, 349 | because that made it almost impossible to grab a window without 350 | resizing it. 351 | 352 | 1998-11-03 enh Basel 353 | 354 | The size feedback now pops up as soon as you grab a window, 355 | rather than waiting for you to actually move. 356 | 357 | 1998-10-06 enh Basel 358 | 359 | Al pointed out that my Sun actually has two framebuffers. One 360 | monitor-lugging later, and I suddenly have a need for a window 361 | manager that can cope with multiple screens. And here it is! 362 | 363 | 1998-05-29 enh Basel 364 | 365 | Fixed window minimum/maximum height code so that it no longer 366 | includes the title decoration. Menu now pops down if a window 367 | disappears while the menu is up. 368 | 369 | 1998-03-23 enh Basel 370 | 371 | Removed unused constant. A little tidying up, renaming. Some 372 | debugging code removed. The width of the size-feedback window 373 | is now calculated at run-time depending on the size of the screen. 374 | 375 | 1998-02-05 enh Basel 376 | 377 | Fixed bug found by Marty Olevitch: lwm's automatic window 378 | placement heuristics broke down when either the right or bottom 379 | of the display were reached. 380 | Changed menu placement to ensure that the menu is fully 381 | on-screen. 382 | 383 | 1998-01-06 enh Basel 384 | 385 | Fixed bug found by J. Han whereby lwm dumped core if a window 386 | disappeared while being reshaped. 387 | 388 | 1997-09-01 enh Basel 389 | 390 | "Push to back" functionality moved from button 3 click in box 391 | to button 3 click anywhere in frame with Shift held down. 392 | 393 | 1997-08-29 enh Basel 394 | 395 | Simple version numbering introduced. 396 | 397 | 1997-08-25 enh Basel 398 | 399 | Fixed stupid mistake introduced with the last change, with regard 400 | to setting the input focus. 401 | 402 | 1997-08-22 enh Basel 403 | 404 | Xt applications (strictly, applications whose window title is 405 | the same as their class hint resource name) no longer have a 406 | title bar. This means it's more awkward to kill them, but that 407 | they don't have pointless decoration. 408 | 409 | 1997-08-07 enh Basel 410 | 411 | Bug related to hiding windows fixed. 412 | 413 | 1997-08-06 enh Basel 414 | 415 | The size indictor now has the correct GC settings. Whoops! 416 | Improved handling of WM_NORMAL_HINTS. Amongst other things, 417 | this means that size reporting of xterm et al is more reliable. 418 | 419 | 1997-07-31 enh Basel 420 | 421 | Reshaping now uses the popup to display the current width and 422 | height of the window being reshaped (in whatever units it uses). 423 | 424 | 1997-07-04 enh Swanwick 425 | 426 | Clicking button 3 on the "box" pushes the window to the bottom. 427 | Changing image in xv no longer causes the window to gravitate to 428 | the southeast. There's an ICCCM convention that clients should 429 | set the border width with each ConfigureWindow request. As usual, 430 | many clients fail to follow this convention. I get the distinct 431 | impression that the very reason for the existance of the Xt 432 | library is because the X11 protocol and ICCCM are so messy and 433 | involved that the only way to make X11 bearable was to write 434 | this code once and for all. The menu code has been rewritten, 435 | changing as a side-effect the order in which hidden windows 436 | appear on the menu. The rewrite now means that the order is very 437 | easy to change for experiments like alphabetical ordering etc. I 438 | like it as it is: a stack. 439 | 440 | 1997-06-24 enh York 441 | 442 | Now handles NoExpose events. Better protocol error reporting. 443 | Default minimum size calculation improved. 444 | 445 | 1997-06-23 enh York 446 | 447 | Both button 1 and 2 can now have commands associated with them. 448 | See the documentation for details. Windows whose minimum and 449 | maximum sizes are identical can no longer be resized. The 450 | oscillation race condition is now less likely to occur. Some 451 | dead code removed. 452 | 453 | 1997-05-25 enh York 454 | 455 | lwm now does the right thing with respect to hidden windows on 456 | exit and startup. a hidden window is now re-hidden if lwm exits 457 | and is then restarted. 458 | 459 | 1997-05-21 enh York 460 | 461 | Fixed a bug that meant a client could confuse lwm by remapping 462 | a hidden window: the menu of hidden windows wasn't being updated. 463 | 464 | 1997-05-16 enh York 465 | 466 | A bug relating to ConfigureRequests on the current window caused 467 | the title-bar to be redrawn incorrectly. Once again, this came 468 | to light with xv. 469 | 470 | The "New Shell" command has gone from the button 3 menu, and 471 | button 2 now performs this function. 472 | 473 | 1997-05-09 enh York 474 | 475 | This version fixes a bug relating to ConfigureRequests. Client 476 | windows that were resized under program control were resized, 477 | but the client was misinformed as to what change had actually 478 | taken place. xv's optimised redraw, for example, missed out on 479 | part of the window because of this. 480 | The behaviour with regard to hidden windows on exit has also 481 | changed. They're now remapped, but lowered in the window stack. 482 | This means that you don't lose them, but that they don't 483 | obliterate the more important windows on your screen if you kill 484 | the window manager. 485 | 486 | - Initial announcement on comp.windows.x.announce - 487 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | #!/bin/cat 2 | 3 | Installing lwm 4 | 5 | 1. Compile lwm: 6 | 7 | xmkmf ; make ; strip lwm 8 | 9 | 2. Copy the binary to where you want it. 10 | 11 | 3. Edit your Xsession or whatever to call lwm. 12 | -------------------------------------------------------------------------------- /Imakefile: -------------------------------------------------------------------------------- 1 | XCOMM lwm, a window manager for X11 2 | XCOMM Copyright (C) 1997-2016 Elliott Hughes, James Carter 3 | XCOMM 4 | XCOMM This program is free software; you can redistribute it and/or 5 | XCOMM modify it under the terms of the GNU General Public License 6 | XCOMM as published by the Free Software Foundation; either version 2 7 | XCOMM of the License, or (at your option) any later version. 8 | XCOMM 9 | XCOMM This program is distributed in the hope that it will be useful, 10 | XCOMM but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | XCOMM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | XCOMM GNU General Public License for more details. 13 | XCOMM 14 | XCOMM You should have received a copy of the GNU General Public License 15 | XCOMM along with this program; if not, write to the Free Software 16 | XCOMM Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | 18 | INCLUDES = -I$(TOP) 19 | DEPLIBS = $(DEPXLIB) $(DEPSMLIB) 20 | LOCAL_LIBRARIES = $(XLIB) $(SMLIB) -lICE 21 | DEFINES = -DSHAPE 22 | 23 | HEADERS = lwm.h ewmh.h 24 | SRCS = lwm.c manage.c mouse.c client.c cursor.c error.c disp.c shape.c resource.c session.c ewmh.c 25 | OBJS = ${SRCS:.c=.o} 26 | 27 | ComplexProgramTarget(lwm) 28 | 29 | ${OBJS}: ${HEADERS} 30 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is lwm, a window manager for X. 2 | 3 | See INSTALL for install instructions and COPYRIGHT for license details. 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lwm 2 | === 3 | 4 | *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. 5 | 6 | [http://www.jfc.org.uk/software/lwm.html](http://www.jfc.org.uk/software/lwm.html) 7 | 8 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | use XFT extensions where available? 2 | 3 | -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "lwm.h" 33 | #include "ewmh.h" 34 | 35 | Client *current; 36 | Client *last_focus = NULL; 37 | static Client *clients; 38 | 39 | static int popup_width; /* The width of the size-feedback window. */ 40 | 41 | Edge interacting_edge; 42 | 43 | static void sendClientMessage(Window, Atom, long, long); 44 | 45 | Client * 46 | client_head(void) { 47 | return clients; 48 | } 49 | 50 | void 51 | setactive(Client *c, int on, long timestamp) { 52 | int inhibit; 53 | 54 | if (c == 0 || hidden(c)) 55 | return; 56 | 57 | inhibit = !c->framed; 58 | 59 | if (!inhibit) { 60 | XMoveResizeWindow(dpy, c->parent, 61 | c->size.x, c->size.y - titleHeight(), 62 | c->size.width, c->size.height + titleHeight()); 63 | XMoveWindow(dpy, c->window, border, border + titleHeight()); 64 | sendConfigureNotify(c); 65 | } 66 | 67 | if (on && c->accepts_focus) { 68 | XSetInputFocus(dpy, c->window, RevertToPointerRoot, CurrentTime); 69 | if (c->proto & Ptakefocus) 70 | sendClientMessage(c->window, wm_protocols, 71 | wm_take_focus, timestamp); 72 | if (focus_mode == focus_click) { 73 | XUngrabButton(dpy, AnyButton, AnyModifier, c->window); 74 | } 75 | cmapfocus(c); 76 | } 77 | 78 | /* FIXME: is this sensible? */ 79 | if (on && !c->accepts_focus) { 80 | XSetInputFocus(dpy, None, RevertToPointerRoot, CurrentTime); 81 | } 82 | 83 | if (!on && focus_mode == focus_click) 84 | XGrabButton(dpy, AnyButton, AnyModifier, c->window, False, 85 | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, 86 | GrabModeSync, None, None); 87 | 88 | if (!inhibit) 89 | Client_DrawBorder(c, on); 90 | } 91 | 92 | 93 | void 94 | Client_DrawBorder(Client *c, int active) { 95 | int quarter = (border + titleHeight()) / 4; 96 | 97 | if (c->parent == c->screen->root || c->parent == 0 || 98 | c->framed == False || c->wstate.fullscreen == True) 99 | return; 100 | 101 | XSetWindowBackground(dpy, c->parent, 102 | active ? c->screen->black : c->screen->gray); 103 | XClearWindow(dpy, c->parent); 104 | 105 | /* Draw the ``box''. */ 106 | if (active || focus_mode == focus_click) { 107 | XDrawRectangle(dpy, c->parent, c->screen->gc, 108 | quarter + 2, quarter, 2 * quarter, 2 * quarter); 109 | } 110 | 111 | /* Draw window title. */ 112 | if (c->name != 0) { 113 | #ifdef X_HAVE_UTF8_STRING 114 | if (c->name_utf8 == True) 115 | Xutf8DrawString(dpy, c->parent, font_set, 116 | c->screen->gc, border + 2 + (3 * quarter), 117 | 2 + ascent(font_set_ext), 118 | c->name, c->namelen); 119 | else 120 | #endif 121 | XmbDrawString(dpy, c->parent, font_set, 122 | c->screen->gc, border + 2 + (3 * quarter), 123 | 2 + ascent(font_set_ext), 124 | c->name, c->namelen); 125 | } 126 | } 127 | 128 | 129 | Client * 130 | Client_Get(Window w) { 131 | Client * c; 132 | 133 | if (w == 0 || (getScreenFromRoot(w) != 0)) 134 | return 0; 135 | 136 | /* Search for the client corresponding to this window. */ 137 | for (c = clients; c; c = c->next) 138 | if (c->window == w || c->parent == w) 139 | return c; 140 | 141 | /* Not found. */ 142 | return 0; 143 | } 144 | 145 | 146 | Client * 147 | Client_Add(Window w, Window root) { 148 | Client * c; 149 | 150 | if (w == 0 || w == root) 151 | return 0; 152 | 153 | /* Search for the client corresponding to this window. */ 154 | for (c = clients; c != 0; c = c->next) 155 | if (c->window == w || c->parent == w) 156 | return c; 157 | 158 | c = calloc(1, sizeof *c); 159 | c->window = w; 160 | c->parent = root; 161 | c->framed = False; 162 | c->hidden = False; 163 | c->state = WithdrawnState; 164 | c->internal_state = INormal; 165 | c->cmap = None; 166 | c->name = 0; 167 | c->menu_name = 0; 168 | c->cursor = ENone; 169 | c->wtype = WTypeNone; 170 | c->wstate.skip_taskbar = False; 171 | c->wstate.skip_pager = False; 172 | c->wstate.fullscreen = False; 173 | c->wstate.above = False; 174 | c->wstate.below = False; 175 | c->strut.left = 0; 176 | c->strut.right = 0; 177 | c->strut.top = 0; 178 | c->strut.bottom = 0; 179 | c->ncmapwins = 0; 180 | c->cmapwins = 0; 181 | c->wmcmaps = 0; 182 | c->accepts_focus = 1; 183 | c->next = clients; 184 | 185 | /* Add to head of list of clients. */ 186 | clients = c; 187 | return clients; 188 | } 189 | 190 | 191 | void 192 | Client_Remove(Client *c) { 193 | Client * cc; 194 | ScreenInfo *screen = c->screen; 195 | 196 | if (c == 0) 197 | return; 198 | 199 | /* Remove the client from our client list. */ 200 | if (c == clients) 201 | clients = c->next; 202 | for (cc = clients; cc && cc->next; cc = cc->next) { 203 | if (cc->next == c) 204 | cc->next = cc->next->next; 205 | } 206 | 207 | /* Remove it from the hidden list if it's hidden. */ 208 | if (hidden(c)) { 209 | unhidec(c, 0); 210 | /* Al Smith points out that you also want to get rid of the menu 211 | * so you can be sure that if you let go on an item, you always 212 | * get the corresponding window. */ 213 | if (mode == wm_menu_up) { 214 | XUnmapWindow(dpy, current_screen->popup); 215 | mode = wm_idle; 216 | } 217 | } 218 | 219 | /* A deleted window can no longer be the current window. */ 220 | if (c == current || (current == NULL && c == last_focus)) { 221 | Client *focus = NULL; 222 | 223 | /* As pointed out by J. Han, if a window disappears while it's 224 | * being reshaped you need to get rid of the size indicator. */ 225 | if (c == current && mode == wm_reshaping) { 226 | XUnmapWindow(dpy, current_screen->popup); 227 | mode = wm_idle; 228 | } 229 | if (focus_mode == focus_click) { 230 | /* Try and find the window that this was a transient 231 | * for, else focus on the top client. */ 232 | if (c->trans != None) { 233 | focus = Client_Get(c->trans); 234 | } 235 | if (!focus) { 236 | Window dw1; 237 | Window dw2; 238 | Window *wins; 239 | unsigned int nwins; 240 | 241 | XQueryTree(dpy, c->screen->root, &dw1, 242 | &dw2, &wins, &nwins); 243 | while (nwins) { 244 | focus = Client_Get(wins[nwins -1]); 245 | if (focus) break; 246 | nwins--; 247 | } 248 | if (wins) XFree(wins); 249 | } 250 | } 251 | Client_Focus(focus, CurrentTime); 252 | } 253 | 254 | if (getScreenFromRoot(c->parent) == 0) 255 | XDestroyWindow(dpy, c->parent); 256 | 257 | if (c->ncmapwins != 0) { 258 | XFree(c->cmapwins); 259 | free(c->wmcmaps); 260 | } 261 | 262 | if (c->name != 0) 263 | free(c->name); 264 | if (c->menu_name != 0) 265 | free(c->name); 266 | 267 | free(c); 268 | 269 | ewmh_set_client_list(screen); 270 | ewmh_set_strut(screen); 271 | } 272 | 273 | 274 | void 275 | Client_MakeSane(Client *c, Edge edge, int *x, int *y, int *dx, int *dy) { 276 | Bool horizontal_ok = True; 277 | Bool vertical_ok = True; 278 | 279 | if (edge != ENone) { 280 | /* 281 | * Make sure we're not making the window too small. 282 | */ 283 | if (*dx < c->size.min_width) 284 | horizontal_ok = False; 285 | if (*dy < c->size.min_height) 286 | vertical_ok = False; 287 | 288 | /* 289 | * Make sure we're not making the window too large. 290 | */ 291 | if (c->size.flags & PMaxSize) { 292 | if (*dx > c->size.max_width) 293 | horizontal_ok = False; 294 | if (*dy > c->size.max_height) 295 | vertical_ok = False; 296 | } 297 | 298 | /* 299 | * Make sure the window's width & height are multiples of 300 | * the width & height increments (not including the base size). 301 | */ 302 | 303 | if (c->size.width_inc > 1) { 304 | int apparent_dx = *dx - 2 * border - c->size.base_width; 305 | int x_fix = apparent_dx % c->size.width_inc; 306 | 307 | switch (edge) { 308 | case ELeft: 309 | case ETopLeft: 310 | case EBottomLeft: 311 | *x += x_fix; 312 | /*FALLTHROUGH*/ 313 | case ERight: 314 | case ETopRight: 315 | case EBottomRight: 316 | *dx -= x_fix; 317 | break; 318 | default: break; 319 | } 320 | } 321 | 322 | if (c->size.height_inc > 1) { 323 | int apparent_dy = *dy - 2 * border - c->size.base_height; 324 | int y_fix = apparent_dy % c->size.height_inc; 325 | 326 | switch (edge) { 327 | case ETop: 328 | case ETopLeft: 329 | case ETopRight: 330 | *y += y_fix; 331 | /*FALLTHROUGH*/ 332 | case EBottom: 333 | case EBottomLeft: 334 | case EBottomRight: 335 | *dy -= y_fix; 336 | break; 337 | default: break; 338 | } 339 | } 340 | 341 | /* 342 | * Check that we may change the client horizontally and vertically. 343 | */ 344 | 345 | if (c->size.width_inc == 0) 346 | horizontal_ok = False; 347 | if (c->size.height_inc == 0) 348 | vertical_ok = False; 349 | } 350 | 351 | /* Ensure that at least one border is not entirely within the 352 | * reserved areas. Keeping clients completely within the 353 | * the workarea is too restrictive, but this measure means they 354 | * should always be accessible. 355 | * Of course all of this is only applicable if the client doesn't 356 | * set a strut itself. jfc 357 | */ 358 | if (c->strut.left == 0 && c->strut.right == 0 && 359 | c->strut.top == 0 && c->strut.bottom == 0) { 360 | if ((int)(*y + border) >= 361 | (int)(c->screen->display_height - 362 | c->screen->strut.bottom)) { 363 | *y = c->screen->display_height - 364 | c->screen->strut.bottom -border; 365 | } 366 | if ((int)(*y + c->size.height - border) <= 367 | (int)c->screen->strut.top) { 368 | *y = c->screen->strut.top + border - c->size.height; 369 | } 370 | if ((int)(*x + border) >= 371 | (int)(c->screen->display_width - 372 | c->screen->strut.right)) { 373 | *x = c->screen->display_width - 374 | c->screen->strut.right -border; 375 | } 376 | if ((int)(*x + c->size.width - border) <= 377 | (int)c->screen->strut.left) { 378 | *x = c->screen->strut.left + border - c->size.width; 379 | } 380 | } 381 | 382 | /* 383 | * Introduce a resistance to the workarea edge, so that windows may 384 | * be "thrown" to the edge of the workarea without precise mousing, 385 | * as requested by MAD. 386 | */ 387 | if (*x < (int)c->screen->strut.left && 388 | *x > ((int)c->screen->strut.left - EDGE_RESIST)) { 389 | *x = (int)c->screen->strut.left; 390 | } 391 | if ((*x + c->size.width) > 392 | (int)(c->screen->display_width - c->screen->strut.right) && 393 | (*x + c->size.width) < 394 | (int)(c->screen->display_width - c->screen->strut.right + EDGE_RESIST)) { 395 | *x = (int)(c->screen->display_width - c->screen->strut.right - 396 | c->size.width); 397 | } 398 | if ((*y - titleHeight()) < (int)c->screen->strut.top && 399 | (*y - titleHeight()) > 400 | ((int)c->screen->strut.top - EDGE_RESIST)) { 401 | *y = (int)c->screen->strut.top + titleHeight(); 402 | } 403 | if ((*y + c->size.height) > 404 | (int)(c->screen->display_height - c->screen->strut.bottom) && 405 | (*y + c->size.height) < 406 | (int)(c->screen->display_height - c->screen->strut.bottom + EDGE_RESIST)) { 407 | *y = (int)(c->screen->display_height - c->screen->strut.bottom - 408 | c->size.height); 409 | } 410 | 411 | /* 412 | * Update that part of the client information that we're happy with. 413 | */ 414 | if (interacting_edge != ENone) { 415 | if (horizontal_ok) { 416 | c->size.x = *x; 417 | c->size.width = *dx; 418 | } 419 | if (vertical_ok) { 420 | c->size.y = *y; 421 | c->size.height = *dy; 422 | } 423 | } else { 424 | if (horizontal_ok) 425 | c->size.x = *x; 426 | if (vertical_ok) 427 | c->size.y = *y; 428 | } 429 | } 430 | 431 | void 432 | Client_SizeFeedback(void) { 433 | int x, y; 434 | char buf[4*2 + 3 + 1]; 435 | 436 | /* Make the popup 10% wider than the widest string it needs to show. */ 437 | snprintf(buf, sizeof(buf), "%i x %i", current_screen->display_width, 438 | current_screen->display_height); 439 | popup_width = popupWidth(buf, strlen(buf)); 440 | popup_width += popup_width/10; 441 | 442 | /* Put the popup in the right place to report on the window's size. */ 443 | getMousePosition(&x, &y); 444 | XMoveResizeWindow(dpy, current_screen->popup, x + 8, y + 8, 445 | popup_width, popupHeight() + 1); 446 | XMapRaised(dpy, current_screen->popup); 447 | 448 | /* 449 | * Ensure that the popup contents get redrawn. Eventually, the function 450 | * size_expose will get called to do the actual redraw. 451 | */ 452 | XClearArea(dpy, current_screen->popup, 0, 0, 0, 0, True); 453 | } 454 | 455 | void 456 | size_expose(void) { 457 | int width, height; 458 | char buf[4*2 + 3 + 1]; 459 | 460 | width = current->size.width - 2*border; 461 | height = current->size.height - 2*border; 462 | 463 | /* This dance ensures that we report 80x24 for an xterm even when 464 | * it has a scrollbar. */ 465 | if (current->size.flags & (PMinSize|PBaseSize) && current->size.flags & PResizeInc) { 466 | if (current->size.flags & PBaseSize) { 467 | width -= current->size.base_width; 468 | height -= current->size.base_height; 469 | } else { 470 | width -= current->size.min_width; 471 | height -= current->size.min_height; 472 | } 473 | } 474 | 475 | if (current->size.width_inc != 0) 476 | width /= current->size.width_inc; 477 | if (current->size.height_inc != 0) 478 | height /= current->size.height_inc; 479 | 480 | snprintf(buf, sizeof(buf), "%i x %i", width, height); 481 | XmbDrawString(dpy, current_screen->popup, popup_font_set, 482 | current_screen->size_gc, 483 | (popup_width - popupWidth(buf, strlen(buf))) / 2, 484 | ascent(popup_font_set_ext) + 1, buf, strlen(buf)); 485 | } 486 | 487 | static void 488 | Client_OpaquePrimitive(Client *c, Edge edge) { 489 | Cursor cursor; 490 | int ox, oy; 491 | 492 | if (c == 0 /*|| c != current*/) 493 | return; 494 | 495 | /* Find out where we've got hold of the window. */ 496 | getMousePosition(&ox, &oy); 497 | ox = c->size.x - ox; 498 | oy = c->size.y - oy; 499 | 500 | cursor = getEdgeCursor(edge); 501 | XChangeActivePointerGrab(dpy, ButtonMask | PointerMotionHintMask | 502 | ButtonMotionMask | OwnerGrabButtonMask, cursor, CurrentTime); 503 | 504 | /* 505 | * Store some state so that we can get back into the main event 506 | * dispatching thing. 507 | */ 508 | interacting_edge = edge; 509 | start_x = ox; 510 | start_y = oy; 511 | mode = wm_reshaping; 512 | ewmh_set_client_list(c->screen); 513 | } 514 | 515 | void 516 | Client_Lower(Client *c) 517 | { 518 | if (c == 0) return; 519 | 520 | XLowerWindow(dpy, c->window); 521 | if (c->framed) XLowerWindow(dpy, c->parent); 522 | ewmh_set_client_list(c->screen); 523 | } 524 | 525 | void 526 | Client_Raise(Client *c) 527 | { 528 | Client * trans; 529 | 530 | if (c == 0) return; 531 | 532 | if (c->framed) XRaiseWindow(dpy, c->parent); 533 | XRaiseWindow(dpy, c->window); 534 | 535 | for (trans = clients; trans != NULL; trans = trans->next) { 536 | if (trans->trans != c->window && 537 | !(c->framed == True && trans->trans == c->parent)) 538 | continue; 539 | if (trans->framed) XRaiseWindow(dpy, trans->parent); 540 | XRaiseWindow(dpy, trans->window); 541 | } 542 | 543 | ewmh_set_client_list(c->screen); 544 | } 545 | 546 | void 547 | Client_Close(Client *c) { 548 | if (c == 0) 549 | return; 550 | 551 | /* 552 | * Terminate the client nicely if possible. Be brutal otherwise. 553 | */ 554 | if (c->proto & Pdelete) { 555 | sendClientMessage(c->window, wm_protocols, wm_delete, CurrentTime); 556 | } else { 557 | XKillClient(dpy, c->window); 558 | } 559 | } 560 | 561 | void 562 | Client_SetState(Client *c, int state) { 563 | long data[2]; 564 | 565 | data[0] = (long) state; 566 | data[1] = (long) None; 567 | 568 | c->state = state; 569 | XChangeProperty(dpy, c->window, wm_state, wm_state, 32, 570 | PropModeReplace, (unsigned char *) data, 2); 571 | ewmh_set_state(c); 572 | } 573 | 574 | static void 575 | sendClientMessage(Window w, Atom a, long data0, long data1) { 576 | XEvent ev; 577 | long mask; 578 | 579 | memset(&ev, 0, sizeof(ev)); 580 | ev.xclient.type = ClientMessage; 581 | ev.xclient.window = w; 582 | ev.xclient.message_type = a; 583 | ev.xclient.format = 32; 584 | ev.xclient.data.l[0] = data0; 585 | ev.xclient.data.l[1] = data1; 586 | mask = (getScreenFromRoot(w) != 0) ? SubstructureRedirectMask : 0L; 587 | 588 | XSendEvent(dpy, w, False, mask, &ev); 589 | } 590 | 591 | extern void 592 | Client_ResetAllCursors(void) { 593 | Client *c; 594 | XSetWindowAttributes attr; 595 | 596 | for (c = clients; c; c = c->next) { 597 | if (c->framed != True) continue; 598 | attr.cursor = c->screen->root_cursor; 599 | XChangeWindowAttributes(dpy, c->parent, 600 | CWCursor, &attr); 601 | c->cursor = ENone; 602 | } 603 | } 604 | 605 | extern void 606 | Client_FreeAll(void) { 607 | Client *c; 608 | XWindowChanges wc; 609 | 610 | for (c = clients; c; c = c->next) { 611 | int not_mapped = !normal(c); 612 | 613 | /* elliott thinks leaving window unmapped causes the x server 614 | * to lose them when the window manager quits. it doesn't 615 | * happen to me with XFree86's Xnest, but unmapping the 616 | * windows stops gtk window generating an extra window when 617 | * the window manager quits. 618 | * who is right? only time will tell.... 619 | */ 620 | XUnmapWindow(dpy, c->parent); 621 | XUnmapWindow(dpy, c->window); 622 | /* Remap the window if it's hidden. 623 | if (not_mapped) { 624 | XMapWindow(dpy, c->parent); 625 | XMapWindow(dpy, c->window); 626 | } */ 627 | 628 | /* Reparent it, and then push it to the bottom if it was hidden. */ 629 | XReparentWindow(dpy, c->window, c->screen->root, c->size.x, c->size.y); 630 | if (not_mapped) 631 | XLowerWindow(dpy, c->window); 632 | 633 | /* Give it back its initial border width. */ 634 | wc.border_width = c->border; 635 | XConfigureWindow(dpy, c->window, CWBorderWidth, &wc); 636 | } 637 | } 638 | 639 | extern void 640 | Client_ColourMap(XEvent *e) { 641 | int i; 642 | Client * c; 643 | 644 | for (c = clients; c; c = c->next) { 645 | for (i = 0; i < c->ncmapwins; i++) { 646 | if (c->cmapwins[i] == e->xcolormap.window) { 647 | c->wmcmaps[i] = e->xcolormap.colormap; 648 | if (c == current) 649 | cmapfocus(c); 650 | return; 651 | } 652 | } 653 | } 654 | } 655 | 656 | extern void 657 | Client_ReshapeEdge(Client *c, Edge e) { 658 | Client_OpaquePrimitive(c, e); 659 | } 660 | 661 | extern void 662 | Client_Move(Client *c) { 663 | Client_OpaquePrimitive(c, ENone); 664 | } 665 | 666 | extern int 667 | hidden(Client *c) { 668 | return c->state == IconicState; 669 | } 670 | 671 | extern int 672 | withdrawn(Client *c) { 673 | return c->state == WithdrawnState; 674 | } 675 | 676 | extern int 677 | normal(Client *c) { 678 | return c->state == NormalState; 679 | } 680 | 681 | extern void 682 | Client_EnterFullScreen(Client *c) { 683 | XWindowChanges fs; 684 | 685 | memcpy(&c->return_size, &c->size, sizeof(XSizeHints)); 686 | if (c->framed) { 687 | c->size.x = fs.x = -border; 688 | c->size.y = fs.y = -border; 689 | c->size.width = fs.width = 690 | c->screen->display_width + 2 * border; 691 | c->size.height = fs.height = 692 | c->screen->display_height + 2 * border; 693 | XConfigureWindow(dpy, c->parent, 694 | CWX | CWY | CWWidth | CWHeight, &fs); 695 | 696 | fs.x = border; 697 | fs.y = border; 698 | fs.width = c->screen->display_width; 699 | fs.height = c->screen->display_height; 700 | XConfigureWindow(dpy, c->window, 701 | CWX | CWY | CWWidth | CWHeight, &fs); 702 | XRaiseWindow(dpy,c->parent); 703 | } else { 704 | c->size.x = c->size.y = fs.x = fs.y = 0; 705 | c->size.width = fs.width = c->screen->display_width; 706 | c->size.height = fs.height = c->screen->display_height; 707 | XConfigureWindow(dpy, c->window, 708 | CWX | CWY | CWWidth | CWHeight, &fs); 709 | XRaiseWindow(dpy,c->window); 710 | } 711 | sendConfigureNotify(c); 712 | } 713 | 714 | extern void 715 | Client_ExitFullScreen(Client *c) { 716 | XWindowChanges fs; 717 | 718 | memcpy(&c->size, &c->return_size, sizeof(XSizeHints)); 719 | if (c->framed == True) { 720 | fs.x = c->size.x; 721 | fs.y = c->size.y - titleHeight(); 722 | fs.width = c->size.width; 723 | fs.height = c->size.height + titleHeight(); 724 | XConfigureWindow(dpy, c->parent, 725 | CWX | CWY | CWWidth | CWHeight, &fs); 726 | 727 | fs.x = border; 728 | fs.y = border + titleHeight(); 729 | fs.width = c->size.width -(2 * border); 730 | fs.height = c->size.height -(2 * border); 731 | XConfigureWindow(dpy, c->window, 732 | CWX | CWY | CWWidth | CWHeight, &fs); 733 | } else { 734 | fs.x = c->size.x; 735 | fs.y = c->size.y; 736 | fs.width = c->size.width; 737 | fs.height = c->size.height; 738 | XConfigureWindow(dpy, c->window, 739 | CWX | CWY | CWWidth | CWHeight, &fs); 740 | } 741 | sendConfigureNotify(c); 742 | } 743 | 744 | extern void 745 | Client_Focus(Client *c, Time time) { 746 | if (current) { 747 | setactive(current, 0, 0L); 748 | XDeleteProperty(dpy, current->screen->root, 749 | ewmh_atom[_NET_ACTIVE_WINDOW]); 750 | } 751 | 752 | if (!c && current) { 753 | last_focus = current; 754 | } else { 755 | last_focus = NULL; 756 | } 757 | current = c; 758 | if (c) { 759 | setactive(current, 1, time); 760 | XChangeProperty(dpy, current->screen->root, 761 | ewmh_atom[_NET_ACTIVE_WINDOW], 762 | XA_WINDOW, 32, PropModeReplace, 763 | (unsigned char *)¤t->window, 1); 764 | } 765 | 766 | if (focus_mode == focus_click) 767 | Client_Raise(c); 768 | } 769 | 770 | extern void 771 | Client_Name(Client *c, const char *name, Bool is_utf8) { 772 | int tx; 773 | static const char dots[] = " [...] "; 774 | int cut; 775 | 776 | if (c->name) free(c->name); 777 | c->name = sdup((char *) name); 778 | c->namelen = strlen(c->name); 779 | c->name_utf8 = is_utf8; 780 | 781 | if (c->menu_name) free(c->menu_name); 782 | c->menu_name = 0; 783 | tx = titleWidth(popup_font_set, c); 784 | if (tx <= (c->screen->display_width - (c->screen->display_width / 10))) 785 | return; 786 | 787 | /* the menu entry for this client will not fit on the display 788 | * (minus 10% for saftey), so produced a truncated version... 789 | */ 790 | cut = 5; 791 | do { 792 | if (c->menu_name) { 793 | free(c->menu_name); 794 | c->menu_name = 0; 795 | } 796 | if (cut >= (strlen(c->name) / 2)) break; 797 | c->menu_name = sdup(c->name); 798 | /* FIXME: this is not UTF-8 safe! */ 799 | sprintf(&c->menu_name[(strlen(c->name) / 2) - cut], dots); 800 | strcat(c->menu_name, 801 | &c->name[(strlen(c->name) / 2) + cut]); 802 | c->menu_namelen = strlen(c->menu_name); 803 | cut++; 804 | tx = titleWidth(popup_font_set, c); 805 | if (!tx) break; 806 | } while (tx > 807 | (c->screen->display_width - (c->screen->display_width / 10))); 808 | } 809 | -------------------------------------------------------------------------------- /cursor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "lwm.h" 28 | 29 | typedef struct CursorMapping CursorMapping; 30 | struct CursorMapping { 31 | Edge edge; 32 | int font_char; 33 | }; 34 | 35 | static CursorMapping cursor_map[] = { 36 | {ETopLeft, XC_top_left_corner}, 37 | {ETop, XC_top_side}, 38 | {ETopRight, XC_top_right_corner}, 39 | {ERight, XC_right_side}, 40 | {ENone, XC_fleur}, 41 | {ELeft, XC_left_side}, 42 | {EBottomLeft, XC_bottom_left_corner}, 43 | {EBottom, XC_bottom_side}, 44 | {EBottomRight, XC_bottom_right_corner}, 45 | 46 | {ENone, 0}, 47 | }; 48 | 49 | extern void 50 | initialiseCursors(int screen) { 51 | XColor red, white, exact; 52 | Colormap cmp; 53 | int i; 54 | 55 | cmp = DefaultColormap(dpy, screen); 56 | 57 | XAllocNamedColor(dpy, cmp, "red", &red, &exact); 58 | XAllocNamedColor(dpy, cmp, "white", &white, &exact); 59 | 60 | screens[screen].root_cursor = XCreateFontCursor(dpy, XC_left_ptr); 61 | XRecolorCursor(dpy, screens[screen].root_cursor, &red, &white); 62 | 63 | screens[screen].box_cursor = XCreateFontCursor(dpy, XC_draped_box); 64 | XRecolorCursor(dpy, screens[screen].box_cursor, &red, &white); 65 | 66 | for (i = 0; cursor_map[i].font_char != 0; i++) { 67 | Edge e = cursor_map[i].edge; 68 | screens[screen].cursor_map[e] = 69 | XCreateFontCursor(dpy, cursor_map[i].font_char); 70 | XRecolorCursor(dpy, screens[screen].cursor_map[e], 71 | &red, &white); 72 | } 73 | } 74 | 75 | extern Cursor 76 | getEdgeCursor(Edge edge) { 77 | return screens[0].cursor_map[edge]; 78 | } 79 | -------------------------------------------------------------------------------- /disp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "lwm.h" 31 | #include "ewmh.h" 32 | 33 | /* 34 | * Dispatcher for main event loop. 35 | */ 36 | typedef struct Disp Disp; 37 | struct Disp { 38 | int type; 39 | void (*handler)(XEvent *); 40 | }; 41 | 42 | static void expose(XEvent *); 43 | static void buttonpress(XEvent *); 44 | static void buttonrelease(XEvent *); 45 | static void focuschange(XEvent *); 46 | static void maprequest(XEvent *); 47 | static void configurereq(XEvent *); 48 | static void unmap(XEvent *); 49 | static void destroy(XEvent *); 50 | static void clientmessage(XEvent *); 51 | static void colormap(XEvent *); 52 | static void property(XEvent *); 53 | static void reparent(XEvent *); 54 | static void enter(XEvent *); 55 | static void circulaterequest(XEvent *); 56 | static void motionnotify(XEvent *); 57 | 58 | void reshaping_motionnotify(XEvent *); 59 | 60 | static Disp disps[] = 61 | { 62 | {Expose, expose}, 63 | {MotionNotify, motionnotify}, 64 | {ButtonPress, buttonpress}, 65 | {ButtonRelease, buttonrelease}, 66 | {FocusIn, focuschange}, 67 | {FocusOut, focuschange}, 68 | {MapRequest, maprequest}, 69 | {ConfigureRequest, configurereq}, 70 | {UnmapNotify, unmap}, 71 | {DestroyNotify, destroy}, 72 | {ClientMessage, clientmessage}, 73 | {ColormapNotify, colormap}, 74 | {PropertyNotify, property}, 75 | {ReparentNotify, reparent}, 76 | {EnterNotify, enter}, 77 | {CirculateRequest, circulaterequest}, 78 | {LeaveNotify, 0}, 79 | {ConfigureNotify, 0}, 80 | {CreateNotify, 0}, 81 | {GravityNotify, 0}, 82 | {MapNotify, 0}, 83 | {MappingNotify, 0}, 84 | {SelectionClear, 0}, 85 | {SelectionNotify, 0}, 86 | {SelectionRequest, 0}, 87 | {NoExpose, 0}, 88 | }; 89 | 90 | /** 91 | * pending it the client in which an action has been started by a mouse press 92 | * and we are waiting for the button to be released before performing the action 93 | */ 94 | static Client *pending=NULL; 95 | 96 | extern void 97 | dispatch(XEvent * ev) { 98 | Disp * p; 99 | 100 | for (p = disps; p < disps + sizeof(disps)/sizeof(disps[0]); p++) { 101 | if (p->type == ev->type) { 102 | if (p->handler != 0) 103 | p->handler(ev); 104 | return; 105 | } 106 | } 107 | 108 | if (!shapeEvent(ev)) 109 | fprintf(stderr, "%s: unknown event %d\n", argv0, ev->type); 110 | } 111 | 112 | static void 113 | expose(XEvent * ev) { 114 | Client * c; 115 | Window w; /* Window the expose event is for. */ 116 | 117 | /* Only handle the last in a group of Expose events. */ 118 | if (ev->xexpose.count != 0) return; 119 | 120 | w = ev->xexpose.window; 121 | 122 | /* 123 | * We don't draw on the root window so that people can have 124 | * their favourite Spice Girls backdrop... 125 | */ 126 | if (getScreenFromRoot(w) != 0) 127 | return; 128 | 129 | /* Decide what needs redrawing: window frame or menu? */ 130 | if (current_screen && w == current_screen->popup) { 131 | if (mode == wm_menu_up) 132 | menu_expose(); 133 | else if (mode == wm_reshaping && current != 0) 134 | size_expose(); 135 | } else { 136 | c = Client_Get(w); 137 | if (c != 0) { 138 | Client_DrawBorder(c, c == current); 139 | } 140 | } 141 | } 142 | 143 | static void 144 | buttonpress(XEvent *ev) { 145 | Client *c; 146 | XButtonEvent *e = &ev->xbutton; 147 | int quarter; 148 | 149 | /* If we're getting it already, we're not in the market for more. */ 150 | if (mode != wm_idle) { 151 | /* but allow a button press to cancel a move/resize, 152 | * to satify the EWMH advisory to allow a second mechanism 153 | * of completing move/resize operations, due to a race. 154 | * (section 4.3) sucky! 155 | */ 156 | if (mode == wm_reshaping) { 157 | mode = wm_idle; 158 | } 159 | return; 160 | } 161 | 162 | c = Client_Get(e->window); 163 | 164 | if (c && c != current && focus_mode == focus_click) { 165 | /* Click is not on current window, 166 | * and in click-to-focus mode, so change focus 167 | */ 168 | Client_Focus(c, e->time); 169 | } 170 | 171 | /*move this test up to disable scroll to focus*/ 172 | if (e->button >= 4 && e->button <= 7) { 173 | return; 174 | } 175 | 176 | if (c && c == current && (e->window == c->parent)) { 177 | /* Click went to our frame around a client. */ 178 | 179 | /* The ``box''. */ 180 | quarter = (border + titleHeight()) / 4; 181 | if (e->x > (quarter + 2) && e->x < (3 + 3*quarter) && e->y > quarter && e->y <= 3*quarter) { 182 | /*Client_Close(c);*/ 183 | pending = c; 184 | mode = wm_closing_window; 185 | return; 186 | } 187 | 188 | /* Somewhere in the rest of the frame. */ 189 | if (e->button == HIDE_BUTTON) { 190 | pending = c; 191 | mode = wm_hiding_window; 192 | return; 193 | } 194 | if (e->button == MOVE_BUTTON) { 195 | Client_Move(c); 196 | return; 197 | } 198 | if (e->button == RESHAPE_BUTTON) { 199 | XMapWindow(dpy, c->parent); 200 | Client_Raise(c); 201 | 202 | /* Lasciate ogni speranza voi ch'entrate... */ 203 | 204 | if (e->x <= border && e->y <= border) { 205 | Client_ReshapeEdge(c, ETopLeft); 206 | } else if (e->x >= (c->size.width - border) && e->y <= border) { 207 | Client_ReshapeEdge(c, ETopRight); 208 | } else if (e->x >= (c->size.width - border) && e->y >= (c->size.height + titleHeight() - border)) { 209 | Client_ReshapeEdge(c, EBottomRight); 210 | } else if (e->x <= border && e->y >= (c->size.height + titleHeight() - border)) { 211 | Client_ReshapeEdge(c, EBottomLeft); 212 | } else if (e->x > border && e->x < (c->size.width - border) && e->y < border) { 213 | Client_ReshapeEdge(c, ETop); 214 | } else if (e->x > border && e->x < (c->size.width - border) && e->y >= border && e->y < (titleHeight() + border)) { 215 | Client_Move(c); 216 | } else if (e->x > (c->size.width - border) && e->y > border && e->y < (c->size.height + titleHeight() - border)) { 217 | Client_ReshapeEdge(c, ERight); 218 | } else if (e->x > border && e->x < (c->size.width - border) && e->y > (c->size.height - border)) { 219 | Client_ReshapeEdge(c, EBottom); 220 | } else if (e->x < border && e->y > border && e->y < (c->size.height + titleHeight() - border)) { 221 | Client_ReshapeEdge(c, ELeft); 222 | } 223 | return; 224 | } 225 | return; 226 | } 227 | 228 | /* Deal with root window button presses. */ 229 | if (e->window == e->root) { 230 | if (e->button == Button3) { 231 | cmapfocus(0); 232 | menuhit(e); 233 | } else { 234 | shell(getScreenFromRoot(e->root), e->button, e->x, e->y); 235 | } 236 | } 237 | } 238 | 239 | static void 240 | buttonrelease(XEvent *ev) { 241 | XButtonEvent *e = &ev->xbutton; 242 | int quarter; 243 | 244 | if (mode == wm_menu_up) 245 | menu_buttonrelease(ev); 246 | else if (mode == wm_reshaping) 247 | XUnmapWindow(dpy, current_screen->popup); 248 | else if (mode == wm_closing_window) { 249 | /* was the button released within the window's box?*/ 250 | quarter = (border + titleHeight()) / 4; 251 | if (pending != NULL && 252 | (e->window == pending->parent) && 253 | (e->x > (quarter + 2) && 254 | e->x < (3 + 3*quarter) && 255 | e->y > quarter && e->y <= 3*quarter)) 256 | Client_Close(pending); 257 | pending = NULL; 258 | } else if (mode == wm_hiding_window) { 259 | /* was the button release within the window's frame? */ 260 | if (pending != NULL && 261 | (e->window == pending->parent) && 262 | (e->x >= 0) && (e->y >= 0) && 263 | (e->x <= pending->size.width) && 264 | (e->y <= (pending->size.height + titleHeight()))) { 265 | if (e->state & ShiftMask) { 266 | Client_Lower(pending); 267 | } else { 268 | hide(pending); 269 | } 270 | } 271 | pending = NULL; 272 | } 273 | 274 | mode = wm_idle; 275 | } 276 | 277 | static void circulaterequest(XEvent *ev) { 278 | XCirculateRequestEvent * e = &ev->xcirculaterequest; 279 | Client * c; 280 | 281 | c = Client_Get(e->window); 282 | 283 | if (c == 0) { 284 | if (e->place == PlaceOnTop) { 285 | XRaiseWindow(e->display, e->window); 286 | } else { 287 | XLowerWindow(e->display, e->window); 288 | } 289 | } else { 290 | if (e->place == PlaceOnTop) { 291 | Client_Raise(c); 292 | } else { 293 | Client_Lower(c); 294 | } 295 | } 296 | } 297 | 298 | static void 299 | maprequest(XEvent *ev) { 300 | Client * c; 301 | XMapRequestEvent * e = &ev->xmaprequest; 302 | 303 | c = Client_Get(e->window); 304 | 305 | if (c == 0 || c->window != e->window) { 306 | int screen; 307 | for (screen = 0; screen < screen_count; screen++) 308 | scanWindowTree(screen); 309 | c = Client_Get(e->window); 310 | if (c == 0 || c->window != e->window) { 311 | fprintf(stderr, "MapRequest for non-existent window!\n"); 312 | return; 313 | } 314 | } 315 | 316 | unhidec(c, 1); 317 | 318 | switch (c->state) { 319 | case WithdrawnState: 320 | if (getScreenFromRoot(c->parent) != 0) { 321 | manage(c, 0); 322 | break; 323 | } 324 | if (c->framed == True) { 325 | XReparentWindow(dpy, c->window, c->parent, border, 326 | border + titleHeight()); 327 | } else { 328 | XReparentWindow(dpy, c->window, c->parent, 329 | c->size.x, c->size.y); 330 | } 331 | XAddToSaveSet(dpy, c->window); 332 | /*FALLTHROUGH*/ 333 | case NormalState: 334 | XMapWindow(dpy, c->parent); 335 | XMapWindow(dpy, c->window); 336 | Client_Raise(c); 337 | Client_SetState(c, NormalState); 338 | break; 339 | } 340 | ewmh_set_client_list(c->screen); 341 | } 342 | 343 | static void 344 | unmap(XEvent *ev) { 345 | Client *c; 346 | XUnmapEvent *e = &ev->xunmap; 347 | 348 | c = Client_Get(e->window); 349 | if (c == 0) return; 350 | 351 | /* 352 | * In the description of the ReparentWindow request we read: "If the window 353 | * is mapped, an UnmapWindow request is performed automatically first". This 354 | * might seem stupid, but it's the way it is. While a reparenting is pending 355 | * we ignore UnmapWindow requests. 356 | */ 357 | if (c->internal_state == IPendingReparenting) { 358 | c->internal_state = INormal; 359 | return; 360 | } 361 | 362 | /* "This time it's the real thing." */ 363 | 364 | if (c->state == IconicState) { 365 | /* 366 | * Is this a hidden window disappearing? If not, then we 367 | * aren't interested because it's an unmap request caused 368 | * by our hiding a window. 369 | */ 370 | if (e->send_event) 371 | unhidec(c, 0); /* It's a hidden window disappearing. */ 372 | } else { 373 | /* This is a plain unmap, so withdraw the window. */ 374 | withdraw(c); 375 | } 376 | 377 | c->internal_state = INormal; 378 | } 379 | 380 | static void 381 | configurereq(XEvent *ev) { 382 | XWindowChanges wc; 383 | Client *c; 384 | XConfigureRequestEvent *e = &ev->xconfigurerequest; 385 | 386 | c = Client_Get(e->window); 387 | 388 | 389 | if (c && c->window == e->window) { 390 | /* 391 | * ICCCM section 4.1.5 says that the x and y coordinates here 392 | * will have been "adjusted for the border width". 393 | * NOTE: this may not be the only place to bear this in mind. 394 | */ 395 | if (e->value_mask & CWBorderWidth) { 396 | e->x -= e->border_width; 397 | e->y -= e->border_width; 398 | } else { 399 | /* 400 | * The ICCCM also says that clients should always set the 401 | * border width in a configure request. As usual, many don't. 402 | */ 403 | /* adding one seems a bit arbitrary and makes edit 404 | drift by one pixel*/ 405 | /*e->x--;*/ 406 | /*e->y--;*/ 407 | } 408 | 409 | if (e->value_mask & CWX) { 410 | c->size.x = e->x; 411 | } 412 | if (e->value_mask & CWY) { 413 | c->size.y = e->y; 414 | if (c->framed == True) 415 | c->size.y += titleHeight(); 416 | } 417 | if (e->value_mask & CWWidth) { 418 | c->size.width = e->width; 419 | if (c->framed == True) 420 | c->size.width += 2 * border; 421 | } 422 | if (e->value_mask & CWHeight) { 423 | c->size.height = e->height; 424 | if (c->framed == True) 425 | c->size.height += 2 * border; 426 | } 427 | if (e->value_mask & CWBorderWidth) 428 | c->border = e->border_width; 429 | 430 | if (getScreenFromRoot(c->parent) == 0) { 431 | wc.x = c->size.x; 432 | wc.y = c->size.y; 433 | if (c->framed == True) 434 | wc.y -= titleHeight(); 435 | wc.width = c->size.width; 436 | wc.height = c->size.height; 437 | if (c->framed == True) 438 | wc.height += titleHeight(); 439 | wc.border_width = 1; 440 | wc.sibling = e->above; 441 | wc.stack_mode = e->detail; 442 | 443 | XConfigureWindow(dpy, e->parent, e->value_mask, &wc); 444 | sendConfigureNotify(c); 445 | } 446 | } 447 | if (c && (c->internal_state == INormal) && (c->framed == True)) { 448 | wc.x = border; 449 | wc.y = border; 450 | } else { 451 | wc.x = e->x; 452 | wc.y = e->y; 453 | } 454 | 455 | wc.width = e->width; 456 | wc.height = e->height; 457 | wc.border_width = 0; 458 | wc.sibling = e->above; 459 | wc.stack_mode = e->detail; 460 | e->value_mask |= CWBorderWidth; 461 | 462 | XConfigureWindow(dpy, e->window, e->value_mask, &wc); 463 | 464 | if (c) { 465 | if (c->framed == True) { 466 | XMoveResizeWindow(dpy, c->parent, 467 | c->size.x, c->size.y - titleHeight(), 468 | c->size.width, c->size.height + titleHeight()); 469 | XMoveWindow(dpy, c->window, 470 | border, border + titleHeight()); 471 | } else { 472 | XMoveResizeWindow(dpy, c->window, 473 | c->size.x, c->size.y, 474 | c->size.width, c->size.height); 475 | } 476 | } 477 | } 478 | 479 | static void 480 | destroy(XEvent *ev) { 481 | Client * c; 482 | Window w = ev->xdestroywindow.window; 483 | 484 | c = Client_Get(w); 485 | if (c == 0) 486 | return; 487 | 488 | ignore_badwindow = 1; 489 | Client_Remove(c); 490 | ignore_badwindow = 0; 491 | } 492 | 493 | static void 494 | clientmessage(XEvent *ev) { 495 | Client * c; 496 | XClientMessageEvent * e = &ev->xclient; 497 | 498 | c = Client_Get(e->window); 499 | if (c == 0) return; 500 | if (e->message_type == wm_change_state) { 501 | if (e->format == 32 && e->data.l[0] == IconicState && normal(c)) 502 | hide(c); 503 | return; 504 | } 505 | if (e->message_type == ewmh_atom[_NET_WM_STATE] && 506 | e->format == 32) { 507 | ewmh_change_state(c, e->data.l[0], e->data.l[1]); 508 | ewmh_change_state(c, e->data.l[0], e->data.l[2]); 509 | return; 510 | } 511 | if (e->message_type == ewmh_atom[_NET_ACTIVE_WINDOW] && 512 | e->format == 32) { 513 | /* An EWMH enabled application has asked for this client 514 | * to be made the active window. The window is raised, and 515 | * focus given if the focus mode is click (focusing on a 516 | * window other than the one the pointer is in makes no 517 | * sense when the focus mode is enter). 518 | */ 519 | if (hidden(c)) unhidec(c,1); 520 | XMapWindow(dpy, c->parent); 521 | Client_Raise(c); 522 | if (c != current && focus_mode == focus_click) 523 | Client_Focus(c, CurrentTime); 524 | return; 525 | } 526 | if (e->message_type == ewmh_atom[_NET_CLOSE_WINDOW] && 527 | e->format == 32) { 528 | Client_Close(c); 529 | return; 530 | } 531 | if (e->message_type == ewmh_atom[_NET_MOVERESIZE_WINDOW] && 532 | e->format == 32) { 533 | XEvent ev; 534 | 535 | /* FIXME: ok, so this is a bit of a hack */ 536 | ev.xconfigurerequest.window = e->window; 537 | ev.xconfigurerequest.x = e->data.l[1]; 538 | ev.xconfigurerequest.y = e->data.l[2]; 539 | ev.xconfigurerequest.width = e->data.l[3]; 540 | ev.xconfigurerequest.height = e->data.l[4]; 541 | ev.xconfigurerequest.value_mask = 0; 542 | if (e->data.l[0] & (1 << 8)) 543 | ev.xconfigurerequest.value_mask |= CWX; 544 | if (e->data.l[0] & (1 << 9)) 545 | ev.xconfigurerequest.value_mask |= CWY; 546 | if (e->data.l[0] & (1 << 10)) 547 | ev.xconfigurerequest.value_mask |= CWWidth; 548 | if (e->data.l[0] & (1 << 11)) 549 | ev.xconfigurerequest.value_mask |= CWHeight; 550 | configurereq(&ev); 551 | return; 552 | } 553 | if (e->message_type == ewmh_atom[_NET_WM_MOVERESIZE] && 554 | e->format == 32) { 555 | Edge edge = E_LAST; 556 | EWMHDirection direction = e->data.l[2]; 557 | /* 558 | int x_root = e->data.l[0]; 559 | int y_root = e->data.l[1]; 560 | */ 561 | 562 | 563 | /* before we can do any resizing, make the window visible */ 564 | if (hidden(c)) unhidec(c,1); 565 | XMapWindow(dpy, c->parent); 566 | Client_Raise(c); 567 | /* FIXME: we're ignorning x_root, y_root and button! */ 568 | switch (direction) { 569 | case DSizeTopLeft: 570 | edge = ETopLeft; 571 | break; 572 | case DSizeTop: 573 | edge = ETop; 574 | break; 575 | case DSizeTopRight: 576 | edge = ETopRight; 577 | break; 578 | case DSizeRight: 579 | edge = ERight; 580 | break; 581 | case DSizeBottomRight: 582 | edge = EBottomRight; 583 | break; 584 | case DSizeBottom: 585 | edge = EBottom; 586 | break; 587 | case DSizeBottomLeft: 588 | edge = EBottomLeft; 589 | break; 590 | case DSizeLeft: 591 | edge = ELeft; 592 | break; 593 | case DMove: 594 | edge = ENone; 595 | break; 596 | case DSizeKeyboard: 597 | /* FIXME: don't know how to deal with this */ 598 | edge = E_LAST; 599 | break; 600 | case DMoveKeyboard: 601 | #if 0 602 | /* need to do a release and this is too broken for that */ 603 | /* don't believe i'm doing this. mouse warping 604 | * sucks! 605 | */ 606 | XWarpPointer(dpy, c->screen->root, c->window, 607 | x_root, y_root, 608 | c->screen->display_width, 609 | c->screen->display_height, 610 | c->size.width / 2, c->size.height / 2); 611 | edge = ENone; 612 | #endif 613 | edge = E_LAST; 614 | break; 615 | default: 616 | edge = E_LAST; 617 | fprintf(stderr, "%s: received _NET_WM_MOVERESIZE" 618 | " with bad direction", argv0); 619 | break; 620 | } 621 | switch (edge) { 622 | case E_LAST: 623 | break; 624 | case ENone: 625 | Client_Move(c); 626 | break; 627 | default: 628 | Client_ReshapeEdge(c, edge); 629 | break; 630 | } 631 | } 632 | } 633 | 634 | static void 635 | colormap(XEvent *ev) { 636 | Client * c; 637 | XColormapEvent * e = &ev->xcolormap; 638 | 639 | if (e->new) { 640 | c = Client_Get(e->window); 641 | if (c) { 642 | c->cmap = e->colormap; 643 | if (c == current) 644 | cmapfocus(c); 645 | } else { 646 | Client_ColourMap(ev); 647 | } 648 | } 649 | } 650 | 651 | static void 652 | property(XEvent * ev) { 653 | Client * c; 654 | XPropertyEvent * e = &ev->xproperty; 655 | 656 | c = Client_Get(e->window); 657 | if (c == 0) 658 | return; 659 | 660 | if (e->atom == _mozilla_url || e->atom == XA_WM_NAME) { 661 | getWindowName(c); 662 | setactive(c, c == current, 0L); 663 | } else if (e->atom == XA_WM_TRANSIENT_FOR) { 664 | getTransientFor(c); 665 | } else if (e->atom == XA_WM_NORMAL_HINTS) { 666 | getNormalHints(c); 667 | } else if (e->atom == wm_colormaps) { 668 | getColourmaps(c); 669 | if (c == current) 670 | cmapfocus(c); 671 | } else if (e->atom == ewmh_atom[_NET_WM_STRUT]) { 672 | ewmh_get_strut(c); 673 | } else if (e->atom == ewmh_atom[_NET_WM_STATE]) { 674 | // Received notice that client wants to change its state 675 | // update internal wstate tracking 676 | Bool wasFullscreen = c->wstate.fullscreen; 677 | ewmh_get_state(c); 678 | // make any changes requested 679 | if (c->wstate.fullscreen == True && wasFullscreen == False) Client_EnterFullScreen(c); 680 | else if (c->wstate.fullscreen == False && wasFullscreen == True) Client_ExitFullScreen(c); 681 | } 682 | } 683 | 684 | static void 685 | reparent(XEvent *ev) { 686 | Client * c; 687 | XReparentEvent * e = &ev->xreparent; 688 | 689 | if (getScreenFromRoot(e->event) == 0 || e->override_redirect || getScreenFromRoot(e->parent) != 0) 690 | return; 691 | 692 | c = Client_Get(e->window); 693 | if (c != 0 && (getScreenFromRoot(c->parent) != 0 || withdrawn(c))) 694 | Client_Remove(c); 695 | } 696 | 697 | static void 698 | focuschange(XEvent *ev) { 699 | Client *c; 700 | Window focus_window; 701 | int revert_to; 702 | 703 | if (ev->type == FocusOut) return; 704 | 705 | XGetInputFocus(dpy, &focus_window, &revert_to); 706 | if (focus_window == PointerRoot || focus_window == None) { 707 | if (current) Client_Focus(NULL, CurrentTime); 708 | return; 709 | } 710 | c = Client_Get(focus_window); 711 | if (c && c != current) { 712 | Client_Focus(c, CurrentTime); 713 | } 714 | return; 715 | } 716 | 717 | static void 718 | enter(XEvent *ev) { 719 | Client *c; 720 | 721 | c = Client_Get(ev->xcrossing.window); 722 | if (c == 0 || mode != wm_idle) 723 | return; 724 | 725 | if (c->framed == True) { 726 | XSetWindowAttributes attr; 727 | 728 | attr.cursor = c->screen->root_cursor; 729 | XChangeWindowAttributes(dpy, c->parent, 730 | CWCursor, &attr); 731 | c->cursor = ENone; 732 | } 733 | if (c != current && !c->hidden && focus_mode == focus_enter) { 734 | /* Entering a new window in enter focus mode, so take focus */ 735 | Client_Focus(c, ev->xcrossing.time); 736 | } 737 | } 738 | 739 | static void 740 | motionnotify(XEvent *ev) { 741 | if (mode == wm_reshaping) 742 | reshaping_motionnotify(ev); 743 | else if (mode == wm_menu_up) 744 | menu_motionnotify(ev); 745 | else if (mode == wm_idle) { 746 | XMotionEvent *e = &ev->xmotion; 747 | Client *c = Client_Get(e->window); 748 | Edge edge = ENone; 749 | int quarter = (border + titleHeight()) / 4; 750 | 751 | if (c && (e->window == c->parent) && 752 | (e->subwindow != c->window) && 753 | mode == wm_idle) { 754 | /* mouse moved in a frame we manage - check cursor */ 755 | if (e->x > (quarter + 2) 756 | && e->x < (3 + 3*quarter) 757 | && e->y > quarter && e->y <= 3*quarter) { 758 | edge = E_LAST; 759 | } else if (e->x <= border && e->y <= border) { 760 | edge = ETopLeft; 761 | } else if (e->x >= (c->size.width - border) 762 | && e->y <= border) { 763 | edge = ETopRight; 764 | } else if (e->x >= (c->size.width - border) 765 | && e->y >= 766 | (c->size.height + titleHeight() - border)) { 767 | edge = EBottomRight; 768 | } else if (e->x <= border && 769 | e->y >= 770 | (c->size.height + titleHeight() - border)) { 771 | edge = EBottomLeft; 772 | } else if (e->x > border && 773 | e->x < (c->size.width - border) 774 | && e->y < border) { 775 | edge = ETop; 776 | } else if (e->x > border && 777 | e->x < (c->size.width - border) 778 | && e->y >= border 779 | && e->y < (titleHeight() + border)) { 780 | edge = ENone; 781 | } else if (e->x > (c->size.width - border) 782 | && e->y > border 783 | && e->y < 784 | (c->size.height + titleHeight() - border)) { 785 | edge = ERight; 786 | } else if (e->x > border 787 | && e->x < (c->size.width - border) 788 | && e->y > (c->size.height - border)) { 789 | edge = EBottom; 790 | } else if (e->x < border 791 | && e->y > border 792 | && e->y < 793 | (c->size.height + titleHeight() - border)) { 794 | edge = ELeft; 795 | } 796 | if (c->cursor != edge) { 797 | XSetWindowAttributes attr; 798 | 799 | if (edge == ENone) { 800 | attr.cursor = 801 | c->screen->root_cursor; 802 | } else if (edge == E_LAST) { 803 | attr.cursor = 804 | c->screen->box_cursor; 805 | } else { 806 | attr.cursor = 807 | c->screen->cursor_map[edge]; 808 | } 809 | XChangeWindowAttributes(dpy, c->parent, 810 | CWCursor, &attr); 811 | c->cursor = edge; 812 | } 813 | } 814 | } 815 | } 816 | 817 | /*ARGSUSED*/ 818 | void 819 | reshaping_motionnotify(XEvent* ev) { 820 | int nx; /* New x. */ 821 | int ny; /* New y. */ 822 | int ox; /* Original x. */ 823 | int oy; /* Original y. */ 824 | int ndx; /* New width. */ 825 | int ndy; /* New height. */ 826 | int odx; /* Original width. */ 827 | int ody; /* Original height. */ 828 | int pointer_x; 829 | int pointer_y; 830 | 831 | if (mode != wm_reshaping || !current) return; 832 | 833 | getMousePosition(&pointer_x, &pointer_y); 834 | 835 | if (interacting_edge != ENone) { 836 | nx = ox = current->size.x; 837 | ny = oy = current->size.y; 838 | ndx = odx = current->size.width; 839 | ndy = ody = current->size.height; 840 | 841 | Client_SizeFeedback(); 842 | 843 | /* Vertical. */ 844 | switch (interacting_edge) { 845 | case ETop: 846 | case ETopLeft: 847 | case ETopRight: 848 | pointer_y += titleHeight(); 849 | ndy += (current->size.y - pointer_y); 850 | ny = pointer_y; 851 | break; 852 | case EBottom: 853 | case EBottomLeft: 854 | case EBottomRight: 855 | ndy = pointer_y - current->size.y; 856 | break; 857 | default: break; 858 | } 859 | 860 | /* Horizontal. */ 861 | switch (interacting_edge) { 862 | case ERight: 863 | case ETopRight: 864 | case EBottomRight: 865 | ndx = pointer_x - current->size.x; 866 | break; 867 | case ELeft: 868 | case ETopLeft: 869 | case EBottomLeft: 870 | ndx += (current->size.x - pointer_x); 871 | nx = pointer_x; 872 | break; 873 | default: break; 874 | } 875 | 876 | Client_MakeSane(current, interacting_edge, &nx, &ny, &ndx, &ndy); 877 | XMoveResizeWindow(dpy, current->parent, 878 | current->size.x, current->size.y - titleHeight(), 879 | current->size.width, current->size.height + titleHeight()); 880 | if (current->size.width == odx && current->size.height == ody) { 881 | if (current->size.x != ox || current->size.y != oy) 882 | sendConfigureNotify(current); 883 | } else 884 | XMoveResizeWindow(dpy, current->window, 885 | border, border + titleHeight(), 886 | current->size.width - 2 * border, 887 | current->size.height - 2 * border); 888 | } else { 889 | nx = pointer_x + start_x; 890 | ny = pointer_y + start_y; 891 | 892 | Client_MakeSane(current, interacting_edge, &nx, &ny, 0, 0); 893 | if (current->framed == True) { 894 | XMoveWindow(dpy, current->parent, 895 | current->size.x, 896 | current->size.y - titleHeight()); 897 | } else { 898 | XMoveWindow(dpy, current->parent, 899 | current->size.x, current->size.y); 900 | } 901 | sendConfigureNotify(current); 902 | } 903 | } 904 | -------------------------------------------------------------------------------- /error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "lwm.h" 29 | 30 | int ignore_badwindow; 31 | 32 | void 33 | panic(char *s) { 34 | fprintf(stderr, "%s: %s\n", argv0, s); 35 | exit(EXIT_FAILURE); 36 | } 37 | 38 | int 39 | errorHandler(Display *d, XErrorEvent *e) { 40 | char msg[80]; 41 | char req[80]; 42 | char number[80]; 43 | 44 | if (mode == wm_initialising && 45 | e->request_code == X_ChangeWindowAttributes && 46 | e->error_code == BadAccess) 47 | panic("another window manager is already running."); 48 | 49 | if (ignore_badwindow && 50 | (e->error_code == BadWindow || e->error_code == BadColor)) 51 | return 0; 52 | 53 | XGetErrorText(d, e->error_code, msg, sizeof(msg)); 54 | sprintf(number, "%d", e->request_code); 55 | XGetErrorDatabaseText(d, "XRequest", number, number, req, sizeof(req)); 56 | 57 | fprintf(stderr, "%s: protocol request %s on resource %#x failed: %s\n", 58 | argv0, req, (unsigned int) e->resourceid, msg); 59 | 60 | if (mode == wm_initialising) 61 | panic("can't initialise."); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /ewmh.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "lwm.h" 30 | #include "ewmh.h" 31 | 32 | Atom ewmh_atom[EWMH_ATOM_LAST]; 33 | Atom utf8_string; 34 | 35 | void 36 | ewmh_init(void) { 37 | /* build half a million EWMH atoms */ 38 | ewmh_atom[_NET_SUPPORTED] = 39 | XInternAtom(dpy, "_NET_SUPPORTED", False); 40 | ewmh_atom[_NET_CLIENT_LIST] = 41 | XInternAtom(dpy, "_NET_CLIENT_LIST", False); 42 | ewmh_atom[_NET_CLIENT_LIST_STACKING] = 43 | XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); 44 | ewmh_atom[_NET_NUMBER_OF_DESKTOPS] = 45 | XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); 46 | ewmh_atom[_NET_DESKTOP_GEOMETRY] = 47 | XInternAtom(dpy, "_NET_DESKTOP_GEOMETRY", False); 48 | ewmh_atom[_NET_DESKTOP_VIEWPORT] = 49 | XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); 50 | ewmh_atom[_NET_CURRENT_DESKTOP] = 51 | XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); 52 | ewmh_atom[_NET_ACTIVE_WINDOW] = 53 | XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 54 | ewmh_atom[_NET_WORKAREA] = 55 | XInternAtom(dpy, "_NET_WORKAREA", False); 56 | ewmh_atom[_NET_SUPPORTING_WM_CHECK] = 57 | XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 58 | ewmh_atom[_NET_CLOSE_WINDOW] = 59 | XInternAtom(dpy, "_NET_CLOSE_WINDOW", False); 60 | ewmh_atom[_NET_MOVERESIZE_WINDOW] = 61 | XInternAtom(dpy, "_NET_MOVERESIZE_WINDOW", False); 62 | ewmh_atom[_NET_WM_MOVERESIZE] = 63 | XInternAtom(dpy, "_NET_WM_MOVERESIZE", False); 64 | ewmh_atom[_NET_WM_NAME] = 65 | XInternAtom(dpy, "_NET_WM_NAME", False); 66 | ewmh_atom[_NET_WM_WINDOW_TYPE] = 67 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 68 | ewmh_atom[_NET_WM_STATE] = 69 | XInternAtom(dpy, "_NET_WM_STATE", False); 70 | ewmh_atom[_NET_WM_ALLOWED_ACTIONS] = 71 | XInternAtom(dpy, "_NET_WM_ALLOWED_ACTIONS", False); 72 | ewmh_atom[_NET_WM_STRUT] = 73 | XInternAtom(dpy, "_NET_WM_STRUT", False); 74 | ewmh_atom[_NET_WM_WINDOW_TYPE_DESKTOP] = 75 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); 76 | ewmh_atom[_NET_WM_WINDOW_TYPE_DOCK] = 77 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); 78 | ewmh_atom[_NET_WM_WINDOW_TYPE_TOOLBAR] = 79 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); 80 | ewmh_atom[_NET_WM_WINDOW_TYPE_MENU] = 81 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False); 82 | ewmh_atom[_NET_WM_WINDOW_TYPE_UTILITY] = 83 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False); 84 | ewmh_atom[_NET_WM_WINDOW_TYPE_SPLASH] = 85 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); 86 | ewmh_atom[_NET_WM_WINDOW_TYPE_DIALOG] = 87 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 88 | ewmh_atom[_NET_WM_WINDOW_TYPE_NORMAL] = 89 | XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); 90 | ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR] = 91 | XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False); 92 | ewmh_atom[_NET_WM_STATE_SKIP_PAGER] = 93 | XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False); 94 | ewmh_atom[_NET_WM_STATE_HIDDEN] = 95 | XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False); 96 | ewmh_atom[_NET_WM_STATE_FULLSCREEN] = 97 | XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 98 | ewmh_atom[_NET_WM_ACTION_MOVE] = 99 | XInternAtom(dpy, "_NET_WM_ACTION_MOVE", False); 100 | ewmh_atom[_NET_WM_ACTION_RESIZE] = 101 | XInternAtom(dpy, "_NET_WM_ACTION_RESIZE", False); 102 | ewmh_atom[_NET_WM_ACTION_FULLSCREEN] = 103 | XInternAtom(dpy, "_NET_WM_ACTION_FULLSCREEN", False); 104 | ewmh_atom[_NET_WM_ACTION_CLOSE] = 105 | XInternAtom(dpy, "_NET_WM_ACTION_CLOSE", False); 106 | utf8_string = XInternAtom(dpy, "UTF8_STRING", False); 107 | } 108 | 109 | void 110 | ewmh_init_screens(void) { 111 | int i; 112 | unsigned long data[4]; 113 | 114 | /* announce EWMH compatibility on all acreens */ 115 | for (i = 0; i < screen_count; i++) { 116 | screens[i].ewmh_set_client_list = False; 117 | screens[i].ewmh_compat = XCreateSimpleWindow(dpy, 118 | screens[i].root, 119 | -200, -200, 1, 1, 120 | 0, 0, 0); 121 | XChangeProperty(dpy, screens[i].ewmh_compat, 122 | ewmh_atom[_NET_WM_NAME], 123 | utf8_string, 8, PropModeReplace, 124 | "lwm", 3); 125 | 126 | /* set root window properties */ 127 | XChangeProperty(dpy, screens[i].root, 128 | ewmh_atom[_NET_SUPPORTED], 129 | XA_ATOM, 32, PropModeReplace, 130 | (unsigned char *)ewmh_atom, EWMH_ATOM_LAST); 131 | 132 | XChangeProperty(dpy, screens[i].root, 133 | ewmh_atom[_NET_SUPPORTING_WM_CHECK], 134 | XA_WINDOW, 32, PropModeReplace, 135 | (unsigned char *)&screens[i].ewmh_compat, 1); 136 | 137 | data[0] = 1; 138 | XChangeProperty(dpy, screens[i].root, 139 | ewmh_atom[_NET_NUMBER_OF_DESKTOPS], 140 | XA_CARDINAL, 32, PropModeReplace, 141 | (unsigned char*)data, 1); 142 | 143 | data[0] = screens[i].display_width; 144 | data[1] = screens[i].display_height; 145 | XChangeProperty(dpy, screens[i].root, 146 | ewmh_atom[_NET_DESKTOP_GEOMETRY], 147 | XA_CARDINAL, 32, PropModeReplace, 148 | (unsigned char*)data, 2); 149 | 150 | data[0] = 0; 151 | data[1] = 0; 152 | XChangeProperty(dpy, screens[i].root, 153 | ewmh_atom[_NET_DESKTOP_VIEWPORT], 154 | XA_CARDINAL, 32, PropModeReplace, 155 | (unsigned char*)data, 2); 156 | 157 | data[0] = 0; 158 | XChangeProperty(dpy, screens[i].root, 159 | ewmh_atom[_NET_CURRENT_DESKTOP], 160 | XA_CARDINAL, 32, PropModeReplace, 161 | (unsigned char*)data, 1); 162 | 163 | ewmh_set_strut(&screens[i]); 164 | ewmh_set_client_list(&screens[i]); 165 | } 166 | } 167 | 168 | EWMHWindowType 169 | ewmh_get_window_type(Window w) { 170 | Atom rt; 171 | Atom *type; 172 | int fmt; 173 | unsigned long n; 174 | unsigned long extra; 175 | int i; 176 | EWMHWindowType ret; 177 | 178 | i = XGetWindowProperty(dpy, w, 179 | ewmh_atom[_NET_WM_WINDOW_TYPE], 180 | 0, 100, False, XA_ATOM, &rt, &fmt, &n, &extra, 181 | (unsigned char **)&type); 182 | if (i != Success || type == NULL) 183 | return WTypeNone; 184 | ret = WTypeNone; 185 | for (; n; n--) { 186 | if (type[n - 1] == 187 | ewmh_atom[_NET_WM_WINDOW_TYPE_DESKTOP]) { 188 | ret = WTypeDesktop; 189 | break; 190 | } 191 | if (type[n - 1] == 192 | ewmh_atom[_NET_WM_WINDOW_TYPE_DOCK]) { 193 | ret = WTypeDock; 194 | break; 195 | } 196 | if (type[n - 1] == 197 | ewmh_atom[_NET_WM_WINDOW_TYPE_TOOLBAR]) { 198 | ret = WTypeToolbar; 199 | break; 200 | } 201 | if (type[n - 1] == 202 | ewmh_atom[_NET_WM_WINDOW_TYPE_MENU]) { 203 | ret = WTypeMenu; 204 | break; 205 | } 206 | if (type[n - 1] == 207 | ewmh_atom[_NET_WM_WINDOW_TYPE_UTILITY]) { 208 | ret = WTypeUtility; 209 | break; 210 | } 211 | if (type[n - 1] == 212 | ewmh_atom[_NET_WM_WINDOW_TYPE_SPLASH]) { 213 | ret = WTypeSplash; 214 | break; 215 | } 216 | if (type[n - 1] == 217 | ewmh_atom[_NET_WM_WINDOW_TYPE_DIALOG]) { 218 | ret = WTypeDialog; 219 | break; 220 | } 221 | if (type[n - 1] == 222 | ewmh_atom[_NET_WM_WINDOW_TYPE_NORMAL]) { 223 | ret = WTypeNormal; 224 | break; 225 | } 226 | } 227 | XFree(type); 228 | return ret; 229 | } 230 | 231 | 232 | Bool ewmh_get_window_name(Client *c) { 233 | #ifdef X_HAVE_UTF8_STRING 234 | Atom rt; 235 | char *name; 236 | int fmt; 237 | unsigned long n; 238 | unsigned long extra; 239 | int i; 240 | 241 | i = XGetWindowProperty(dpy, c->window, 242 | ewmh_atom[_NET_WM_NAME], 243 | 0, 100, False, utf8_string, &rt, &fmt, &n, &extra, 244 | (unsigned char **)&name); 245 | if (i != Success || name == NULL) 246 | return False; 247 | Client_Name(c, name, True); 248 | XFree(name); 249 | return True; 250 | #else 251 | return False; 252 | #endif 253 | } 254 | 255 | Bool 256 | ewmh_hasframe(Client *c) { 257 | switch (c->wtype) { 258 | case WTypeDesktop: 259 | case WTypeDock: 260 | case WTypeMenu: 261 | case WTypeSplash: 262 | return False; 263 | default: 264 | return True; 265 | } 266 | } 267 | 268 | void 269 | ewmh_get_state(Client *c) { 270 | Atom rt; 271 | Atom *state; 272 | int fmt; 273 | unsigned long n; 274 | unsigned long extra; 275 | int i; 276 | 277 | if (c == NULL) return; 278 | i = XGetWindowProperty(dpy, c->window, 279 | ewmh_atom[_NET_WM_STATE], 280 | 0, 100, False, XA_ATOM, &rt, &fmt, &n, &extra, 281 | (unsigned char **)&state); 282 | if (i != Success || state == NULL) return; 283 | c->wstate.skip_taskbar = False; 284 | c->wstate.skip_pager = False; 285 | c->wstate.fullscreen = False; 286 | c->wstate.above = False; 287 | c->wstate.below = False; 288 | for (; n; n--) { 289 | if (state[n - 1] == 290 | ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR]) 291 | c->wstate.skip_taskbar = True; 292 | if (state[n - 1] == 293 | ewmh_atom[_NET_WM_STATE_SKIP_PAGER]) 294 | c->wstate.skip_pager = True; 295 | if (state[n - 1] == 296 | ewmh_atom[_NET_WM_STATE_FULLSCREEN]) 297 | c->wstate.fullscreen = True; 298 | if (state[n - 1] == 299 | ewmh_atom[_NET_WM_STATE_ABOVE]) 300 | c->wstate.above = True; 301 | if (state[n - 1] == 302 | ewmh_atom[_NET_WM_STATE_BELOW]) 303 | c->wstate.below = True; 304 | } 305 | XFree(state); 306 | } 307 | 308 | static Bool 309 | new_state(unsigned long action, Bool current) 310 | { 311 | enum Action {remove, add, toggle}; 312 | 313 | switch (action) { 314 | case remove: 315 | return False; 316 | case add: 317 | return True; 318 | case toggle: 319 | if (current == True) return False; else return True; 320 | } 321 | fprintf(stderr,"%s: bad action in _NET_WM_STATE (%d)\n", 322 | argv0, (int) action); 323 | return current; 324 | } 325 | 326 | void 327 | ewmh_change_state(Client *c, unsigned long action, 328 | unsigned long atom) { 329 | Atom *a = (Atom *)&atom; 330 | 331 | if (atom == 0) return; 332 | if (*a == ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR]) 333 | c->wstate.skip_taskbar = 334 | new_state(action, c->wstate.skip_taskbar); 335 | if (*a == ewmh_atom[_NET_WM_STATE_SKIP_PAGER]) 336 | c->wstate.skip_pager = 337 | new_state(action, c->wstate.skip_pager); 338 | if (*a == ewmh_atom[_NET_WM_STATE_FULLSCREEN]) { 339 | Bool was_fullscreen = c->wstate.fullscreen; 340 | 341 | c->wstate.fullscreen = 342 | new_state(action, c->wstate.fullscreen); 343 | if (was_fullscreen == False && 344 | c->wstate.fullscreen == True) Client_EnterFullScreen(c); 345 | if (was_fullscreen == True && 346 | c->wstate.fullscreen == False) Client_ExitFullScreen(c); 347 | } 348 | if (*a == ewmh_atom[_NET_WM_STATE_ABOVE]) 349 | c->wstate.above = 350 | new_state(action, c->wstate.above); 351 | if (*a == ewmh_atom[_NET_WM_STATE_BELOW]) 352 | c->wstate.below = 353 | new_state(action, c->wstate.below); 354 | ewmh_set_state(c); 355 | 356 | /* may have to shuffle windows in the stack after a change of state */ 357 | ewmh_set_client_list(c->screen); 358 | } 359 | 360 | void 361 | ewmh_set_state(Client *c) { 362 | int atoms = 0; 363 | Atom *a = NULL; 364 | int i = 0; 365 | 366 | if (c == NULL) return; 367 | 368 | if (c->state != WithdrawnState) { 369 | if (c->hidden == True) atoms++; 370 | if (c->wstate.skip_taskbar == True) atoms++; 371 | if (c->wstate.skip_pager == True) atoms++; 372 | if (c->wstate.fullscreen == True) atoms++; 373 | if (c->wstate.above == True) atoms++; 374 | if (c->wstate.below == True) atoms++; 375 | if (atoms > 0) a = malloc(sizeof(Atom) * atoms); 376 | 377 | if (c->hidden == True) { 378 | a[i] = ewmh_atom[_NET_WM_STATE_HIDDEN]; 379 | i++; 380 | } 381 | if (c->wstate.skip_taskbar == True) { 382 | a[i] = ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR]; 383 | i++; 384 | } 385 | if (c->wstate.skip_pager == True) { 386 | a[i] = ewmh_atom[_NET_WM_STATE_SKIP_PAGER]; 387 | i++; 388 | } 389 | if (c->wstate.fullscreen == True) { 390 | a[i] = ewmh_atom[_NET_WM_STATE_FULLSCREEN]; 391 | i++; 392 | } 393 | if (c->wstate.above == True) { 394 | a[i] = ewmh_atom[_NET_WM_STATE_ABOVE]; 395 | i++; 396 | } 397 | if (c->wstate.below == True) { 398 | a[i] = ewmh_atom[_NET_WM_STATE_BELOW]; 399 | i++; 400 | } 401 | } 402 | 403 | XChangeProperty(dpy, c->window, ewmh_atom[_NET_WM_STATE], 404 | XA_ATOM, 32, PropModeReplace, (unsigned char *)a, atoms); 405 | if (a != NULL) free(a); 406 | 407 | } 408 | 409 | void 410 | ewmh_set_allowed(Client *c) 411 | { 412 | /* FIXME: this is dumb - the allowed actions should be calculuated 413 | * but for now, anything goes. 414 | */ 415 | Atom action[4]; 416 | 417 | action[0] = ewmh_atom[_NET_WM_ACTION_MOVE]; 418 | action[1] = ewmh_atom[_NET_WM_ACTION_RESIZE]; 419 | action[2] = ewmh_atom[_NET_WM_ACTION_FULLSCREEN]; 420 | action[3] = ewmh_atom[_NET_WM_ACTION_CLOSE]; 421 | XChangeProperty(dpy, c->window, ewmh_atom[_NET_WM_ALLOWED_ACTIONS], 422 | XA_ATOM, 32, PropModeReplace, (unsigned char *)action, 4); 423 | } 424 | 425 | void 426 | ewmh_set_strut(ScreenInfo *screen) { 427 | Client *c; 428 | EWMHStrut strut; 429 | unsigned long data[4]; 430 | /* FIXME: add parameter to MakeSane rather than this hack */ 431 | Edge backup; 432 | 433 | /* find largest reserved areas */ 434 | strut.left = 0; 435 | strut.right = 0; 436 | strut.top = 0; 437 | strut.bottom = 0; 438 | for (c = client_head(); c; c = c->next) { 439 | if (c->screen != screen) continue; 440 | if (c->strut.left > strut.left) strut.left = c->strut.left; 441 | if (c->strut.right > strut.right) strut.right = c->strut.right; 442 | if (c->strut.top > strut.top) strut.top = c->strut.top; 443 | if (c->strut.bottom > strut.bottom) 444 | strut.bottom = c->strut.bottom; 445 | } 446 | 447 | /* if the reservered aread have not changed then we're done */ 448 | if ( screen->strut.left == strut.left && 449 | screen->strut.right == strut.right && 450 | screen->strut.top == strut.top && 451 | screen->strut.bottom == strut.bottom) return; 452 | 453 | /* apply the new strut */ 454 | screen->strut.left = strut.left; 455 | screen->strut.right = strut.right; 456 | screen->strut.top = strut.top; 457 | screen->strut.bottom = strut.bottom; 458 | 459 | /* set the new workarea */ 460 | data[0] = strut.left; 461 | data[1] = strut.top; 462 | data[2] = screen->display_width - (strut.left + strut.right); 463 | data[3] = screen->display_height - (strut.top + strut.bottom); 464 | XChangeProperty(dpy, screen->root, 465 | ewmh_atom[_NET_WORKAREA], 466 | XA_CARDINAL, 32, PropModeReplace, 467 | (unsigned char*)data, 4); 468 | 469 | /* ensure no window fully occupy reserved areas */ 470 | for (c = client_head(); c; c = c->next) { 471 | int x = c->size.x; 472 | int y = c->size.y; 473 | 474 | if (c->wstate.fullscreen == True) continue; 475 | backup = interacting_edge; 476 | interacting_edge = ENone; 477 | Client_MakeSane(c, ENone, &x, &y, 0, 0); 478 | interacting_edge = backup; 479 | if (c->framed == True) { 480 | XMoveWindow(dpy, c->parent, 481 | c->size.x, 482 | c->size.y - titleHeight()); 483 | } else { 484 | XMoveWindow(dpy, c->parent, 485 | c->size.x, c->size.y); 486 | } 487 | sendConfigureNotify(c); 488 | } 489 | 490 | } 491 | 492 | /* 493 | * get _NET_WM_STRUT and if it is available recalulate the screens 494 | * reserved areas. the EWMH spec isn't clear about what we should do 495 | * about hidden windows. It seems silly to reserve space for an invisible 496 | * window, but the spec allows it. Ho Hum... jfc 497 | */ 498 | void 499 | ewmh_get_strut(Client *c) { 500 | Atom rt; 501 | unsigned long *strut; 502 | int fmt; 503 | unsigned long n; 504 | unsigned long extra; 505 | int i; 506 | 507 | if (c == NULL) return; 508 | i = XGetWindowProperty(dpy, c->window, 509 | ewmh_atom[_NET_WM_STRUT], 510 | 0, 5, False, XA_CARDINAL, &rt, &fmt, &n, &extra, 511 | (unsigned char **)&strut); 512 | if (i != Success || strut == NULL || n < 4) return; 513 | c->strut.left = (unsigned int) strut[0]; 514 | c->strut.right = (unsigned int) strut[1]; 515 | c->strut.top = (unsigned int) strut[2]; 516 | c->strut.bottom = (unsigned int) strut[3]; 517 | ewmh_set_strut(c->screen); 518 | } 519 | 520 | /* fix stack forces each window on the screen to be in the right place in 521 | * the window stack as indicated in the EWMH spec version 1.2 (section 7.10). 522 | */ 523 | static void 524 | fix_stack(ScreenInfo *screen) { 525 | Client *c; 526 | 527 | /* this is pretty dumb. we should query the tree and only move 528 | * those windows that require it. doing it regardless liek this 529 | * causes the desktop to flicker 530 | */ 531 | 532 | /* first lower clients with _NET_WM_STATE_BELOW */ 533 | for (c = client_head(); c; c = c->next) { 534 | if (c->wstate.below == False) continue; 535 | Client_Lower(c); 536 | } 537 | 538 | /* lower desktops - they are always the lowest */ 539 | for (c = client_head(); c; c = c->next) { 540 | if (c->wtype != WTypeDesktop) continue; 541 | Client_Lower(c); 542 | break; /* only one desktop, surely */ 543 | } 544 | 545 | /* raise clients with _NET_WM_STATE_ABOVE and docks 546 | * (unless marked with _NET_WM_STATE_BELOW) 547 | */ 548 | for (c = client_head(); c; c = c->next) { 549 | if (!(c->wstate.above == True || 550 | (c->wtype == WTypeDock && 551 | c->wstate.below == False))) 552 | continue; 553 | Client_Raise(c); 554 | } 555 | 556 | /* raise fullscreens - they're always on top */ 557 | /* Misam Saki reports problems with this and believes fullscreens 558 | * should not be automatically raised. 559 | * 560 | * However if the code below is removed then the panel is raised above 561 | * fullscreens, which is not desirable. 562 | */ 563 | for (c = client_head(); c; c = c->next) { 564 | if (c->wstate.fullscreen == False) continue; 565 | Client_Raise(c); 566 | } 567 | } 568 | 569 | 570 | static Bool 571 | valid_for_client_list(ScreenInfo *screen, Client *c) { 572 | if (c->screen != screen) return False; 573 | if (c->state == WithdrawnState) return False; 574 | return True; 575 | } 576 | 577 | /* 578 | * update_client_list updates the properties on the root window used by 579 | * task lists and pagers. 580 | * 581 | * it should be called whenever the window stack is modified, or when clients 582 | * are hidden or unhidden. 583 | */ 584 | void 585 | ewmh_set_client_list(ScreenInfo *screen) { 586 | int no_clients=0; 587 | Window *client_list=NULL; 588 | Window *stacked_client_list=NULL; 589 | Client *c; 590 | 591 | if (screen == NULL || screen->ewmh_set_client_list == True) return; 592 | screen->ewmh_set_client_list = True; 593 | fix_stack(screen); 594 | for (c = client_head(); c; c = c->next) { 595 | if (valid_for_client_list(screen, c) == True) no_clients++; 596 | } 597 | if (no_clients > 0) { 598 | int i; 599 | Window dw1; 600 | Window dw2; 601 | Window *wins; 602 | unsigned int win; 603 | unsigned int nwins; 604 | 605 | client_list = malloc(sizeof(Window) * no_clients); 606 | i = no_clients - 1; /* array starts with oldest */ 607 | for (c = client_head(); c; c = c->next) { 608 | if (valid_for_client_list(screen, c) == True) { 609 | client_list[i] = c->window; 610 | i--; 611 | if (i < 0) break; 612 | } 613 | } 614 | 615 | stacked_client_list = malloc(sizeof(Window) * no_clients); 616 | i = 0; 617 | XQueryTree(dpy, screen->root, &dw1, &dw2, &wins, &nwins); 618 | for (win = 0; win < nwins; win++) { 619 | c = Client_Get(wins[win]); 620 | if (!c) continue; 621 | if (valid_for_client_list(screen, c) == True) { 622 | stacked_client_list[i] = c->window; 623 | i++; 624 | if (i >= no_clients) break; 625 | } 626 | } 627 | if ( nwins > 0 ) XFree(wins); 628 | 629 | } 630 | XChangeProperty(dpy, screen->root, 631 | ewmh_atom[_NET_CLIENT_LIST], 632 | XA_WINDOW, 32, PropModeReplace, 633 | (unsigned char*)client_list, no_clients); 634 | XChangeProperty(dpy, screen->root, 635 | ewmh_atom[_NET_CLIENT_LIST_STACKING], 636 | XA_WINDOW, 32, PropModeReplace, 637 | (unsigned char*)stacked_client_list, no_clients); 638 | if (no_clients > 0 ) { 639 | free(client_list); 640 | free(stacked_client_list); 641 | } 642 | screen->ewmh_set_client_list = False; 643 | } 644 | -------------------------------------------------------------------------------- /ewmh.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | /** 21 | * These are indexes into the ewmh_atom array. Only atoms actually supported 22 | * by lwm should be included here because _NET_SUPPORTED is built from the 23 | * ewmh_atom array. 24 | */ 25 | 26 | typedef enum { 27 | /* root window properties */ 28 | _NET_SUPPORTED, 29 | _NET_CLIENT_LIST, 30 | _NET_CLIENT_LIST_STACKING, 31 | _NET_NUMBER_OF_DESKTOPS, 32 | _NET_DESKTOP_GEOMETRY, 33 | _NET_DESKTOP_VIEWPORT, 34 | _NET_CURRENT_DESKTOP, 35 | /*_NET_DESKTOP_NAMES,*/ 36 | _NET_ACTIVE_WINDOW, 37 | _NET_WORKAREA, 38 | _NET_SUPPORTING_WM_CHECK, 39 | /*_NET_VIRTUAL_ROOTS,*/ 40 | /*_NET_DESKTOP_LAYOUT,*/ 41 | /*_NET_SHOWING_DESKTOP,*/ 42 | /* other root window messages */ 43 | _NET_CLOSE_WINDOW, 44 | _NET_MOVERESIZE_WINDOW, 45 | _NET_WM_MOVERESIZE, 46 | /* application window properties */ 47 | _NET_WM_NAME, 48 | /*_NET_WM_VISIBLE_NAME,*/ 49 | /*_NET_WM_ICON_NAME,*/ 50 | /*_NET_WM_VISIBLE_ICON_NAME,*/ 51 | /*_NET_WM_DESKTOP,*/ 52 | _NET_WM_WINDOW_TYPE, 53 | _NET_WM_STATE, 54 | _NET_WM_ALLOWED_ACTIONS, 55 | _NET_WM_STRUT, 56 | /*_NET_WM_ICON_GEOMETRY,*/ 57 | /*_NET_WM_ICON,*/ 58 | /*_NET_WM_PID,*/ 59 | /*_NET_WM_HANDLED_ICONS,*/ 60 | /* window types for _NET_WM_WINDOW_TYPE */ 61 | _NET_WM_WINDOW_TYPE_DESKTOP, 62 | _NET_WM_WINDOW_TYPE_DOCK, 63 | _NET_WM_WINDOW_TYPE_TOOLBAR, 64 | _NET_WM_WINDOW_TYPE_MENU, 65 | _NET_WM_WINDOW_TYPE_UTILITY, 66 | _NET_WM_WINDOW_TYPE_SPLASH, 67 | _NET_WM_WINDOW_TYPE_DIALOG, 68 | _NET_WM_WINDOW_TYPE_NORMAL, 69 | /* window states for _NET_WM_STATE */ 70 | /*_NET_WM_STATE_MODAL,*/ 71 | /*_NET_WM_STATE_STICKY,*/ 72 | /*_NET_WM_STATE_MAXIMISED_VERT,*/ 73 | /*_NET_WM_STATE_MAXIMISED_HORZ,*/ 74 | /*_NET_WM_STATE_SHADED,*/ 75 | _NET_WM_STATE_SKIP_TASKBAR, 76 | _NET_WM_STATE_SKIP_PAGER, 77 | _NET_WM_STATE_HIDDEN, 78 | _NET_WM_STATE_FULLSCREEN, 79 | _NET_WM_STATE_ABOVE, 80 | _NET_WM_STATE_BELOW, 81 | /* allowed actions for _NET_WM_ALLOWED_ACTIONS */ 82 | _NET_WM_ACTION_MOVE, 83 | _NET_WM_ACTION_RESIZE, 84 | /*_NET_WM_ACTION_MINIMIZE,*/ 85 | /*_NET_WM_ACTION_SHADE,*/ 86 | /*_NET_WM_ACTION_STICK,*/ 87 | /*_NET_WM_ACTION_MAXIMIZE_HORIZ,*/ 88 | /*_NET_WM_ACTION_MAXIMIZE_VERT,*/ 89 | _NET_WM_ACTION_FULLSCREEN, 90 | /*_NET_WM_ACTION_CHANGE_DESKTOP,*/ 91 | _NET_WM_ACTION_CLOSE, 92 | EWMH_ATOM_LAST 93 | } EWMHAtom; 94 | 95 | -------------------------------------------------------------------------------- /lwm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "lwm.h" 38 | 39 | Mode mode; /* The window manager's mode. (See "lwm.h".) */ 40 | int start_x; /* The X position where the mode changed. */ 41 | int start_y; /* The Y position where the mode changed. */ 42 | 43 | Display * dpy; /* The connection to the X server. */ 44 | int screen_count; /* The number of screens. */ 45 | ScreenInfo * screens; /* Information about these screens. */ 46 | ScreenInfo * current_screen; 47 | 48 | XFontSet font_set = NULL; /* Font set for title var */ 49 | XFontSetExtents *font_set_ext = NULL; 50 | XFontSet popup_font_set = NULL; /* Font set for popups */ 51 | XFontSetExtents *popup_font_set_ext = NULL; 52 | 53 | Bool shape; /* Does server have Shape Window extension? */ 54 | int shape_event; /* ShapeEvent event type. */ 55 | 56 | /* Atoms we're interested in. See the ICCCM for more information. */ 57 | Atom wm_state; 58 | Atom wm_change_state; 59 | Atom wm_protocols; 60 | Atom wm_delete; 61 | Atom wm_take_focus; 62 | Atom wm_colormaps; 63 | Atom compound_text; 64 | 65 | /** Netscape uses this to give information about the URL it's displaying. */ 66 | Atom _mozilla_url; 67 | 68 | /* 69 | * if we're really short of a clue we might look at motif hints, and 70 | * we're not going to link with motif, so we'll have to do it by hand 71 | */ 72 | Atom motif_wm_hints; 73 | 74 | char *argv0; 75 | 76 | static void initScreens(void); 77 | static void initScreen(int); 78 | 79 | /*ARGSUSED*/ 80 | extern int 81 | main(int argc, char *argv[]) { 82 | XEvent ev; 83 | struct sigaction sa; 84 | int dpy_fd, max_fd; 85 | 86 | argv0 = argv[0]; 87 | 88 | mode = wm_initialising; 89 | 90 | setlocale(LC_ALL, ""); 91 | 92 | /* Open a connection to the X server. */ 93 | dpy = XOpenDisplay(NULL); 94 | if (dpy == 0) 95 | panic("can't open display."); 96 | 97 | parseResources(); 98 | 99 | /* Set up an error handler. */ 100 | XSetErrorHandler(errorHandler); 101 | 102 | /* Set up signal handlers. */ 103 | signal(SIGTERM, Terminate); 104 | signal(SIGINT, Terminate); 105 | signal(SIGHUP, Terminate); 106 | 107 | /* Ignore SIGCHLD. */ 108 | sa.sa_handler = SIG_IGN; 109 | #ifdef SA_NOCLDWAIT 110 | sa.sa_flags = SA_NOCLDWAIT; 111 | #else 112 | sa.sa_flags = 0; 113 | #endif 114 | sigemptyset(&sa.sa_mask); 115 | sigaction(SIGCHLD, &sa, 0); 116 | 117 | /* Internalize useful atoms. */ 118 | wm_state = XInternAtom(dpy, "WM_STATE", False); 119 | wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False); 120 | wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); 121 | wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 122 | wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 123 | wm_colormaps = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False); 124 | compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False); 125 | 126 | _mozilla_url = XInternAtom(dpy, "_MOZILLA_URL", False); 127 | 128 | motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); 129 | 130 | ewmh_init(); 131 | 132 | /* 133 | * Get fonts for our titlebar and our popup window. We try to 134 | * get Lucida, but if we can't we make do with fixed because everyone 135 | * has that. 136 | */ 137 | { 138 | /* FIXME: do these need to be freed? */ 139 | char **missing; 140 | char *def; 141 | int missing_count; 142 | 143 | font_set = XCreateFontSet(dpy, font_name, 144 | &missing, &missing_count, &def); 145 | if (font_set == NULL) 146 | font_set = XCreateFontSet(dpy, "fixed", 147 | &missing, &missing_count, &def); 148 | if (font_set == NULL) 149 | panic("unable to create font set for title font"); 150 | if (missing_count > 0) 151 | fprintf(stderr,"%s: warning: missing %d charset" 152 | "%s for title font\n", argv0, missing_count, 153 | (missing_count == 1)?"":"s"); 154 | font_set_ext = XExtentsOfFontSet(font_set); 155 | 156 | popup_font_set = XCreateFontSet(dpy, popup_font_name, 157 | &missing, &missing_count, &def); 158 | if (popup_font_set == NULL) 159 | popup_font_set = XCreateFontSet(dpy, "fixed", 160 | &missing, &missing_count, &def); 161 | if (popup_font_set == NULL) 162 | panic("unable to create font set for popup font"); 163 | if (missing_count > 0) 164 | fprintf(stderr,"%s: warning: missing %d charset" 165 | "%s for popup font\n", argv0, missing_count, 166 | (missing_count == 1)?"":"s"); 167 | popup_font_set_ext = XExtentsOfFontSet(popup_font_set); 168 | } 169 | 170 | initScreens(); 171 | ewmh_init_screens(); 172 | session_init(argc, argv); 173 | 174 | /* See if the server has the Shape Window extension. */ 175 | shape = serverSupportsShapes(); 176 | 177 | /* 178 | * Initialisation is finished, but we start off not interacting with the 179 | * user. 180 | */ 181 | mode = wm_idle; 182 | 183 | /* 184 | * The main event loop. 185 | */ 186 | dpy_fd = ConnectionNumber(dpy); 187 | max_fd = dpy_fd + 1; 188 | if (ice_fd > dpy_fd) max_fd = ice_fd + 1; 189 | for (;;) { 190 | fd_set readfds; 191 | 192 | FD_ZERO(&readfds); 193 | FD_SET(dpy_fd, &readfds); 194 | if (ice_fd > 0) FD_SET(ice_fd, &readfds); 195 | if (select(max_fd, &readfds, NULL, NULL, NULL) > -1) { 196 | if (FD_ISSET(dpy_fd, &readfds)) { 197 | while (XPending(dpy)) { 198 | XNextEvent(dpy, &ev); 199 | dispatch(&ev); 200 | } 201 | } 202 | if (ice_fd > 0 && FD_ISSET(ice_fd, &readfds)) { 203 | session_process(); 204 | } 205 | } 206 | } 207 | } 208 | 209 | void 210 | sendConfigureNotify(Client *c) { 211 | XConfigureEvent ce; 212 | 213 | ce.type = ConfigureNotify; 214 | ce.event = c->window; 215 | ce.window = c->window; 216 | if (c->framed == True) { 217 | ce.x = c->size.x + border; 218 | ce.y = c->size.y + border; 219 | ce.width = c->size.width - 2 * border; 220 | ce.height = c->size.height - 2 * border; 221 | ce.border_width = c->border; 222 | } else { 223 | ce.x = c->size.x; 224 | ce.y = c->size.y; 225 | ce.width = c->size.width; 226 | ce.height = c->size.height; 227 | ce.border_width = c->border; 228 | } 229 | ce.above = None; 230 | ce.override_redirect = 0; 231 | XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent *) &ce); 232 | } 233 | 234 | extern void 235 | scanWindowTree(int screen) { 236 | unsigned int i; 237 | unsigned int nwins; 238 | Client * c; 239 | Window dw1; 240 | Window dw2; 241 | Window * wins; 242 | XWindowAttributes attr; 243 | 244 | XQueryTree(dpy, screens[screen].root, &dw1, &dw2, &wins, &nwins); 245 | for (i = 0; i < nwins; i++) { 246 | XGetWindowAttributes(dpy, wins[i], &attr); 247 | if (attr.override_redirect /*|| isShaped(wins[i])*/ || wins[i] == screens[screen].popup) 248 | continue; 249 | c = Client_Add(wins[i], screens[screen].root); 250 | if (c != 0 && c->window == wins[i]) { 251 | c->screen = &screens[screen]; 252 | c->size.x = attr.x; 253 | c->size.y = attr.y; 254 | /* we'll leave it until it's managed 255 | if (c->framed == True) { 256 | c->size.x -= border; 257 | c->size.y -= border; 258 | } 259 | */ 260 | c->size.width = attr.width; 261 | c->size.height = attr.height; 262 | /* we'll leave it until it's managed 263 | if (c->framed == True) { 264 | c->size.width += 2 * border; 265 | c->size.height += 2 * border; 266 | } 267 | */ 268 | c->border = attr.border_width; 269 | if (attr.map_state == IsViewable) { 270 | c->internal_state = IPendingReparenting; 271 | manage(c, 1); 272 | } 273 | } 274 | } 275 | XFree(wins); 276 | } 277 | 278 | /*ARGSUSED*/ 279 | extern void 280 | shell(ScreenInfo * screen, int button, int x, int y) { 281 | char * command = NULL; 282 | char * sh; 283 | 284 | /* Get the command we're to execute. Give up if there isn't one. */ 285 | if (button == Button1) 286 | command = btn1_command; 287 | if (button == Button2) 288 | command = btn2_command; 289 | if (command == NULL) 290 | return; 291 | 292 | sh = getenv("SHELL"); 293 | if (sh == 0) 294 | sh = "/bin/sh"; 295 | 296 | switch (fork()) { 297 | case 0: /* Child. */ 298 | close(ConnectionNumber(dpy)); 299 | if (screen && screen->display_spec != 0) 300 | putenv(screen->display_spec); 301 | execl(sh, sh, "-c", command, NULL); 302 | fprintf(stderr, "%s: can't exec \"%s -c %s\"\n", argv0, sh, 303 | command); 304 | execlp("xterm", "xterm", NULL); 305 | exit(EXIT_FAILURE); 306 | case -1: /* Error. */ 307 | fprintf(stderr, "%s: couldn't fork\n", argv0); 308 | break; 309 | } 310 | } 311 | 312 | extern int 313 | titleHeight(void) { 314 | return font_set_ext->max_logical_extent.height; 315 | } 316 | 317 | extern int 318 | ascent(XFontSetExtents *font_set_ext) { 319 | return abs(font_set_ext->max_logical_extent.y); 320 | } 321 | 322 | extern int 323 | popupHeight(void) { 324 | return popup_font_set_ext->max_logical_extent.height; 325 | } 326 | 327 | extern int 328 | titleWidth(XFontSet font_set, Client *c) { 329 | XRectangle ink; 330 | XRectangle logical; 331 | char *name; 332 | int namelen; 333 | 334 | if (c == NULL) return 0; 335 | if (c->menu_name == NULL) { 336 | name = c->name; 337 | namelen = c->namelen; 338 | } else { 339 | name = c->menu_name; 340 | namelen = c->menu_namelen; 341 | } 342 | if (name == NULL) return 0; 343 | #ifdef X_HAVE_UTF8_STRING 344 | if (c->name_utf8 == True) 345 | Xutf8TextExtents(font_set, name, namelen, 346 | &ink, &logical); 347 | else 348 | #endif 349 | XmbTextExtents(font_set, name, namelen, 350 | &ink, &logical); 351 | 352 | return logical.width; 353 | } 354 | 355 | extern int 356 | popupWidth(char *string, int string_length) { 357 | XRectangle ink; 358 | XRectangle logical; 359 | 360 | XmbTextExtents(popup_font_set, string, string_length, 361 | &ink, &logical); 362 | 363 | return logical.width; 364 | } 365 | 366 | static void 367 | initScreens(void) { 368 | int screen; 369 | 370 | /* Find out how many screens we've got, and allocate space for their info. */ 371 | screen_count = ScreenCount(dpy); 372 | screens = (ScreenInfo *) malloc(screen_count * sizeof(ScreenInfo)); 373 | 374 | /* Go through the screens one-by-one, initialising them. */ 375 | for (screen = 0; screen < screen_count; screen++) { 376 | initialiseCursors(screen); 377 | initScreen(screen); 378 | scanWindowTree(screen); 379 | } 380 | } 381 | 382 | static void 383 | initScreen(int screen) { 384 | XGCValues gv; 385 | XSetWindowAttributes attr; 386 | XColor colour, exact; 387 | int len; 388 | char * display_string = DisplayString(dpy); 389 | char * colon = strrchr(display_string, ':'); 390 | char * dot = NULL; 391 | 392 | /* Set the DISPLAY specification. */ 393 | if (colon) { 394 | dot = strrchr(colon, '.'); 395 | len = 9 + strlen(display_string) + ((dot == 0) ? 2 : 0) + 10; 396 | screens[screen].display_spec = (char *) malloc(len); 397 | sprintf(screens[screen].display_spec, "DISPLAY=%s", display_string); 398 | if (dot == 0) dot = screens[screen].display_spec + len - 3; 399 | else dot = strrchr(screens[screen].display_spec, '.'); 400 | sprintf(dot, ".%i", screen); 401 | } else { 402 | screens[screen].display_spec = 0; 403 | } 404 | 405 | /* Find the root window. */ 406 | screens[screen].root = RootWindow(dpy, screen); 407 | screens[screen].display_width = DisplayWidth(dpy, screen); 408 | screens[screen].display_height = DisplayHeight(dpy, screen); 409 | screens[screen].strut.left = 0; 410 | screens[screen].strut.right = 0; 411 | screens[screen].strut.top = 0; 412 | screens[screen].strut.bottom = 0; 413 | 414 | /* Get the pixel values of the only two colours we use. */ 415 | screens[screen].black = BlackPixel(dpy, screen); 416 | screens[screen].white = WhitePixel(dpy, screen); 417 | XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "DimGray", &colour, &exact); 418 | screens[screen].gray = colour.pixel; 419 | 420 | /* Set up root (frame) GC's. */ 421 | gv.foreground = screens[screen].black ^ screens[screen].white; 422 | gv.background = screens[screen].white; 423 | gv.function = GXxor; 424 | gv.line_width = 1; 425 | gv.subwindow_mode = IncludeInferiors; 426 | screens[screen].gc_thin = XCreateGC(dpy, screens[screen].root, 427 | GCForeground | GCBackground | GCFunction | 428 | GCLineWidth | GCSubwindowMode, &gv); 429 | 430 | gv.line_width = 2; 431 | screens[screen].gc = XCreateGC(dpy, screens[screen].root, 432 | GCForeground | GCBackground | GCFunction | 433 | GCLineWidth | GCSubwindowMode, &gv); 434 | 435 | /* Create a window for our popup. */ 436 | screens[screen].popup = XCreateSimpleWindow(dpy, screens[screen].root, 437 | 0, 0, 1, 1, 1, screens[screen].black, screens[screen].white); 438 | attr.event_mask = ButtonMask | ButtonMotionMask | ExposureMask; 439 | XChangeWindowAttributes(dpy, screens[screen].popup, CWEventMask, &attr); 440 | 441 | /* Create menu GC. */ 442 | gv.line_width = 1; 443 | screens[screen].menu_gc = XCreateGC(dpy, screens[screen].popup, 444 | GCForeground | GCBackground | GCFunction | 445 | GCLineWidth | GCSubwindowMode, &gv); 446 | 447 | /* Create size indicator GC. */ 448 | gv.foreground = screens[screen].black; 449 | gv.function = GXcopy; 450 | screens[screen].size_gc = XCreateGC(dpy, screens[screen].popup, 451 | GCForeground | GCBackground | GCFunction | 452 | GCLineWidth | GCSubwindowMode, &gv); 453 | 454 | /* Announce our interest in the root window. */ 455 | attr.cursor = screens[screen].root_cursor; 456 | attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | 457 | ColormapChangeMask | ButtonPressMask | PropertyChangeMask | 458 | EnterWindowMask; 459 | XChangeWindowAttributes(dpy, screens[screen].root, CWCursor | 460 | CWEventMask, &attr); 461 | 462 | /* Make sure all our communication to the server got through. */ 463 | XSync(dpy, False); 464 | } 465 | 466 | /** 467 | Find the screen for which root is the root window. 468 | */ 469 | ScreenInfo * 470 | getScreenFromRoot(Window root) { 471 | int screen; 472 | 473 | for (screen = 0; screen < screen_count; screen++) 474 | if (screens[screen].root == root) 475 | return &screens[screen]; 476 | 477 | return 0; 478 | } 479 | -------------------------------------------------------------------------------- /lwm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | /* --- Administrator-configurable defaults. --- */ 21 | 22 | #define DEFAULT_TITLE_FONT "-*-lucida-bold-r-normal-sans-14-*-*-*-p-*-iso10646-1" 23 | #define DEFAULT_POPUP_FONT "-*-lucida-medium-r-normal-sans-12-*-*-*-p-*-iso10646-1" 24 | #define DEFAULT_TERMINAL "xterm" 25 | #define DEFAULT_BORDER 6 26 | 27 | #define HIDE_BUTTON Button3 28 | #define MOVE_BUTTON Button2 29 | #define RESHAPE_BUTTON Button1 30 | 31 | #define EDGE_RESIST 32 32 | 33 | /* --- End of administrator-configurable defaults. --- */ 34 | 35 | /* 36 | * Window manager mode. wm is in one of five modes: it's either getting 37 | * user input to move/reshape a window, getting user input to make a 38 | * selection from the menu, waiting for user input to confirm a window close 39 | * (by releasing a mouse button after prssing it in a window's box), 40 | * waiting for user input to confirm a window hide (by releasing a mouse 41 | * button after prssing it in a window's frame), 42 | * or it's `idle' --- responding to events arriving 43 | * from the server, but not directly interacting with the user. 44 | * OK, so I lied: there's a sixth mode so that we can tell when wm's still 45 | * initialising. 46 | */ 47 | typedef enum { 48 | wm_initialising, 49 | wm_idle, 50 | wm_reshaping, 51 | wm_menu_up, 52 | wm_closing_window, 53 | wm_hiding_window 54 | } Mode; 55 | 56 | /** Window internal state. Yuck. */ 57 | typedef enum { 58 | IPendingReparenting, INormal 59 | } IState ; 60 | 61 | 62 | /** 63 | * Focus mode, may me (and defaults to) enter where entering a window gives 64 | * that window input focus, or click where a window must be explicitly clicked 65 | * to give it the focus. 66 | */ 67 | typedef enum { 68 | focus_enter, focus_click 69 | } FocusMode; 70 | 71 | /** 72 | * Window edge, used in resizing. The `edge' ENone is used to signify a 73 | * window move rather than a resize. The code is sufficiently similar that 74 | * this isn't a special case to be treated separately. 75 | */ 76 | typedef enum { 77 | ETopLeft, ETop, ETopRight, 78 | ERight, ENone, ELeft, 79 | EBottomLeft, EBottom, EBottomRight, 80 | E_LAST 81 | } Edge ; 82 | 83 | /** 84 | * EWMH direction for _NET_WM_MOVERESIZE 85 | */ 86 | typedef enum { 87 | DSizeTopLeft, DSizeTop, DSizeTopRight, 88 | DSizeRight, DSizeBottomRight, DSizeBottom, 89 | DSizeBottomLeft, DSizeLeft, DMove, 90 | DSizeKeyboard, DMoveKeyboard 91 | } EWMHDirection; 92 | 93 | /** 94 | * EWMH window type. See section 5.6 of the EWMH specification (1.2). 95 | * WTypeNone indicates that no EWMH window type as been set and MOTIF 96 | * hints should be used instead. 97 | */ 98 | typedef enum { 99 | WTypeDesktop, WTypeDock, WTypeToolbar, 100 | WTypeMenu, WTypeUtility, WTypeSplash, 101 | WTypeDialog, WTypeNormal, WTypeNone 102 | } EWMHWindowType; 103 | 104 | /** 105 | * EWMH window state, See section 5.7 of the EWMH specification (1.2). 106 | * lwm does not support all states. _NET_WM_STATE_HIDDEN is taken from 107 | * Client.hidden. 108 | */ 109 | typedef struct { 110 | Bool skip_taskbar; 111 | Bool skip_pager; 112 | Bool fullscreen; 113 | Bool above; 114 | Bool below; 115 | } EWMHWindowState; 116 | 117 | /** 118 | * EWMH "strut", or area on each edge of the screen reserved for docking 119 | * bars/panels. 120 | */ 121 | typedef struct { 122 | unsigned int left; 123 | unsigned int right; 124 | unsigned int top; 125 | unsigned int bottom; 126 | } EWMHStrut; 127 | 128 | /** 129 | * Screen information. 130 | */ 131 | typedef struct ScreenInfo ScreenInfo; 132 | struct ScreenInfo { 133 | Window root; 134 | Window popup; 135 | Window ewmh_compat; 136 | 137 | int display_width; /* The width of the screen. */ 138 | int display_height; /* The height of the screen. */ 139 | EWMHStrut strut; /* reserved areas */ 140 | 141 | GC gc; /* The default GC. */ 142 | GC gc_thin; /* The default GC but with thinner lines. */ 143 | GC menu_gc; /* The GC for the popup window (menu). */ 144 | GC size_gc; /* The GC for the popup window (sizing). */ 145 | 146 | unsigned long black; /* Black pixel value. */ 147 | unsigned long white; /* White pixel value. */ 148 | unsigned long gray; /* Gray pixel value. */ 149 | 150 | Cursor root_cursor; 151 | Cursor box_cursor; 152 | 153 | Cursor cursor_map[E_LAST]; 154 | 155 | Bool ewmh_set_client_list; /* hack to prevent recursion */ 156 | 157 | char * display_spec; 158 | }; 159 | 160 | typedef struct Client Client; 161 | struct Client { 162 | Window window; /* Client's window. */ 163 | Window parent; /* Window manager frame. */ 164 | Window trans; /* Window that client is a transient for. */ 165 | 166 | Bool framed; /* True is lwm is maintaining a frame */ 167 | 168 | Client * next; /* Next window in client list. */ 169 | 170 | int border; /* Client's original border width. */ 171 | 172 | XSizeHints size; /* Client's current geometry information. */ 173 | XSizeHints return_size; /* Client's old geometry information. */ 174 | int state; /* Window state. See ICCCM and */ 175 | 176 | Bool hidden; /* True if this client is hidden. */ 177 | IState internal_state; 178 | int proto; 179 | 180 | int accepts_focus; /* Does this window want keyboard events? */ 181 | 182 | char * name; /* Name used for title in frame. */ 183 | int namelen; 184 | char * menu_name; /* Name used in root popup */ 185 | int menu_namelen; 186 | Bool name_utf8; 187 | 188 | ScreenInfo * screen; 189 | 190 | Edge cursor; /* indicates which cursor is being used for parent window */ 191 | 192 | EWMHWindowType wtype; 193 | EWMHWindowState wstate; 194 | EWMHStrut strut; /* reserved areas */ 195 | 196 | /* Colourmap scum. */ 197 | Colormap cmap; 198 | int ncmapwins; 199 | Window * cmapwins; 200 | Colormap * wmcmaps; 201 | }; 202 | 203 | 204 | /* 205 | * c->proto is a bitarray of these 206 | */ 207 | enum { 208 | Pdelete = 1, 209 | Ptakefocus = 2 210 | }; 211 | 212 | /* 213 | * This should really have been in X.h --- if you select both ButtonPress 214 | * and ButtonRelease events, the server makes an automatic grab on the 215 | * pressed button for you. This is almost always exactly what you want. 216 | */ 217 | #define ButtonMask (ButtonPressMask | ButtonReleaseMask) 218 | 219 | /* lwm.c */ 220 | extern Mode mode; 221 | extern int start_x; 222 | extern int start_y; 223 | extern Display * dpy; 224 | extern int screen_count; 225 | extern ScreenInfo * screens; 226 | extern ScreenInfo * current_screen; 227 | extern XFontSet font_set; 228 | extern XFontSetExtents *font_set_ext; 229 | extern XFontSet popup_font_set; 230 | extern XFontSetExtents *popup_font_set_ext; 231 | extern Atom _mozilla_url; 232 | extern Atom motif_wm_hints; 233 | extern Atom wm_state; 234 | extern Atom wm_change_state; 235 | extern Atom wm_protocols; 236 | extern Atom wm_delete; 237 | extern Atom wm_take_focus; 238 | extern Atom wm_colormaps; 239 | extern Atom compound_text; 240 | extern Bool shape; 241 | extern int shape_event; 242 | extern char *argv0; 243 | extern void shell(ScreenInfo *, int, int, int); 244 | extern void sendConfigureNotify(Client *); 245 | extern int titleHeight(void); 246 | extern int titleWidth(XFontSet font_set, Client *c); 247 | extern int popupHeight(void); 248 | extern int popupWidth(char *string, int string_length); 249 | extern int ascent(XFontSetExtents *font_set_ext); 250 | extern ScreenInfo * getScreenFromRoot(Window); 251 | extern void scanWindowTree(int); 252 | 253 | /* client.c */ 254 | extern Client *client_head(void); 255 | extern Edge interacting_edge; 256 | extern Client *Client_Get(Window); 257 | extern Client *Client_Add(Window, Window); 258 | extern void Client_MakeSane(Client *, Edge, int *, int *, int *, int *); 259 | extern void Client_DrawBorder(Client *, int); 260 | extern void setactive(Client *, int, long); 261 | extern void Client_SizeFeedback(void); 262 | extern void size_expose(void); 263 | extern void Client_ReshapeEdge(Client *, Edge); 264 | extern void Client_Move(Client*); 265 | extern void Client_SetState(Client *, int); 266 | extern void Client_Raise(Client *); 267 | extern void Client_Lower(Client *); 268 | extern void Client_Close(Client *); 269 | extern void Client_Remove(Client *); 270 | extern void Client_FreeAll(void); 271 | extern void Client_ColourMap(XEvent*); 272 | extern void Client_EnterFullScreen(Client *c); 273 | extern void Client_ExitFullScreen(Client *c); 274 | extern void Client_Focus(Client *c, Time time); 275 | extern void Client_ResetAllCursors(void); 276 | extern void Client_Name(Client *c, const char *name, Bool is_utf8); 277 | extern int hidden(Client *); 278 | extern int withdrawn(Client *); 279 | extern int normal(Client *); 280 | extern void update_client_list(ScreenInfo *screen); 281 | extern Client *current; 282 | 283 | /* cursor.c */ 284 | extern Cursor getEdgeCursor(Edge edge); 285 | extern void initialiseCursors(int); 286 | 287 | /* disp.c */ 288 | extern void dispatch(XEvent *); 289 | extern void reshaping_motionnotify(XEvent *); 290 | 291 | /* error.c */ 292 | extern int ignore_badwindow; 293 | extern int errorHandler(Display *, XErrorEvent *); 294 | extern void panic(char*); 295 | 296 | /* manage.c */ 297 | extern void getWindowName(Client *); 298 | extern void getNormalHints(Client *); 299 | extern Bool motifWouldDecorate(Client *); 300 | extern void manage(Client *, int); 301 | extern void withdraw(Client *); 302 | extern void cmapfocus(Client *); 303 | extern void getColourmaps(Client *); 304 | extern void getTransientFor(Client *); 305 | extern void Terminate(int); 306 | 307 | /* mouse.c */ 308 | extern void getMousePosition(int *, int *); 309 | extern void hide(Client *); 310 | extern void unhidec(Client *, int); 311 | extern int menu_whichitem(int, int); 312 | extern void menuhit(XButtonEvent *); 313 | extern void unhide(int, int); 314 | extern void menu_expose(void); 315 | extern void menu_motionnotify(XEvent *); 316 | extern void menu_buttonrelease(XEvent *); 317 | 318 | /* shape.c */ 319 | extern int shapeEvent(XEvent *); 320 | extern int serverSupportsShapes(void); 321 | extern int isShaped(Window); 322 | extern void setShape(Client *); 323 | 324 | /* resource.c */ 325 | extern char *font_name; 326 | extern char *popup_font_name; 327 | extern char *btn1_command; 328 | extern char *btn2_command; 329 | extern int border; 330 | extern FocusMode focus_mode; 331 | extern char * sdup(char *); 332 | extern void parseResources(void); 333 | 334 | /* session.c */ 335 | extern int ice_fd; 336 | extern void session_init(int argc, char *argv[]); 337 | extern void session_process(void); 338 | extern void session_end(void); 339 | 340 | /* ewmh.c */ 341 | extern Atom ewmh_atom[]; 342 | extern void ewmh_init(void); 343 | extern void ewmh_init_screens(void); 344 | extern EWMHWindowType ewmh_get_window_type(Window w); 345 | extern Bool ewmh_get_window_name(Client *c); 346 | extern Bool ewmh_hasframe(Client *c); 347 | extern void ewmh_set_state(Client *c); 348 | extern void ewmh_get_state(Client *c); 349 | extern void ewmh_change_state(Client *c, unsigned long action, 350 | unsigned long atom); 351 | extern void ewmh_set_allowed(Client *c); 352 | extern void ewmh_set_client_list(ScreenInfo *screen); 353 | extern void ewmh_get_strut(Client *c); 354 | extern void ewmh_set_strut(ScreenInfo *screen); 355 | -------------------------------------------------------------------------------- /lwm.man: -------------------------------------------------------------------------------- 1 | .\" lwm, a window manager for X11 2 | .\" Copyright (C) 1997-2016 Elliott Hughes, James Carter 3 | .\" 4 | .\" This program is free software; you can redistribute it and/or 5 | .\" modify it under the terms of the GNU General Public License 6 | .\" as published by the Free Software Foundation; either version 2 7 | .\" of the License, or (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program; if not, write to the Free Software 16 | .\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | .\" 18 | .TH LWM 1 19 | .SH NAME 20 | lwm \- Lightweight Window Manager for the X Window System 21 | .SH SYNTAX 22 | \fBlwm \fP[ \fB\-s\fP \fIsession-id\fP ] 23 | .SH DESCRIPTION 24 | \fILwm\fP is a window manager for the X Window System. It provides enough 25 | features to allow the user to manage their windows, and no more. 26 | .PP 27 | Windows are surrounded by a frame with a 28 | titlebar at the top next to a small box. The frame is a grey colour for 29 | all windows except that which has the input focus, where it is black. 30 | .PP 31 | In the default configuration, \fIlwm\fP uses the enter-to-focus scheme, where 32 | moving the pointer into a window gives that window the input focus. 33 | \fILwm\fP may also be configured to use the click-to-focus scheme, where a 34 | window must be clicked on (with any button) to receive the input focus. Clicking 35 | on a window in this mode causes the window to be raised. Note that a click 36 | used to focus a window is always swallowed by \fIlwm\fP, so clicking a 37 | button in a new window requires two clicks. 38 | .PP 39 | A button 1 click on a window frame brings that window to the top. Dragging 40 | button 1 on the frame of a resizable window repositions that edge of 41 | the window. If a corner rather than an edge is dragged, then both edges 42 | forming the corner are repositioned. While you're reshaping a window, 43 | a little window pops up to show you the window's current size. 44 | .PP 45 | In the default configuration, button 1 on the root window does nothing. 46 | .PP 47 | Button 2 is used to drag a window by its frame, repositioning the window 48 | but maintaining its position in the window stack. 49 | .PP 50 | In the default configuration, button 2 on the root window brings up a 51 | new shell. 52 | .PP 53 | A button 3 click on a window frame hides that window. Pressing 54 | button 3 on the root window brings up a menu of all the hidden windows. 55 | Releasing the button while over an item will unhide the named window. 56 | .PP 57 | A button 3 click in the frame while Shift is held down pushes the window 58 | to the back, under any other windows. (Users with 4-button mice are 59 | encouraged to use their fourth button for this function.) 60 | .PP 61 | A click with any button inside the little white box in a window's frame 62 | can be used to close the window. 63 | .SH OPTIONS 64 | \fILwm\fP accepts the following command line options: 65 | .PP 66 | .TP 8 67 | .B \-s 68 | specifies a client ID for the X Session Management system, and is used 69 | exclusively by session managers. 70 | .SH RESOURCES 71 | \fILwm\fP understands the following X resources: 72 | .TP 12 73 | .B titleFont 74 | font used in window titles 75 | .TP 12 76 | .B popupFont 77 | font used in popup window (menu/size indicator) 78 | .TP 12 79 | .B border 80 | width in pixels of window borders 81 | .TP 12 82 | .B button1 83 | program spawned when button 1 is clicked on the root window 84 | .TP 12 85 | .B button2 86 | program spawned when button 2 is clicked on the root window 87 | .TP 12 88 | .B focus 89 | focus mode, one of "enter" for enter-to-focus (or sloppy focus), or 90 | "click" for click-to-focus 91 | .SH "SEE ALSO" 92 | .PP 93 | X(7) 94 | .SH AUTHORS 95 | Elliott Hughes , 96 | James Carter 97 | -------------------------------------------------------------------------------- /manage.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* These are Motif definitions from Xm/MwmUtil.h, but Motif isn't available 30 | everywhere. */ 31 | #define MWM_HINTS_FUNCTIONS (1L << 0) 32 | #define MWM_HINTS_DECORATIONS (1L << 1) 33 | #define MWM_HINTS_INPUT_MODE (1L << 2) 34 | #define MWM_HINTS_STATUS (1L << 3) 35 | #define MWM_DECOR_ALL (1L << 0) 36 | #define MWM_DECOR_BORDER (1L << 1) 37 | #define MWM_DECOR_RESIZEH (1L << 2) 38 | #define MWM_DECOR_TITLE (1L << 3) 39 | #define MWM_DECOR_MENU (1L << 4) 40 | #define MWM_DECOR_MINIMIZE (1L << 5) 41 | #define MWM_DECOR_MAXIMIZE (1L << 6) 42 | 43 | #include "lwm.h" 44 | 45 | static int getProperty(Window, Atom, Atom, long, unsigned char **); 46 | static int getWindowState(Window, int *); 47 | static void applyGravity(Client *); 48 | 49 | /*ARGSUSED*/ 50 | void 51 | manage(Client * c, int mapped) 52 | { 53 | int state; 54 | XWMHints * hints; 55 | XWindowAttributes current_attr; 56 | XSetWindowAttributes attr; 57 | 58 | /* For WM_PROTOCOLS handling. */ 59 | Atom * protocols; 60 | int n; 61 | int p; 62 | 63 | /* Where auto-placement is going to put the next window. */ 64 | static int auto_x = 100; 65 | static int auto_y = 100; 66 | 67 | /* get the EWMH window type, as this might overrule some hints */ 68 | c->wtype = ewmh_get_window_type(c->window); 69 | /* get in the initial EWMH state */ 70 | ewmh_get_state(c); 71 | /* set EWMH allowable actions, now we intend to manage this window */ 72 | ewmh_set_allowed(c); 73 | /* is this window to have a frame? */ 74 | if (c->wtype == WTypeNone) { 75 | /* this breaks the ewmh spec (section 5.6) because in the 76 | * absence of a _NET_WM_WINDOW_TYPE, _WM_WINDOW_TYPE_NORMAL 77 | * must be taken. bummer. 78 | */ 79 | c->framed = motifWouldDecorate(c); 80 | } else { 81 | c->framed = ewmh_hasframe(c); 82 | } 83 | if (isShaped(c->window)) c->framed = False; 84 | 85 | /* get the EWMH strut - if there is one */ 86 | ewmh_get_strut(c); 87 | 88 | /* 89 | * Get the hints, window name, and normal hints (see ICCCM 90 | * section 4.1.2.3). 91 | */ 92 | hints = XGetWMHints(dpy, c->window); 93 | 94 | getWindowName(c); 95 | getNormalHints(c); 96 | 97 | /* 98 | * Get the colourmaps associated with this window. Get the window 99 | * attribute colourmap first, then look to see if the 100 | * WM_COLORMAP_WINDOWS property has been used to specify 101 | * windows needing colourmaps that differ from the top-level 102 | * colourmap. (See ICCCM section 4.1.8.) 103 | */ 104 | XGetWindowAttributes(dpy, c->window, ¤t_attr); 105 | c->cmap = current_attr.colormap; 106 | 107 | getColourmaps(c); 108 | 109 | /* 110 | * Scan the list of atoms on WM_PROTOCOLS to see which of the 111 | * protocols that we understand the client is prepared to 112 | * participate in. (See ICCCM section 4.1.2.7.) 113 | */ 114 | if (XGetWMProtocols(dpy, c->window, &protocols, &n) != 0) { 115 | for (p = 0; p < n; p++) { 116 | if (protocols[p] == wm_delete) { 117 | c->proto |= Pdelete; 118 | } else if (protocols[p] == wm_take_focus) { 119 | c->proto |= Ptakefocus; 120 | } 121 | } 122 | 123 | XFree(protocols); 124 | } 125 | 126 | /* Get the WM_TRANSIENT_FOR property (see ICCCM section 4.1.2.6). */ 127 | getTransientFor(c); 128 | 129 | /* Work out details for the Client structure from the hints. */ 130 | if (hints && (hints->flags & InputHint)) 131 | c->accepts_focus = hints->input; 132 | if (c->proto | Ptakefocus) 133 | /* WM_TAKE_FOCUS overrides normal hints */ 134 | c->accepts_focus = True; 135 | 136 | if (!getWindowState(c->window, &state)) 137 | state = hints ? hints->initial_state : NormalState; 138 | 139 | /* 140 | * Sort out the window's position. 141 | */ 142 | { 143 | Window root_window; 144 | int x, y; 145 | unsigned int w, h; 146 | unsigned int border_width, depth; 147 | 148 | XGetGeometry(dpy, c->window, &root_window, &x, &y, &w, &h, 149 | &border_width, &depth); 150 | 151 | /* 152 | * Do the size first. 153 | * 154 | * "The size specifiers refer to the width and height of the 155 | * client excluding borders" -- ICCCM 4.1.2.3. 156 | */ 157 | c->size.width = w; 158 | c->size.height = h; 159 | if (c->framed == True) { 160 | c->size.width += 2 * border; 161 | c->size.height += 2 * border; 162 | } 163 | 164 | /* 165 | * THIS IS A HACK! 166 | * 167 | * OpenGL programs have a habit of appearing smaller than their 168 | * minimum sizes, which they don't like. 169 | */ 170 | if (c->size.width < c->size.min_width) 171 | c->size.width = c->size.min_width; 172 | if (c->size.height < c->size.min_height) 173 | c->size.height = c->size.min_height; 174 | 175 | /* Do the position next. */ 176 | 177 | /* 178 | * If we have a user-specified position for a top-level window, 179 | * or a program-specified position for a dialogue box, we'll 180 | * take it. We'll also just take it during initialisation, 181 | * since the previous manage probably placed its windows 182 | * sensibly. 183 | */ 184 | if (c->trans != None && c->size.flags & PPosition) { 185 | /* It's a "dialogue box". Trust it. */ 186 | c->size.x = x; 187 | c->size.y = y; 188 | } else if ((c->size.flags & USPosition) || 189 | c->framed == False || mode == wm_initialising ) { 190 | /* Use the specified window position. */ 191 | c->size.x = x; 192 | c->size.y = y; 193 | 194 | /* 195 | * We need to be careful of the right-hand edge and 196 | * bottom. We can use the window gravity (if specified) 197 | * to handle this. (See section 4.1.2.3 of the ICCCM.) 198 | */ 199 | applyGravity(c); 200 | } else { 201 | /* No position was specified: use the auto-placement 202 | * heuristics. */ 203 | 204 | /* firstly, make sure auto_x and auto_y are outside 205 | * strut */ 206 | if (auto_x < c->screen->strut.left) 207 | auto_x = c->screen->strut.left; 208 | if (auto_y < c->screen->strut.top) 209 | auto_y = c->screen->strut.top; 210 | 211 | if ((auto_x + c->size.width) > 212 | (c->screen->display_width - 213 | c->screen->strut.right) && 214 | (c->size.width <= 215 | (c->screen->display_width - 216 | c->screen->strut.left - 217 | c->screen->strut.right))) { 218 | /* 219 | * If the window wouldn't fit using normal 220 | * auto-placement but is small enough to fit 221 | * horizontally, then centre the window 222 | * horizontally. 223 | */ 224 | c->size.x = (c->screen->display_width 225 | - c->size.width) / 2; 226 | auto_x = c->screen->strut.left + 20; 227 | } else { 228 | c->size.x = auto_x; 229 | auto_x += 10; 230 | if (auto_x > (c->screen->display_width / 2)) 231 | auto_x = c->screen->strut.left + 20; 232 | } 233 | 234 | if (((auto_y + c->size.height) > 235 | (c->screen->display_height - 236 | c->screen->strut.bottom)) && 237 | (c->size.height <= 238 | (c->screen->display_height - 239 | c->screen->strut.top - 240 | c->screen->strut.bottom))) { 241 | /* 242 | * If the window wouldn't fit using normal 243 | * auto-placement but is small enough to fit 244 | * vertically, then centre the window 245 | * vertically. 246 | */ 247 | c->size.y = (c->screen->display_height 248 | - c->size.height) / 2; 249 | auto_y = c->screen->strut.top + 20; 250 | } else { 251 | c->size.y = auto_y; 252 | auto_y += 10; 253 | if (auto_y > (c->screen->display_height / 2)) 254 | auto_y = c->screen->strut.top + 20; 255 | } 256 | } 257 | } 258 | 259 | if (hints) 260 | XFree(hints); 261 | 262 | /* 263 | * Do all the reparenting and stuff. 264 | */ 265 | 266 | if (c->framed == True) { 267 | c->parent = XCreateSimpleWindow(dpy, c->screen->root, 268 | c->size.x, c->size.y - titleHeight(), 269 | c->size.width, c->size.height + titleHeight(), 270 | 1, c->screen->black, c->screen->white); 271 | 272 | attr.event_mask = ExposureMask | EnterWindowMask | ButtonMask | 273 | SubstructureRedirectMask | SubstructureNotifyMask | 274 | PointerMotionMask; 275 | XChangeWindowAttributes(dpy, c->parent, CWEventMask, &attr); 276 | 277 | XResizeWindow(dpy, c->window, c->size.width - 2 * border, 278 | c->size.height - 2 * border); 279 | } 280 | 281 | /* 282 | * Stupid X11 doesn't let us change border width in the above 283 | * call. It's a window attribute, but it's somehow second-class. 284 | * 285 | * As pointed out by Adrian Colley, we can't change the window 286 | * border width at all for InputOnly windows. 287 | */ 288 | if (current_attr.class != InputOnly) 289 | XSetWindowBorderWidth(dpy, c->window, 0); 290 | 291 | attr.event_mask = ColormapChangeMask | EnterWindowMask | 292 | PropertyChangeMask | FocusChangeMask; 293 | attr.win_gravity = StaticGravity; 294 | attr.do_not_propagate_mask = ButtonMask; 295 | XChangeWindowAttributes(dpy, c->window, 296 | CWEventMask | CWWinGravity | CWDontPropagate, &attr); 297 | 298 | if (c->framed == True) { 299 | XReparentWindow(dpy, c->window, c->parent, 300 | border, border + titleHeight()); 301 | } else { 302 | XReparentWindow(dpy, c->window, c->parent, 303 | c->size.x, c->size.y); 304 | } 305 | 306 | setShape(c); 307 | 308 | XAddToSaveSet(dpy, c->window); 309 | if (state == IconicState) { 310 | hide(c); 311 | } else { 312 | /* Map the new window in the relevant state. */ 313 | c->hidden = False; 314 | XMapWindow(dpy, c->parent); 315 | XMapWindow(dpy, c->window); 316 | setactive(c, (focus_mode == focus_click) ? 1 : 0, 0L); 317 | Client_SetState(c, NormalState); 318 | } 319 | 320 | if (c->wstate.fullscreen == True) Client_EnterFullScreen(c); 321 | 322 | if (current != c) 323 | cmapfocus(current); 324 | } 325 | 326 | static void 327 | applyGravity(Client *c) { 328 | if (c->framed == False) return; /* only required for framed windows*/ 329 | if (c->size.flags & PWinGravity) { 330 | switch (c->size.win_gravity) { 331 | case NorthEastGravity: 332 | c->size.x -= 2 * border; 333 | break; 334 | case SouthWestGravity: 335 | c->size.y -= 2 * border; 336 | break; 337 | case SouthEastGravity: 338 | c->size.x -= 2 * border; 339 | c->size.y -= 2 * border; 340 | break; 341 | } 342 | } 343 | } 344 | 345 | void 346 | getTransientFor(Client *c) { 347 | Window trans = None; 348 | 349 | XGetTransientForHint(dpy, c->window, &trans); 350 | c->trans = trans; 351 | } 352 | 353 | void 354 | withdraw(Client *c) { 355 | if (c->parent != c->screen->root) { 356 | XUnmapWindow(dpy, c->parent); 357 | XReparentWindow(dpy, c->parent, c->screen->root, c->size.x, c->size.y); 358 | } 359 | 360 | XRemoveFromSaveSet(dpy, c->window); 361 | Client_SetState(c, WithdrawnState); 362 | 363 | /* 364 | * Flush and ignore any errors. X11 sends us an UnmapNotify before it 365 | * sends us a DestroyNotify. That means we can get here without knowing 366 | * whether the relevant window still exists. 367 | */ 368 | ignore_badwindow = 1; 369 | XSync(dpy, False); 370 | ignore_badwindow = 0; 371 | } 372 | 373 | static void 374 | installColourmap(Colormap cmap) { 375 | if (cmap == None) 376 | cmap = DefaultColormap(dpy, DefaultScreen(dpy)); 377 | XInstallColormap(dpy, cmap); 378 | } 379 | 380 | void 381 | cmapfocus(Client * c) { 382 | int i; 383 | int found; 384 | Client *cc; 385 | 386 | if (c == 0) 387 | installColourmap(None); 388 | else if (c->ncmapwins != 0) { 389 | found = 0; 390 | for (i = c->ncmapwins - 1; i >= 0; i--) { 391 | installColourmap(c->wmcmaps[i]); 392 | if (c->cmapwins[i] == c->window) 393 | found++; 394 | } 395 | if (!found) 396 | installColourmap(c->cmap); 397 | } else if (c->trans != None && (cc = Client_Get(c->trans)) != 0 && 398 | cc->ncmapwins != 0) 399 | cmapfocus(cc); 400 | else 401 | installColourmap(c->cmap); 402 | } 403 | 404 | void 405 | getColourmaps(Client *c) { 406 | int n; 407 | int i; 408 | Window *cw; 409 | XWindowAttributes attr; 410 | 411 | if (c == 0) 412 | return; 413 | 414 | n = getProperty(c->window, wm_colormaps, XA_WINDOW, 100L, (unsigned char **) &cw); 415 | if (c->ncmapwins != 0) { 416 | XFree(c->cmapwins); 417 | free(c->wmcmaps); 418 | } 419 | if (n <= 0) { 420 | c->ncmapwins = 0; 421 | return; 422 | } 423 | c->ncmapwins = n; 424 | c->cmapwins = cw; 425 | 426 | c->wmcmaps = (Colormap *) malloc(n * sizeof(Colormap)); 427 | for (i = 0; i < n; i++) { 428 | if (cw[i] == c->window) { 429 | c->wmcmaps[i] = c->cmap; 430 | } else { 431 | XSelectInput(dpy, cw[i], ColormapChangeMask); 432 | XGetWindowAttributes(dpy, cw[i], &attr); 433 | c->wmcmaps[i] = attr.colormap; 434 | } 435 | } 436 | } 437 | 438 | /*ARGSUSED*/ 439 | void 440 | Terminate(int signal) { 441 | /* Set all clients free. */ 442 | Client_FreeAll(); 443 | 444 | /* Give up the input focus and the colourmap. */ 445 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 446 | installColourmap(None); 447 | 448 | XCloseDisplay(dpy); 449 | 450 | session_end(); 451 | 452 | if (signal) { 453 | exit(EXIT_FAILURE); 454 | } else { 455 | exit(EXIT_SUCCESS); 456 | } 457 | } 458 | 459 | 460 | static int 461 | getProperty(Window w, Atom a, Atom type, long len, unsigned char **p) { 462 | Atom real_type; 463 | int format; 464 | unsigned long n; 465 | unsigned long extra; 466 | int status; 467 | 468 | /* 469 | * len is in 32-bit multiples. 470 | */ 471 | status = XGetWindowProperty(dpy, w, a, 0L, len, False, type, &real_type, &format, &n, &extra, p); 472 | if (status != Success || *p == 0) 473 | return -1; 474 | if (n == 0) 475 | XFree(*p); 476 | /* 477 | * could check real_type, format, extra here... 478 | */ 479 | return n; 480 | } 481 | 482 | void 483 | getWindowName(Client *c) { 484 | char * name; 485 | Atom actual_type; 486 | int format; 487 | unsigned long n; 488 | unsigned long extra; 489 | int was_nameless; 490 | 491 | if (c == 0) 492 | return; 493 | 494 | was_nameless = (c->name == 0); 495 | 496 | if (ewmh_get_window_name(c) == False) { 497 | if (XGetWindowProperty(dpy, c->window, _mozilla_url, 0L, 100L, False, AnyPropertyType, &actual_type, &format, &n, &extra, (unsigned char **) &name) == Success && name && *name != '\0' && n != 0) { 498 | Client_Name(c, name, False); 499 | XFree(name); 500 | } 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) { 501 | /* That rather unpleasant condition is necessary because xwsh uses 502 | * COMPOUND_TEXT rather than STRING for its WM_NAME property, 503 | * and anonymous xwsh windows are annoying. 504 | */ 505 | if (actual_type == compound_text && memcmp(name, "\x1b\x28\x42", 3) == 0) { 506 | Client_Name(c, name + 3, False); 507 | } else { 508 | Client_Name(c, name, False); 509 | } 510 | XFree(name); 511 | } 512 | } 513 | 514 | if (!was_nameless) 515 | Client_DrawBorder(c, c == current); 516 | } 517 | 518 | void 519 | getNormalHints(Client *c) { 520 | int x, y, w, h; 521 | long msize; 522 | 523 | /* We have to be a little careful here. The ICCCM says that the x, y 524 | * and width, height components aren't used. So we use them. That means 525 | * that we need to save and restore them whenever we fill the size 526 | * struct. */ 527 | x = c->size.x; 528 | y = c->size.y; 529 | w = c->size.width; 530 | h = c->size.height; 531 | 532 | /* Do the get. */ 533 | if (XGetWMNormalHints(dpy, c->window, &c->size, &msize) == 0) 534 | c->size.flags = 0; 535 | 536 | if (c->framed == True) { 537 | /* 538 | * Correct the minimum allowable size of this client to take 539 | * account of the window border. 540 | */ 541 | if (c->size.flags & PMinSize) { 542 | c->size.min_width += 2 * border; 543 | c->size.min_height += 2 * border; 544 | } else { 545 | c->size.flags |= PMinSize; 546 | c->size.min_width = 2 * (2 * border); 547 | if (c->accepts_focus) 548 | c->size.min_height = 2 * (2*border); 549 | else 550 | c->size.min_height = 2 * (2*border); 551 | } 552 | 553 | /* 554 | * Correct the maximum allowable size of this client to take 555 | * account of the window border. 556 | */ 557 | if (c->size.flags & PMaxSize) { 558 | c->size.max_width += 2 * border; 559 | c->size.max_height += 2 * border; 560 | } 561 | } 562 | 563 | /* 564 | * Ensure that the base width & height and the width & height increments 565 | * are set correctly so that we don't have to do this in MakeSane. 566 | */ 567 | if (!(c->size.flags & PBaseSize)) 568 | c->size.base_width = c->size.base_height = 0; 569 | 570 | if (!(c->size.flags & PResizeInc)) 571 | c->size.width_inc = c->size.height_inc = 1; 572 | 573 | /* 574 | * If the client gives identical minimum and maximum sizes, we don't 575 | * want the user to resize in that direction. 576 | */ 577 | if (c->size.min_width == c->size.max_width) 578 | c->size.width_inc = 0; 579 | 580 | if (c->size.min_height == c->size.max_height) 581 | c->size.height_inc = 0; 582 | 583 | /* Restore the window-manager bits. */ 584 | c->size.x = x; 585 | c->size.y = y; 586 | c->size.width = w; 587 | c->size.height = h; 588 | } 589 | 590 | static int 591 | getWindowState(Window w, int *state) { 592 | long *p = 0; 593 | 594 | if (getProperty(w, wm_state, wm_state, 2L, (unsigned char **) &p) <= 0) 595 | return 0; 596 | 597 | *state = (int) *p; 598 | XFree(p); 599 | return 1; 600 | } 601 | 602 | 603 | extern Bool 604 | motifWouldDecorate(Client *c) { 605 | unsigned long *p = 0; 606 | Bool ret = True; /* if all else fails - decorate */ 607 | 608 | if (getProperty(c->window, motif_wm_hints, motif_wm_hints, 609 | 5L, (unsigned char **) &p) <= 0) 610 | return ret; 611 | 612 | if ((p[0] & MWM_HINTS_DECORATIONS) && 613 | !(p[2] & (MWM_DECOR_BORDER | MWM_DECOR_ALL))) 614 | ret = False; 615 | 616 | XFree(p); 617 | return ret; 618 | } 619 | -------------------------------------------------------------------------------- /mouse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "lwm.h" 29 | #include "ewmh.h" 30 | 31 | static int current_item; /* Last known selected menu item. -1 if none. */ 32 | 33 | typedef struct menuitem menuitem; 34 | struct menuitem { 35 | Client * client; 36 | menuitem * next; 37 | }; 38 | 39 | static menuitem * hidden_menu = 0; 40 | 41 | static void getMenuDimensions(int *, int *, int *); 42 | 43 | void 44 | getMousePosition(int * x, int * y) { 45 | Window root, child; 46 | int t1, t2; 47 | unsigned int b; 48 | 49 | /* It doesn't matter which root window we give this call. */ 50 | XQueryPointer(dpy, screens[0].root, &root, &child, x, y, &t1, &t2, &b); 51 | current_screen = getScreenFromRoot(root); 52 | } 53 | 54 | int 55 | menu_whichitem(int x, int y) { 56 | int width; /* Width of menu. */ 57 | int height; /* Height of each menu item. */ 58 | int length; /* Number of items on the menu. */ 59 | 60 | getMenuDimensions(&width, &height, &length); 61 | 62 | /* 63 | * Translate to popup window coordinates. We do this ourselves to avoid 64 | * a round trip to the server. 65 | */ 66 | x -= start_x; 67 | y -= start_y; 68 | 69 | /* 70 | * Are we outside the menu? 71 | */ 72 | if (x < 0 || x > width || y < 0 || y >= length * height) 73 | return -1; 74 | 75 | return y / height; 76 | } 77 | 78 | static void 79 | getMenuDimensions(int *width, int *height, int *length) { 80 | int w; /* Widest string so far. */ 81 | int i; /* Menu item. */ 82 | 83 | menuitem *m = hidden_menu; 84 | 85 | w = 0; 86 | for (i = 0; m != 0; m = m->next, i++) { 87 | int tw = titleWidth(popup_font_set, m->client) + 4; 88 | if (tw > w) w = tw; 89 | } 90 | 91 | *width = w + border; 92 | *height = popupHeight(); 93 | *length = i; 94 | } 95 | 96 | void 97 | menuhit(XButtonEvent *e) { 98 | int width; /* Width of menu. */ 99 | int height; /* Height of each menu item. */ 100 | int length; /* Number of menu items. */ 101 | 102 | if (hidden_menu == 0) 103 | return; 104 | 105 | Client_ResetAllCursors(); 106 | 107 | current_screen = getScreenFromRoot(e->root); 108 | 109 | getMenuDimensions(&width, &height, &length); 110 | 111 | /* 112 | * Arrange for centre of first menu item to be under pointer, 113 | * unless that would put the menu offscreen. 114 | */ 115 | start_x = e->x - width / 2; 116 | start_y = e->y - height / 2; 117 | 118 | if (start_x + width > current_screen->display_width) 119 | start_x = current_screen->display_width - width; 120 | if (start_x < 0) 121 | start_x = 0; 122 | if (start_y + (height * length) > current_screen->display_height) 123 | start_y = current_screen->display_height - (height * length); 124 | if (start_y < 0) 125 | start_y = 0; 126 | 127 | 128 | current_item = menu_whichitem(e->x_root, e->y_root); 129 | 130 | XMoveResizeWindow(dpy, current_screen->popup, start_x, start_y, 131 | width, length * height); 132 | XMapRaised(dpy, current_screen->popup); 133 | XChangeActivePointerGrab(dpy, ButtonMask | ButtonMotionMask | 134 | OwnerGrabButtonMask, None, CurrentTime); 135 | 136 | mode = wm_menu_up; 137 | } 138 | 139 | void 140 | hide(Client *c) { 141 | menuitem *newitem; 142 | 143 | if (c == 0) 144 | return; 145 | 146 | /* Create new menu item, and thread it on the menu. */ 147 | newitem = (menuitem *) malloc(sizeof(menuitem)); 148 | if (newitem == 0) 149 | return; 150 | newitem->client = c; 151 | newitem->next = hidden_menu; 152 | hidden_menu = newitem; 153 | 154 | /* Actually hide the window. */ 155 | XUnmapWindow(dpy, c->parent); 156 | XUnmapWindow(dpy, c->window); 157 | 158 | c->hidden = True; 159 | 160 | /* If the window was the current window, it isn't any more... */ 161 | if (c == current) Client_Focus(NULL, CurrentTime); 162 | Client_SetState(c, IconicState); 163 | } 164 | 165 | void 166 | unhide(int n, int map) { 167 | Client *c; 168 | menuitem *prev = 0; 169 | menuitem *m = hidden_menu; 170 | 171 | /* Find the nth client. */ 172 | if (n < 0) 173 | return; 174 | 175 | while (n-- > 0 && m != 0) { 176 | prev = m; 177 | m = m->next; 178 | } 179 | 180 | if (m == 0) 181 | return; 182 | 183 | c = m->client; 184 | 185 | /* Remove the item from the menu, and dispose of it. */ 186 | if (prev == 0) { 187 | hidden_menu = m->next; 188 | } else { 189 | prev->next = m->next; 190 | } 191 | free(m); 192 | 193 | c->hidden = False; 194 | 195 | /* Unhide it. */ 196 | if (map) { 197 | XMapWindow(dpy, c->parent); 198 | XMapWindow(dpy, c->window); 199 | Client_Raise(c); 200 | Client_SetState(c, NormalState); 201 | 202 | if (focus_mode == focus_click) { 203 | /* it feels right that the unhidden window gets focus*/ 204 | Client_Focus(c, CurrentTime); 205 | } 206 | } 207 | } 208 | 209 | void 210 | unhidec(Client *c, int map) { 211 | int i = 0; 212 | menuitem *m = hidden_menu; 213 | 214 | if (c == 0) 215 | return; 216 | 217 | /* My goodness, how the world sucks. */ 218 | while (m != 0) { 219 | if (m->client == c) { 220 | unhide(i, map); 221 | return; 222 | } 223 | m = m->next; 224 | i++; 225 | } 226 | } 227 | 228 | void 229 | menu_expose(void) { 230 | int i; /* Menu item being drawn. */ 231 | int width; /* Width of each item. */ 232 | int height; /* Height of each item. */ 233 | int length; /* Number of menu items. */ 234 | menuitem *m; 235 | 236 | getMenuDimensions(&width, &height, &length); 237 | 238 | /* Redraw the labels. */ 239 | for (m = hidden_menu, i = 0; m != 0; m = m->next, i++) { 240 | int tx = (width - titleWidth(popup_font_set, m->client)) / 2; 241 | int ty = i * height + ascent(popup_font_set_ext); 242 | char *name; 243 | int namelen; 244 | 245 | if (m->client->menu_name == NULL) { 246 | name = m->client->name; 247 | namelen = m->client->namelen; 248 | } else { 249 | name = m->client->menu_name; 250 | namelen = m->client->menu_namelen; 251 | } 252 | 253 | #ifdef X_HAVE_UTF8_STRING 254 | if (m->client->name_utf8 == True) 255 | Xutf8DrawString(dpy, m->client->screen->popup, 256 | popup_font_set, 257 | current_screen->menu_gc, tx, ty, 258 | name, namelen); 259 | else 260 | #endif 261 | XmbDrawString(dpy, m->client->screen->popup, 262 | popup_font_set, 263 | current_screen->menu_gc, tx, ty, 264 | name, namelen); 265 | } 266 | 267 | /* Highlight current item if there is one. */ 268 | if (current_item >= 0 && current_item < length) 269 | XFillRectangle(dpy, current_screen->popup, current_screen->menu_gc, 270 | 0, current_item * height, width, height); 271 | } 272 | 273 | void 274 | menu_motionnotify(XEvent* ev) { 275 | int old; /* Old menu position. */ 276 | int width; /* Width of menu. */ 277 | int height; /* Height of each menu item. */ 278 | int length; /* Number of menu items. */ 279 | XButtonEvent *e = &ev->xbutton; 280 | 281 | getMenuDimensions(&width, &height, &length); 282 | 283 | old = current_item; 284 | current_item = menu_whichitem(e->x_root, e->y_root); 285 | 286 | if (current_item == old) return; 287 | 288 | /* Unhighlight the old position, if it was on the menu. */ 289 | if (old >= 0 && old < length) 290 | XFillRectangle(dpy, current_screen->popup, current_screen->menu_gc, 291 | 0, old * height, width, height); 292 | 293 | /* Highlight the new position, if it's on the menu. */ 294 | if (current_item >= 0 && current_item < length) 295 | XFillRectangle(dpy, current_screen->popup, current_screen->menu_gc, 296 | 0, current_item * height, width, height); 297 | } 298 | 299 | void 300 | menu_buttonrelease(XEvent *ev) { 301 | int n; /* Menu item. */ 302 | 303 | /* 304 | * Work out which menu item the button was released over. 305 | */ 306 | n = menu_whichitem(ev->xbutton.x_root, ev->xbutton.y_root); 307 | 308 | /* Hide the menu until it's needed again. */ 309 | XUnmapWindow(dpy, current_screen->popup);/*BUG?*/ 310 | 311 | /* Do the menu thing (of unhiding windows). */ 312 | unhide(n, 1); 313 | 314 | if (current) { 315 | cmapfocus(current); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /no_xmkmf_makefile: -------------------------------------------------------------------------------- 1 | # Sample Makefile for lwm. 2 | 3 | # You ought to be using the Imakefile (xmkmf;make) but 4 | # if Imake isn't set up properly on your system, this might 5 | # help you out. I used to use it on an SGI. 6 | 7 | # Uncomment these lines to use gcc. 8 | #CC = gcc 9 | #CFLAGS = -ansi -pedantic -Wall -DSHAPE 10 | 11 | # Uncomment these lines to use SGI cc. 12 | #CC = cc 13 | #CFLAGS = -fullwarn -g -DSHAPE 14 | 15 | # Uncomment these for Solaris Sun Studio, choose your architecture. 16 | #CC = cc 17 | #CFLAGS = -Xa -fast -xarch=v8a 18 | #CFLAGS = -Xa -fast -xarch=386 19 | 20 | DEFINES = 21 | 22 | # Bennett Todd (bet@lehman.com) says this helped him compile on 23 | # Solaris 2.5.1, avoiding a problem with . 24 | #DEFINES = -D_POSIX_C_SOURCE=2 25 | 26 | # Add any strange libraries your system needs here. 27 | LDFLAGS = -lXext -lX11 -lICE -lSM 28 | 29 | # ----------------------------------------------------------------------------- 30 | 31 | OFILES = client.o cursor.o disp.o error.o ewmh.o lwm.o manage.o mouse.o \ 32 | resource.o session.o shape.o 33 | HFILES = lwm.h ewmh.h 34 | 35 | # ----------------------------------------------------------------------------- 36 | 37 | all: lwm 38 | 39 | lwm: $(OFILES) 40 | $(CC) $(CFLAGS) $(DEFINES) -o lwm $(OFILES) $(LDFLAGS) 41 | 42 | install: lwm 43 | cp lwm /usr/local/bin 44 | 45 | $(OFILES): $(HFILES) 46 | 47 | clean: 48 | rm -f lwm *.o core 49 | -------------------------------------------------------------------------------- /resource.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "lwm.h" 35 | 36 | char *font_name; /* User's choice of titlebar font. */ 37 | char *popup_font_name; /* User's choice of menu font. */ 38 | char *btn1_command; /* User's choice of button 1 command. */ 39 | char *btn2_command; /* User's choice of button 2 command. */ 40 | int border; /* User's choice of border size. */ 41 | FocusMode focus_mode; /* User's choice of focus mode (default enter) */ 42 | 43 | char * 44 | sdup(char *p) { 45 | char *s ; 46 | 47 | s = malloc(strlen(p) + 1); 48 | if(s == 0) 49 | panic("malloc failed."); 50 | return strcpy(s, p); 51 | } 52 | 53 | extern void 54 | parseResources(void) { 55 | XrmDatabase db; 56 | XrmValue value; 57 | char *resource_manager; 58 | char *type; 59 | 60 | /* Set our fall-back defaults. */ 61 | font_name = DEFAULT_TITLE_FONT; 62 | popup_font_name = DEFAULT_POPUP_FONT; 63 | border = DEFAULT_BORDER; 64 | btn1_command = 0; 65 | btn2_command = DEFAULT_TERMINAL; 66 | focus_mode = focus_enter; 67 | 68 | resource_manager = XResourceManagerString(dpy); 69 | if (resource_manager == 0) 70 | return; 71 | 72 | XrmInitialize(); 73 | db = XrmGetStringDatabase(resource_manager); 74 | if (db == 0) 75 | return; 76 | 77 | /* Fonts. */ 78 | if (XrmGetResource(db, "lwm.titleFont", "Font", &type, &value) == True) 79 | if (strcmp(type, "String") == 0) 80 | font_name = sdup((char *) value.addr); 81 | if (XrmGetResource(db, "lwm.popupFont", "Font", &type, &value) == True) 82 | if (strcmp(type, "String") == 0) 83 | popup_font_name = sdup((char *) value.addr); 84 | 85 | /* Window border width. */ 86 | if(XrmGetResource(db, "lwm.border", "Border", &type, &value) == True) 87 | if (strcmp(type, "String") == 0) 88 | border = (int) strtol((char *) value.addr, (char **) 0, 0); 89 | 90 | /* The button commands. */ 91 | if (XrmGetResource(db, "lwm.button1", "Command", &type, &value) == True) 92 | if (strcmp(type, "String") == 0) 93 | btn1_command = sdup((char *) value.addr); 94 | if (XrmGetResource(db, "lwm.button2", "Command", &type, &value) == True) 95 | if (strcmp(type, "String") == 0) 96 | btn2_command = sdup((char *) value.addr); 97 | 98 | /* The focus mode */ 99 | if (XrmGetResource(db, "lwm.focus", "FocusMode", &type, &value) 100 | == True && strcmp(type, "String") == 0) { 101 | if (strcmp(value.addr, "enter") == 0) focus_mode = focus_enter; 102 | if (strcmp(value.addr, "click") == 0) focus_mode = focus_click; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /session.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "lwm.h" 33 | 34 | typedef struct { 35 | SmProp p; 36 | SmPropValue v; 37 | } SmProperty; 38 | 39 | int ice_fd=-1; 40 | static IceConn ice_conn; 41 | static void *smc_conn = NULL; 42 | static int session_argc; 43 | static char **session_argv; 44 | static char *client_id = NULL; 45 | 46 | static void 47 | ice_error(IceConn c) { 48 | /* i only bother catching ice i/o errors because metacity claims the 49 | * default handler calls exit. twm doesn't bother, so it might not 50 | * be necessary. 51 | */ 52 | fprintf(stderr,"%s: ICE I/O error\n",argv0); 53 | } 54 | 55 | static void 56 | session_save_yourself(SmcConn smc_conn, 57 | SmPointer client_data, 58 | int save_type, 59 | Bool shutdown, 60 | int interact_style, 61 | Bool fast) { 62 | int i; 63 | SmProperty program; 64 | SmProperty user_id; 65 | SmProperty restart_style_hint; 66 | SmProperty pid; 67 | SmProperty gsm_priority; 68 | SmProp clone_command; 69 | SmProp restart_command; 70 | enum prop_nums { 71 | prop_program, 72 | prop_user_id, 73 | prop_restart_style_hint, 74 | prop_pid, 75 | prop_gsm_priority, 76 | prop_clone_command, 77 | prop_restart_command, 78 | prop_LAST 79 | }; 80 | SmProp *props[prop_LAST]; 81 | struct passwd *pw; 82 | char hint = SmRestartImmediately; 83 | char pid_s[32]; 84 | char priority = 20; 85 | 86 | program.p.name = SmProgram; 87 | program.p.type = SmARRAY8; 88 | program.p.num_vals = 1; 89 | program.p.vals = &program.v; 90 | program.v.value = "lwm"; 91 | program.v.length = 3; 92 | props[prop_program] = &program.p; 93 | 94 | 95 | pw = getpwuid(getuid()); 96 | user_id.p.name = SmUserID; 97 | user_id.p.type = SmARRAY8; 98 | user_id.p.num_vals = 1; 99 | user_id.p.vals = &user_id.v; 100 | user_id.v.value = pw ? pw->pw_name : NULL ; 101 | user_id.v.length = pw ? strlen(pw->pw_name) : 0; 102 | props[prop_user_id] = &user_id.p; 103 | 104 | restart_style_hint.p.name = SmRestartStyleHint; 105 | restart_style_hint.p.type = SmCARD8; 106 | restart_style_hint.p.num_vals = 1; 107 | restart_style_hint.p.vals = &restart_style_hint.v; 108 | restart_style_hint.v.value = &hint; 109 | restart_style_hint.v.length = 1; 110 | props[prop_restart_style_hint] = &restart_style_hint.p; 111 | 112 | snprintf(pid_s, sizeof(pid_s), "%d", getpid()); 113 | pid.p.name = SmProcessID; 114 | pid.p.type = SmARRAY8; 115 | pid.p.num_vals = 1; 116 | pid.p.vals = &pid.v; 117 | pid.v.value = pid_s; 118 | pid.v.length = strlen(pid_s); 119 | props[prop_pid] = &pid.p; 120 | 121 | gsm_priority.p.name = "_GSM_Priority"; 122 | gsm_priority.p.type = SmCARD8; 123 | gsm_priority.p.num_vals = 1; 124 | gsm_priority.p.vals = &gsm_priority.v; 125 | gsm_priority.v.value = &priority; 126 | gsm_priority.v.length = 1; 127 | props[prop_gsm_priority] = &gsm_priority.p; 128 | 129 | clone_command.name = SmCloneCommand; 130 | clone_command.type = SmLISTofARRAY8; 131 | clone_command.num_vals = session_argc; 132 | clone_command.vals = 133 | (SmPropValue *) malloc(sizeof(SmPropValue) * session_argc); 134 | for (i=0; i < session_argc; i++) { 135 | clone_command.vals[i].value = session_argv[i]; 136 | clone_command.vals[i].length = strlen(session_argv[i]); 137 | } 138 | props[prop_clone_command] = &clone_command; 139 | 140 | restart_command.name = SmRestartCommand; 141 | restart_command.type = SmLISTofARRAY8; 142 | restart_command.num_vals = session_argc + 2; 143 | restart_command.vals = 144 | (SmPropValue *) malloc(sizeof(SmPropValue) * 145 | (session_argc + 2)); 146 | for (i=0; i < session_argc; i++) { 147 | restart_command.vals[i].value = session_argv[i]; 148 | restart_command.vals[i].length = strlen(session_argv[i]); 149 | } 150 | restart_command.vals[i].value = "-s"; 151 | restart_command.vals[i].length = 2; 152 | i++; 153 | restart_command.vals[i].value = client_id; 154 | restart_command.vals[i].length = strlen(client_id); 155 | props[prop_restart_command] = &restart_command; 156 | 157 | SmcSetProperties(smc_conn, prop_LAST, props); 158 | 159 | free(clone_command.vals); 160 | free(restart_command.vals); 161 | 162 | SmcSaveYourselfDone(smc_conn, True); 163 | } 164 | 165 | void 166 | session_end(void) { 167 | if (smc_conn == NULL) return; 168 | SmcCloseConnection(smc_conn,0,NULL); 169 | } 170 | 171 | static void 172 | session_die(SmcConn smc_conn, SmPointer client_data) { 173 | Terminate(0); 174 | } 175 | 176 | static void 177 | session_save_complete(SmcConn smc_conn, SmPointer client_data) { 178 | } 179 | 180 | static void 181 | session_shutdown_cancelled(SmcConn smc_conn, SmPointer client_data) { 182 | } 183 | 184 | void 185 | session_init(int argc, char *argv[]) { 186 | int i; 187 | char *previd = NULL; 188 | char err[256]; 189 | unsigned long mask; 190 | SmcCallbacks callbacks; 191 | 192 | session_argv = (char **) malloc(sizeof(char *) * (argc + 2)); 193 | session_argc = 0; 194 | for (i = 0; i < argc; i++) { 195 | if (i != 0 && strcmp(argv[i], "-s") == 0) { 196 | if ((i + 1) < argc) { 197 | i++; 198 | previd = argv[i]; 199 | } 200 | } else { 201 | session_argv[session_argc] = argv[i]; 202 | session_argc++; 203 | } 204 | } 205 | 206 | mask = SmcSaveYourselfProcMask | 207 | SmcDieProcMask | 208 | SmcSaveCompleteProcMask | 209 | SmcShutdownCancelledProcMask; 210 | 211 | callbacks.save_yourself.callback=session_save_yourself; 212 | callbacks.save_yourself.client_data=NULL; 213 | callbacks.die.callback = session_die; 214 | callbacks.die.client_data = NULL; 215 | callbacks.save_complete.callback = session_save_complete; 216 | callbacks.save_complete.client_data = NULL; 217 | callbacks.shutdown_cancelled.callback = session_shutdown_cancelled; 218 | callbacks.shutdown_cancelled.client_data = NULL; 219 | 220 | smc_conn = SmcOpenConnection(NULL, NULL, 221 | SmProtoMajor, SmProtoMinor, 222 | mask, &callbacks, 223 | previd, &client_id, 224 | sizeof(err) - 1, err); 225 | 226 | if (smc_conn == NULL) { 227 | /* 228 | this isn't actually an error, and can cause confusion 229 | fprintf(stderr, 230 | "%s: could not connect to session manager - %s\n", 231 | argv0, err); 232 | */ 233 | return; 234 | } 235 | if (client_id == NULL) { 236 | fprintf(stderr, 237 | "%s: session manager returned NULL connection\n", 238 | argv0); 239 | return; 240 | } 241 | 242 | 243 | IceSetIOErrorHandler(ice_error); 244 | ice_conn = SmcGetIceConnection(smc_conn); 245 | ice_fd = IceConnectionNumber(ice_conn); 246 | } 247 | 248 | void 249 | session_process(void) { 250 | IceProcessMessages(ice_conn, NULL, NULL); 251 | } 252 | -------------------------------------------------------------------------------- /shape.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lwm, a window manager for X11 3 | * Copyright (C) 1997-2016 Elliott Hughes, James Carter 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #ifdef SHAPE 31 | #include 32 | #endif 33 | 34 | #include "lwm.h" 35 | 36 | /*ARGSUSED*/ 37 | extern void 38 | setShape(Client *c) { 39 | #ifdef SHAPE 40 | int n; 41 | int order; 42 | XRectangle *rect; 43 | 44 | if (shape) { 45 | XShapeSelectInput(dpy, c->window, ShapeNotifyMask); 46 | rect = XShapeGetRectangles(dpy, c->window, ShapeBounding, 47 | &n, &order); 48 | if (n > 1) 49 | XShapeCombineShape(dpy, c->parent, ShapeBounding, 50 | border - 1, border - 1, c->window, 51 | ShapeBounding, ShapeSet); 52 | XFree(rect); 53 | } 54 | #else 55 | #endif 56 | } 57 | 58 | /*ARGSUSED*/ 59 | extern int 60 | shapeEvent(XEvent *ev) { 61 | #ifdef SHAPE 62 | if (shape && ev->type == shape_event) { 63 | Client *c; 64 | XShapeEvent *e = (XShapeEvent *)ev; 65 | 66 | c = Client_Get(e->window); 67 | if (c != 0) 68 | setShape(c); 69 | return 1; 70 | } 71 | #else 72 | #endif 73 | return 0; 74 | } 75 | 76 | /*ARGSUSED*/ 77 | extern int 78 | isShaped(Window w) { 79 | #ifdef SHAPE 80 | int n; 81 | int order; 82 | XRectangle *rect; 83 | 84 | rect = XShapeGetRectangles(dpy, w, ShapeBounding, &n, &order); 85 | XFree(rect); 86 | 87 | return (n > 1); 88 | #else 89 | return 0; 90 | #endif 91 | } 92 | 93 | extern int 94 | serverSupportsShapes(void) { 95 | #ifdef SHAPE 96 | int shape_error; 97 | return XShapeQueryExtension(dpy, &shape_event, &shape_error); 98 | #else 99 | return 0; 100 | #endif 101 | } 102 | --------------------------------------------------------------------------------